In boost land on Mac OS, following code will deadlock itself:
boost::mutex m;
m.lock();
m.lock();
the same goes with
boost::mutex m;
boost::mutex::scoped_lock lock(m);
boost::mutex::scoped_lock lock(m);
in Windows land, the same thread can get a Win32 mutex as often as it wants, as long as the release count is the same. I need the exact behavior with a boost sync object.
What you need is a boost::recursive_mutex:
The recursive_mutex class uses a Recursive locking strategy, so attempts to recursively lock a recursive_mutex object succeed and an internal "lock count" is maintained. Attempts to unlock a recursive_mutex object by threads that don't own a lock on it result in undefined behavior.
Note that the boost::mutex:
The mutex class uses an Unspecified locking strategy, so attempts to recursively lock a mutex object or attempts to unlock one by threads that don't own a lock on it result in undefined behavior. This strategy allows implementations to be as efficient as possible on any given platform.
In Computer Science the recursive mutex is called Reentrant Mutex:
In computer science, a reentrant mutex is a mutual exclusion, recursive lock mechanism. In a reentrant mutex, the same thread can acquire the lock multiple times. However, the lock must be released the same number of times or else other threads will be unable to acquire the lock
try to use a recursive mutex : As boost::mutex is not recursive, we need to use its recursive version boost::recursive_mutex
Related
I guess I'm a little unsure of how mutexs work. If a mutex gets locked after some conditional, will it only lock out threads that meet that same condition, or will it lock out all threads regardless until the mutex is unlocked?
Ex:
if (someBoolean)
pthread_mutex_lock(& mut);
someFunction();
pthread_mutex_unlock(& mut);
Will all threads be stopped from running someFunction();, or just those threads that pass through the if-statement?
You need to call pthread_mutex_lock() in order for a thread to be locked. If you call pthread_mutex lock in thread A, but not in thread B, thread B does not get locked (and you have likely defeated the purpose of mutual exclusion in your code, as it's only useful if every thread follows the same locking protocol to protect your code).
The code in question has a few issues outlined below:
if (someBoolean) //if someBoolean is shared among threads, you need to lock
//access to this variable as well.
pthread_mutex_lock(& mut);
someFunction(); //now you have some threads calling someFunction() with the lock
//held, and some calling it without the lock held, which
//defeats the purpose of mutual exclusion.
pthread_mutex_unlock(& mut); //If you did not lock the *mut* above, you must not
//attempt to unlock it.
Will all threads be stopped from running someFunction();, or just those threads that pass through the if-statement?
Only the threads for which someBoolean is true will obtain the lock. Therefore, only those threads will be prevented from calling someFunction() while someone else holds the same lock.
However, in the provided code, all threads will call pthread_mutex_unlock on the mutex, regardless of whether they actually locked it. For mutexes created with default parameters this constitutes undefined behavior and must be fixed.
I'm wondering if it's possible to lock multiple mutexes at the same time, like:
Mutex1.Lock();
{
Mutex2.Lock();
{
// Code locked by mutex 1 and 2.
}
Mutex2.Unlock();
// Code locked by mutex 1.
}
Mutex1.Unlock();
It would be very useful for some situations. Thanks.
std::lock seems to exist for this purpose.
Locks the given Lockable objects lock1, lock2, ..., lockn using a deadlock avoidance algorithm to avoid deadlock.
The objects are locked by an unspecified series of calls to lock, try_lock, unlock. If a call to lock or unlock results in an exception, unlock is called for any locked objects before rethrowing.
http://en.cppreference.com/w/cpp/thread/lock
C++17 also provides scoped_lock for the specific purpose of locking multiple mutexes that prevents deadlock in a RAII style, similar to lock_guard.
#include<mutex>
std::mutex mtx1, mtx2;
void foo()
{
std::scoped_lock lck{mtx1, mtx2};
// proceed
}
It is possible but the order of locking must be consistent throughout the application otherwise deadlock is a likely result (if two threads acquire the locks in opposite order then each thread could be waiting on the other to release one of the locks).
Recommend using a scoped lock and unlock facility for exception safety, to ensure locks are always released (std::lock_guard with std::mutex for example):
std::mutex mtx1;
std::mutex mtx2;
std::lock_guard<std::mutex> mtx1_lock(mtx1);
{
std::lock_guard<std::mutex> mtx2_lock(mtx2);
{
}
}
If your compiler does not support these C++11 features boost has similar in boost::mutex and boost::lock_guard.
I am coming from Java , so i am familiar with synchronize and not mutex.
I wonder if pthread_mutex_t is also reentrancy. if not is there another mechanism for this?
Thank you
This depends on the mutex type, the default does no checking and an attempt to lock it more than once in the same thread results in undefined behavior. Read about it here.
You can create a mutex of type PTHREAD_MUTEX_RECURSIVE to be able to recursively lock it, which is done by providing a pthread_mutexattr_t with the desired mutex type to pthread_mutex_init
According to the manual, you can declare a mutex object as PTHREAD_MUTEX_RECURSIVE:
If the mutex type is PTHREAD_MUTEX_RECURSIVE, then the mutex shall
maintain the concept of a lock count. When a thread successfully
acquires a mutex for the first time, the lock count shall be set to
one. Every time a thread relocks this mutex, the lock count shall be
incremented by one. Each time the thread unlocks the mutex, the lock
count shall be decremented by one. When the lock count reaches zero,
the mutex shall become available for other threads to acquire. If a
thread attempts to unlock a mutex that it has not locked or a mutex
which is unlocked, an error shall be returned.
See also pthread_mutex_attr_settype.
By default, pthread_mutex is not recursive, but there is a way for initialize it as recursive:
pthread_mutexattr_t Attr;
pthread_mutexattr_init(&Attr);
pthread_mutexattr_settype(&Attr, PTHREAD_MUTEX_RECURSIVE);
pthread_mutex_init(&_mutex, &Attr);
I think you don't mean reentrancy but recursiveness. Having reentrant locking with Java is impossible.
Recursive locking is simply when you already own a lock in a thread you might lock it again without any issues. Actually this is very efficient since this doesn't need any atomic operations but just a compare of the owning thread id of the mutex with the current thread id and if both are equal, an owning-counter is - non-atomically (!) - incremented.
Reentrancy is rather is asynchronous execution within the same thread. For example if you have a signal handler in C / C++ and this signal handler locks a mutex already owned by this thread in the synchronous part of the code again. I'm not aware if there are mutex implementations that can handle this situations but there's not much need for that so I guess not. I think such mutexes couldn't be as efficient as normal recursive mutexes.
I want to use a shared mutex so threads only get locked when a vector/map/whatever is written to rather than read from. But I think func2() will never get the uniqueue lock because func1() will never get to unlock. Is there any way to not count a same-thread lock on shared_mutex when trying to get the uniqueue lock? Or would the problem still occur even then?
I'm guessing I need to find a way to force-get the lock (one thread at a time) once all threads have reached func2() OR have released the lock.
func2()
{
boost::unique_lock<boost::shared_mutex> lock_access3(shared_mutex);
/*stuff*/
lock_access3.unlock();
}
func1()
{
boost::shared_lock<boost::shared_mutex> lock_access1(shared_mutex);
func2();
lock_access1.unlock();
}
I believe you need to use a recursive mutex. See the good discussion on this question:
Recursive Lock (Mutex) vs Non-Recursive Lock (Mutex)
Boost provides this in the boost:recursive_mutex class.
Two things you need to do:
1) Since func1 implements a write operation, it needs to acquire an exclusive lock, not a shared lock.
2) Since func2 is called with the lock already held, it should not try to acquire the lock.
I read this answer on SO:
Because the recursive mutex has a sense of ownership, the thread that grabs the mutex must be the same thread that releases the mutex. In the case of non-recursive mutexes, there is no sense of ownership and any thread can usually release the mutex no matter which thread originally took the mutex.
I'm confused by the last statement. Can one thread lock a mutex and another different thread unlock that mutex? I thought that same thread should be the only one able to unlock the mutex? Or is there any specific mutex that allows this? I hope someone can clarify.
Non-recursive mutex
Most mutexes are (or at least should be) non-recursive. A mutex is an object which can be acquired or released atomically, which allows data which is shared between multiple threads to be protected against race conditions, data corruption, and other nasty things.
A single mutex should only ever be acquired once by a single thread within the same call chain. Attempting to acquire (or hold) the same mutex twice, within the same thread context, should be considered an invalid scenario, and should be handled appropriately (usually via an ASSERT as you are breaking a fundamental contract of your code).
Recursive mutex
This should be considered a code smell, or a hack. The only way in which a recursive mutex differs from a standard mutex is that a recursive mutex can be acquired multiple times by the same thread.
The root cause of the need for a recursive mutex is lack of ownership and no clear purpose or delineation between classes. For example, your code may call into another class, which then calls back into your class. The starting class could then try to acquire the same mutex again, and because you want to avoid a crash, you implement this as a recursive mutex.
This kind of topsy-turvy class hierarchy can lead to all sorts of headaches, and a recursive mutex provides only a band-aid solution for a more fundamental architectural problem.
Regardless of the mutex type, it should always be the same thread which acquires and releases the same mutex. The general pattern that you use in code is something like this:
Thread 1
Acquire mutex A
// Modify or read shared data
Release mutex A
Thread 2
Attempt to acquire mutex A
Block as thread 1 has mutex A
When thread 1 has released mutex A, acquire it
// Modify or read shared data
Release mutex A
It gets more complicated when you have multiple mutexes that can be acquired simultaneously (say, mutex A and B). There is the risk that you'll run into a deadlock situation like this:
Thread 1
Acquire mutex A
// Access some data...
*** Context switch to thread 2 ***
Thread 2
Acquire mutex B
// Access some data
*** Context switch to thread 1 ***
Attempt to acquire mutex B
Wait for thread 2 to release mutex B
*** Context switch to thread 2 ***
Attempt to acquire mutex A
Wait for thread 1 to release mutex A
*** DEADLOCK ***
We now have a situation where each thread is waiting for the other thread to release the other lock -- this is known as the ABBA deadlock pattern.
To prevent this situation, it is important that each thread always acquires the mutexes in the same order (e.g. always A, then B).
Recursive mutexes are thread-specific by design (the same thread locking again = recursion, another thread locking meanwhile = block). Regular mutexes do not have this design and thus they can in fact be locked and unlocked in different threads.
I think this covers all your questions. Straight from linux man pages for pthreads:
If the mutex type is PTHREAD_MUTEX_NORMAL, deadlock detection shall not be provided. Attempting to relock the mutex causes deadlock. If a thread
attempts to unlock a mutex that it has not locked or a mutex which is unlocked, undefined behavior results.
If the mutex type is PTHREAD_MUTEX_ERRORCHECK, then error checking shall be provided. If a thread attempts to relock a mutex that it has already
locked, an error shall be returned. If a thread attempts to unlock a mutex that it has not locked or a mutex which is unlocked, an error shall be
returned.
If the mutex type is PTHREAD_MUTEX_RECURSIVE, then the mutex shall maintain the concept of a lock count. When a thread successfully acquires a
mutex for the first time, the lock count shall be set to one. Every time a thread relocks this mutex, the lock count shall be incremented by one.
Each time the thread unlocks the mutex, the lock count shall be decremented by one. When the lock count reaches zero, the mutex shall become
available for other threads to acquire. If a thread attempts to unlock a mutex that it has not locked or a mutex which is unlocked, an error shall
be returned.
If the mutex type is PTHREAD_MUTEX_DEFAULT, attempting to recursively lock the mutex results in undefined behavior. Attempting to unlock the mutex
if it was not locked by the calling thread results in undefined behavior. Attempting to unlock the mutex if it is not locked results in undefined
behavior.