multiple "lockMutex" from the same thread?
BlitzMax Forums/BlitzMax Programming/multiple "lockMutex" from the same thread?
| ||
Hello! Is it safe to "lockMutex" and "tryLockMutex" the same Mutex multiple times from the same thread? (reason: within the "catch" phrase of a "try" statement I would like to "check" - using "trylockmutex" - if the Mutex has been locked in order to unlock it again) |
| ||
They should be recursive, so yes. As for tryLockMutex, it's safe to call that at any time from any thread, I believe, since if the lock cannot be acquired, it should simply return that it can't. However, you'd have to check pthread/Win32 documentation respectively to see how each platform handles those scenarios. Either way, again, they should be recursive. |
| ||
Unless the behavior has changed, mutexes on Windows are recursive, while on Linux and Mac they are not. You can always call TryLock, but it will return 0 if the mutex has already been taken by the same thread (on *nix). If you need recursive behavior, it is quite simple to wrap mutexes into your own recursive type. |
| ||
Otus: I took a look at the source code for 1.38 and it appears to be recursive now. |
| ||
Yeah, sorry. It has been changed since I last tried. Mutexes are recursive on all platforms. |
| ||
Hmmm, let me explain my situation using some source code: superstrict global Mutex:TMutex = createMutex() function ThreadBody:Object (Data:Object) debuglog("Subthread started") lockMutex(Mutex) debuglog("Mutex locked by subthread") if (tryLockMutex(Mutex)) then debuglog("tryLockMutex succeeded in Subthread") else debuglog("tryLockMutex failed in Subthread") end if unlockMutex(Mutex) debuglog("Mutex unlocked by subthread") debuglog("Subthread finished") end function debuglog("Mainthread started") debuglog("spawning subthread") local Subthread:TThread = createThread(ThreadBody,null) delay(2000) if (tryLockMutex(Mutex)) then debuglog("tryLockMutex succeeded in MainThread") else debuglog("tryLockMutex failed in MainThread") end if debuglog("Mainthread stopped") If you run that code you will see the following output: DebugLog:Mainthread started DebugLog:spawning subthread DebugLog:Subthread started DebugLog:Mutex locked by subthread DebugLog:tryLockMutex succeeded in Subthread DebugLog:Mutex unlocked by subthread DebugLog:Subthread finished DebugLog:tryLockMutex failed in MainThread DebugLog:Mainthread stopped Process complete In practice this means: there is no way to check if a thread has already locked a mutex (itself) or not - because "tryLockMutex" may succeed because the Mutex was not yet locked or because it was already locked by the thread which issued the "tryLockMutex" Is there a good alternative? Thanks in advance for any hint! |
| ||
Here is a potential "workaround"superstrict global Mutex:TMutex = createMutex() global MutexLocker:TThread = null function _lockMutex (Mutex:TMutex) lockMutex(Mutex) MutexLocker = currentThread() end function function _tryLockMutex:int (Mutex:TMutex) if (tryLockMutex(Mutex)) then if (MutexLocker = currentThread()) then ' Mutex was already locked unlockMutex(Mutex) ' reduce internal Mutex count! else MutexLocker = currentThread() end if return true else return false end if end function function _unlockMutex (Mutex:TMutex) MutexLocker = null unlockMutex(Mutex) end function function ThreadBody:Object (Data:Object) debuglog("Subthread started") _lockMutex(Mutex) debuglog("Mutex locked by subthread") if (_tryLockMutex(Mutex)) then debuglog("tryLockMutex succeeded in Subthread") else debuglog("tryLockMutex failed in Subthread") end if _unlockMutex(Mutex) debuglog("Mutex unlocked by subthread") debuglog("Subthread finished") end function debuglog("Mainthread started") debuglog("spawning subthread") local Subthread:TThread = createThread(ThreadBody,null) delay(2000) if (_tryLockMutex(Mutex)) then debuglog("tryLockMutex succeeded in MainThread") else debuglog("tryLockMutex failed in MainThread") end if debuglog("Mainthread stopped") Are the three functions (_lockMutex, _tryLockMutex and _unlockMutex) free of "race conditions"? |
| ||
So you would like to have a reentrant mutex that is not recursive? So you can lock it any number of times from the same thread, but only have to unlock it once? You are using a single global mutexLocker while passing the mutex as a parameter, which does not seem appropriate. Also, you don't really need to know the thread, just keep a flag to check for double locks: Type TNRMutex Field _mutex:TMutex Field _locked:Int Method New() _mutex = CreateMutex() End Method Method Lock() _mutex.Lock If _locked Then _mutex.Unlock Else _locked = True End Method Method TryLock:Int() If Not _mutex.TryLock() Return False If _locked Then _mutex.Unlock Else _locked = True Return True End Method Method Unlock() _locked = False _mutex.Unlock End Method End Type (I would've used inheritance, but unfortunately New TMutex does not do the initialization in Create.) |
| ||
Otus, thanks! Using a global MutexLocker was just a simple experiment (and test). Meanwhile, I created a type very similar to yours (but still with a MutexLocker that keeps the locking thread in order to catch the situation when a different thread tries to unlock a mutex) Thanks to "Object Orientation", it's been really simple to adapt my program to the new Mutex type... |