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?

Petko Six
  • 285
  • 1
  • 3
  • 7

2 Answers2

7

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.

Jesper Juhl
  • 30,449
  • 3
  • 47
  • 70
  • Thank you for your answer! :) – Petko Six Jul 20 '16 at 19:50
  • 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. – lorro Jul 20 '16 at 20:57
  • @lorro 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. :-) – Jesper Juhl Jul 20 '16 at 21:03
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.

D.Shawley
  • 58,213
  • 10
  • 98
  • 113