Holding two mutex locks at the same time
Asked Answered
C

2

6

I would like to know if there would be any issue if I hold two boost::scoped_locks at the same time. The locks are locking different mutexes. Consider the following example:

void foo1()
{
   boost::recursive_mutex::scoped_lock lock(mutex1);
   foo2();
}

void foo2()
{
   boost::recursive_mutex::scoped_lock lock(mutex2);
}

I know that this shouldn't cause a deadlock. But are there any other issues. Maybe this could cause the thread to sleep for too long?

Copp answered 20/7, 2016 at 19:25 Comment(0)
D
8

Holding more than one lock is not in itself a problem.

Problems arise when other threads attempt to obtain those same locks in different order and you end up with ABBA deadlocks. Thread 1 locks A and B, then thread 2 wants to lock B then A and both end up being blocked (if the locking interleaves so t1 locks A, then t2 locks B and then both block trying to lock the other) waiting for the other to release one of the locks in order to be able to continue (and release their own held locks which would allow the other to continue).

So, the general rule of thumb is; if you need to obtain more than one lock, make damn sure that all threads always attempt to aquire those locks in the same order.

Dimitri answered 20/7, 2016 at 19:36 Comment(3)
Thank you for your answer! :)Copp
Just to continue: one way to ensure locking order is to have lightweight wrappers around the mutexes/locks, and then have layer i's constructor take const ref of layer (i-1)'s obtained lock. Then you cannot construct it any other way. Or - use C++17 and lock them at the same time.Burkey
@Burkey I'm well aware that there are many ways to address the issue. But I was not trying to give advice on how to solve the problem implementation wise, merely to point out what the potential problem is and a general rule that avoids it. Implementation left as an exercise for the reader. :-)Dimitri
P
7

This can cause a deadlock if anyone acquires both mutexes in the opposite order.

void bar1() {
    boost::recursive_mutex::scoped_lock lock(mutex2);
    bar2();
}

void bar2() {
    boost::recursive_mutex::scoped_lock lock(mutex1);
}

Two threads interleaved as follows will deadlock:

                                 mutex1  mutex2
Time    Thread A     Thread B    owner   owner
  0     foo1()                     A
  1                  bar1()        A       B
  2                  bar2()        A       B
  3     foo2()                     A       B

At this point threads A & B are deadlocked. I don't think that boost provides protection against this though the underlying OS may.

President answered 20/7, 2016 at 19:36 Comment(1)
Thank you for your answer! :)Copp

© 2022 - 2024 — McMap. All rights reserved.