|
Hi,
in my DeviceAdapter I have communication code that should be usable by multiple threads at the same time. Basically, it just sends data to the stage controller and waits until the response is received. With multiple threads, I need to ensure that after each "send" always a "receive" is done. I implemented my communication function in the following way: ------------------------------------------------------------------- // the following should be atomic MMThreadGuard myLock(g_communication_lock); send() receive() ------------------------------------------------------------------- In the constructor of MMThreadGuard the mutex is locked, whereas in the destructor it is unlocked again. So if another thread calls the above function, it is suspended until the first thread is done communicating. However, I discovered that MMThreadGuard does not lock at all ! I tracked it down to MM_THREAD_INITIALIZE_GUARD in MMDevice/DeviceThreads.h and discovered, that for Linux the locks are configured as recursive locks. While usually after the first call to pthread_mutex_lock, a subsequent call to pthread_mutex_lock() would block until the already locked mutex is unlocked, with recursive mutexes is does not block at all. Instead it returns a number indicating how many times a mutex has been locked. Why are the locks in micromanager recursive ? Is it due to the windows thread implementation ? How is one supposed to use the locking classes MMThreadLock and MMThreadGuard if they do not block ? My first idea would have been to wrap a timeout around Lock() by waiting until the mutex is either locked only once or a timeout occurred. But then I saw that Lock() does not have a return value. Regards, Stefan ------------------------------------------------------------------------------ Get your Android app more play: Bring it to the BlackBerry PlayBook in minutes. BlackBerry App World™ now supports Android™ Apps for the BlackBerry® PlayBook™. Discover just how easy and simple it is! http://p.sf.net/sfu/android-dev2dev _______________________________________________ micro-manager-general mailing list [hidden email] https://lists.sourceforge.net/lists/listinfo/micro-manager-general |
|
Hi,
On 10/30/2011 12:18 PM, Stefan Schoenleitner wrote: > Why are the locks in micromanager recursive ? Is it due to the windows > thread implementation ? -------------------------------------------------------------------------------------------------- 5023 karlh // #define MM_THREAD_INITIALIZE_GUARD(plock) pthread_mutex_init(plock, NULL) 5023 karlh 5023 karlh #ifdef linux 5023 karlh #define _MUTEX_RECURSIVE PTHREAD_MUTEX_RECURSIVE_NP 5023 karlh #else 5023 karlh /* OS X, ... */ 5023 karlh #define _MUTEX_RECURSIVE PTHREAD_MUTEX_RECURSIVE 5023 karlh #endif 5023 karlh #define MM_THREAD_INITIALIZE_GUARD(plock) \ 5023 karlh { pthread_mutexattr_t a; pthread_mutexattr_init( &a ); \ 5023 karlh pthread_mutexattr_settype( &a, _MUTEX_RECURSIVE ); \ 5023 karlh pthread_mutex_init(plock,&a); pthread_mutexattr_destroy( &a ); \ 5023 karlh } -------------------------------------------------------------------------------------------------- -------------------------------------------------------------------------------------------------- r5023 | karlh | 2010-08-09 21:00:19 +0200 (Mon, 09 Aug 2010) | 1 line merge from 1.3 -- on Unix, always init the mutex with RECURSIVE attribute so that behavior is as on Windows -------------------------------------------------------------------------------------------------- I think this is wrong. On Windows, EnterCriticalSection() always blocks if a mutex is already locked with one exception: If the thread that locked a mutex tries to lock the same mutex again, it will not block. "This prevents a thread from deadlocking itself while waiting for a critical section that it already owns."[1] However, on Linux "If the mutex is of the ``recursive'' kind, the call to pthread_mutex_lock(3) returns immediately with a success return code. The number of times the thread owning the mutex has locked it is recorded in the mutex. The owning thread must call pthread_mutex_unlock(3) the same number of times before the mutex returns to the unlocked state." [pthread_mutexattr_init manpage] So if one really wants to have the Windows behavior (which does not block if the owner of the mutex tries to lock it again), setting a pthread mutex to recursive will not do the job. The only solution I see at the moment it is to remember which thread successfully locked a mutex (i.e. in a global variable). It isn't pretty, but I guess it would work. Still, to me it seems that it is a programming error if a thread that already successfully locked a mutex tries to lock it again. So why not just revert back to: #define MM_THREAD_INITIALIZE_GUARD(plock) pthread_mutex_init(plock, NULL) ? Regards, Stefan [1] http://msdn.microsoft.com/en-us/library/windows/desktop/ms682608%28v=vs.85%29.aspx ------------------------------------------------------------------------------ Get your Android app more play: Bring it to the BlackBerry PlayBook in minutes. BlackBerry App World™ now supports Android™ Apps for the BlackBerry® PlayBook™. Discover just how easy and simple it is! http://p.sf.net/sfu/android-dev2dev _______________________________________________ micro-manager-general mailing list [hidden email] https://lists.sourceforge.net/lists/listinfo/micro-manager-general |
|
In reply to this post by Stefan Schoenleitner
Hi Stefan,
Thanks for bringing this issue up! It is quite fundamental.
How did you discover that? Are you sure that the second thread entering this code block (while the first one is still in there) did not block?
Not true. With recursive mutexes, a second call to pthread_mutex_lock() will block, but only if it is coming from another thread than the first.
Exactly.
They do block, but only to other thread entering the same critical section. (Continuing with your next mail to keep things in context): -------------------------------------------------------------------------------------------------- 5023 karlh #ifdef linux I think this is wrong. The statement above must refer to the same thread entering this section. Quoting from http://sourceware.org/pthreads-win32/manual/pthread_mutexattr_init.html : "The mutex type determines what happens if a thread attempts to lock a mutex it already owns with pthread_mutex_lock(3) ." Of course, a mutex of the "recursive" kind must block to another thread trying to enter the same section (what else would it be doing?). Thus, it does lead to the same behavior as on Windows. Still, to me it seems that it is a programming error if a thread that already successfully locked a mutex tries to lock it again. It may not be best practice, but since different platforms have facilities for this behavior, I would hardly call it a programming error. In fact, this change to the unix code was introduced since we had a deadlock somewhere in code that mad use of the Micro-Manaer threadguards. Karl decided on this approach, I do not remember much of the details other than that it successfully dealt with the deadlock. So why not just revert back to: That will most certainly lead to deadlocks in various places and to different behavior on the different platforms. If you want a non-recursive lock, why not add one in DeviceThreads.h? I would certainly encourage everyone to use non-recursive threads and it would be great to add that behavior so that device adapter writers can use that approach. Best, Nico ------------------------------------------------------------------------------ Get your Android app more play: Bring it to the BlackBerry PlayBook in minutes. BlackBerry App World™ now supports Android™ Apps for the BlackBerry® PlayBook™. Discover just how easy and simple it is! http://p.sf.net/sfu/android-dev2dev _______________________________________________ micro-manager-general mailing list [hidden email] https://lists.sourceforge.net/lists/listinfo/micro-manager-general |
|
Hi Nico,
On 10/30/2011 07:21 PM, Nico Stuurman wrote: >> However, I discovered that MMThreadGuard does not lock at all ! > > How did you discover that? Are you sure that the second thread entering this code block (while the first one is still in there) did not block? I tested this from the *same* thread. Hence, with regard to the windows thread behavior, it did not block. Since was expecting it to block, I though there was an error. (Especially after reading the man page saying that "pthread_mutex_lock(3) returns immediately".) However, I just wrote a small example program and was able to verify that for different threads, the mutex locking works as expected. >> However, on Linux >> "If the mutex is of the ``recursive'' kind, the call to >> pthread_mutex_lock(3) returns immediately with a success return code. >> The number of times the thread owning the mutex has locked it is >> recorded in the mutex. The owning thread must call >> pthread_mutex_unlock(3) the same number of times before the mutex >> returns to the unlocked state." [pthread_mutexattr_init manpage] > > The statement above must refer to the same thread entering this section. Quoting from http://sourceware.org/pthreads-win32/manual/pthread_mutexattr_init.html : > "The mutex type determines what happens if a thread attempts to lock a mutex it already owns with pthread_mutex_lock(3) ." Of course, a mutex of the "recursive" kind must block to another thread trying to enter the same section (what else would it be doing?). Thus, it does lead to the same behavior as on Windows. Indeed. The man page misses the part regarding the blocking to other threads. > If you want a non-recursive lock, why not add one in DeviceThreads.h? I would certainly encourage everyone to use non-recursive threads and it would be great to add that behavior so that device adapter writers can use that approach. I changed DeviceThreads.h like this: ---------------------------------------- #ifdef MM_THREAD_NONRECURSIVE_LOCK #define MM_THREAD_INITIALIZE_GUARD(plock) pthread_mutex_init(plock, NULL) #else #define MM_THREAD_INITIALIZE_GUARD(plock) \ { pthread_mutexattr_t a; pthread_mutexattr_init( &a ); \ pthread_mutexattr_settype( &a, _MUTEX_RECURSIVE ); \ pthread_mutex_init(plock,&a); pthread_mutexattr_destroy( &a ); \ } #endif ---------------------------------------- So in the code, one can: ---------------------------------------- #define MM_THREAD_NONRECURSIVE_LOCK #include "DeviceThreads.h" ---------------------------------------- However, this approach has a number of limitations. Also I'm not sure how to implement this with Windows mutexes. cheers, Stefan ------------------------------------------------------------------------------ Get your Android app more play: Bring it to the BlackBerry PlayBook in minutes. BlackBerry App World™ now supports Android™ Apps for the BlackBerry® PlayBook™. Discover just how easy and simple it is! http://p.sf.net/sfu/android-dev2dev _______________________________________________ micro-manager-general mailing list [hidden email] https://lists.sourceforge.net/lists/listinfo/micro-manager-general |
|
Hi Seamus,
Sorry for the delay in answering. Since your code is waiting for an answer from the device, there is no chance that the thread will hit the same mutes again, so you should be safe to use the current locking mechanism. >> If you want a non-recursive lock, why not add one in DeviceThreads.h? I would certainly encourage everyone to use non-recursive threads and it would be great to add that behavior so that device adapter writers can use that approach. > > I changed DeviceThreads.h like this: > ---------------------------------------- > #ifdef MM_THREAD_NONRECURSIVE_LOCK > #define MM_THREAD_INITIALIZE_GUARD(plock) pthread_mutex_init(plock, NULL) > #else > #define MM_THREAD_INITIALIZE_GUARD(plock) \ > { pthread_mutexattr_t a; pthread_mutexattr_init( &a ); \ > pthread_mutexattr_settype( &a, _MUTEX_RECURSIVE ); \ > pthread_mutex_init(plock,&a); pthread_mutexattr_destroy( &a ); \ > } > #endif > ---------------------------------------- I was more thinking about adding another lock that behaves non-recursively, or by adding a flag to the existing lock mechanism to give the device adapter author the choice between a recursive and non-recursive lock. I am not too wild about doing this through a pre-processor directive. Since we are now including boost in the build, we may as well switch to using the boost locks. It makes building device adapters a bit more complicated though. Best, Nico ------------------------------------------------------------------------------ RSA(R) Conference 2012 Save $700 by Nov 18 Register now http://p.sf.net/sfu/rsa-sfdev2dev1 _______________________________________________ micro-manager-general mailing list [hidden email] https://lists.sourceforge.net/lists/listinfo/micro-manager-general |
| Powered by Nabble | See how NAML generates this page |
