

Though std::lock can help us avoid deadlock in those cases where we need to acquire two or more locks together, it doesn’t help if they are acquired separately-in that case we have to rely on our discipline as developers to ensure we don’t get deadlock. Std::lock_guard lock_b(rhs.m,std::adopt_lock) #3 Listing A: Locking two mutexes with lock() in a comparison operatorīool operator lock_a(lhs.m,std::adopt_lock) #2 If std::lock had successfully acquired a lock on the other mutex, then this lock is released automatically. Also, it’s worth noting that locking either of lhs.m or rhs.m inside the call to std::lock can throw an exception: in this case the exception is propagated out of std::lock. This ensures that the mutexes are correctly unlocked on function exit in the general case where the protected operation might throw an exception, as well as allowing for a simple return with the comparison result in this case. The std::adopt_lock parameter is supplied in addition to the mutex to indicate to the std::lock_guard objects that the mutexes are already locked, and they should just “adopt” the lock on the mutex rather than locking it in the constructor. Then, the call to std::lock() (#1) locks the two mutexes, and two std::lock_guard instances are constructed (#2, #3): one for each mutex. First, the arguments are checked to ensure they are different instances, as locking the same mutex twice is undefined behaviour if it’s not recursive.
#Deadlock example how to
The example in listing 4.6 shows how to use this for a comparison operator. Thankfully, the C++ Standard Library has a cure for this in the form of std::lock-a function that can lock two or more mutexes at once without risk of deadlock. the mutex for the instance supplied as the first parameter, then the mutex for the instance supplied as the second parameter), then this can backfire: all it takes is for two threads to try and compare the same instances with the parameters swapped, and you’ve got deadlock! However, if a fixed order is chosen (e.g. Consider for example a comparison operation on two instances of the same class-in order to avoid the comparison being affected by concurrent modifications, the mutexes on both instances must be locked. Sometimes this is straightforward, as the mutexes are serving different purposes, but other times it is not so simple, such as when the mutexes are each protecting a separate instance of the same class. The common advice for avoiding deadlock is to always lock the two mutexes in the same order: if you always lock mutex A before mutex B, then you’ll never deadlock. This scenario is called deadlock, and is the biggest problem with having to lock two or more mutexes in order to perform an operation. Neither thread can proceed, as each is waiting for the other to release its mutex. Now imagine that you’ve not got children arguing over toys, but threads arguing over locks on mutexes: each of a pair of threads needs to lock both of a pair of mutexes to perform some operation, and each thread has one mutex and is waiting for the other. Now they’re stuck-unless the children are remarkably kind, each will hold onto whatever they’ve got and demand the other gives them the other bit, and neither gets to play. One finds the drum and the other finds the drumstick.


Now imagine that the drum and the drumstick are buried (separately) in the toy box, and your children both decide to play with it at the same time, so go rummaging in the toy box. If the other child wants to play, they have to wait, however sad that makes them. If one of them gets both the drum and the drumstick, they can merrily play the drum until they get fed up. Now imagine that you’ve got two small children, both of whom like playing with it. Imagine you’ve got a toy that comes in two parts, and you need both parts to play with it-a toy drum and drumstick, for example. This article is excerpted from the pre-release of Manning Publications’ book, C++ Concurrency in Action By Anthony Williams.
