====== C - C++ Threads - Troubleshooting - Deadlocks ======
The most common cause of a DEADLOCK is not acquiring multiple locks in the same order.
A situation where threads block indefinitely because they are waiting to acquire access to resources currently locked by other blocked threads.
Let's see an example:
^Thread 1^Thread 2^
|Lock A|Lock B|
|.. Do some processing|..do some processing|
|Lock B|Lock A|
|.. Do some more processing|..Do some more processing|
|Unlock B|Unlock A|
|Unlock A|Unlock B|
**NOTE:** In some situations, what is going to happen is that when Thread 1 tries to acquire Lock B, it gets blocked because Thread 2 is already holding lock B.
And from Thread 2's perspective, it is blocked on acquiring lock A, but cannot do so because Thread 1 is holding lock A.
Thread 1 cannot release lock A unless it has acquired lock B and so on.
In other words, your program is hanged at this point.
----
===== Example to simulate a deadlock =====
#include
#include
#include
#include
using namespace std;
std::mutex muA;
std::mutex muB;
void CallHome_AB(string message)
{
muA.lock();
//Some additional processing
std::this_thread::sleep_for(std::chrono::milliseconds(100));
muB.lock();
cout << "Thread " << this_thread::get_id() << " says " << message << endl;
muB.unlock();
muA.unlock();
}
void CallHome_BA(string message)
{
muB.lock();
//Some additional processing
std::this_thread::sleep_for(std::chrono::milliseconds(100));
muA.lock();
cout << "Thread " << this_thread::get_id() << " says " << message << endl;
muA.unlock();
muB.unlock();
}
int main()
{
thread t1(CallHome_AB, "Hello from Jupiter");
thread t2(CallHome_BA, "Hello from Pluto");
t1.join();
t2.join();
return 0;
}
**NOTE:** If you run this, it will hang.
* Go ahead and break into debugger to look at the threads window and you will see that Thread 1 (calling function CallHome_Th1()) is trying to acquire mutex B while thread 2 (calling function CallHome_Th2()) is trying to acquire mutex A.
* None of them is making any progress because of the deadlock!
So, what can you do about it?
* The best thing to do is to structure your code in such a way that all locks are acquired in the same order.
* Depending on your situation,you can also employ the following strategies:
- Acquire locks together if both need to be acquired :
std::scoped_lock lock{muA, muB};
- You can use a [[https://en.cppreference.com/w/cpp/thread/timed_mutex|timed mutex]] where you can mandate that a lock be released after a timeout if it is not already available.
----
===== References =====
https://en.cppreference.com/w/cpp/thread/timed_mutex