Reputation: 1041
Is deadlock possible in the following code snippet:
void f()
{
{
std::lock_guard <std::mutex> inner (lock1);
// do something and conditionally return
}
std::lock_guard <std::mutex> outer (lock2);
// do something
}
IOW, if multiple threads call this function, can a deadlock occur?
I am not too sure so any help would be highly appreciated.
Upvotes: 1
Views: 274
Reputation: 3018
The answer is no. A fast explanation is that the locks are always acquired in the same order and there is no repetition (first lock1
, then lock2
if no return
takes place).
Each thread first holds lock1
. Whether the thread then conditionally returns or not does not matter. It does not try to acquire lock2
while lock1
is still held. In other words, lock1
is always unlocked by a thread before that same thread tries to acquire lock2
.
As @Gupta suggested, here is a link which describes how deadlocks can be avoided by locking in a predefined order.
Upvotes: 3
Reputation: 2070
If you refactor your code so each scope is a function, it become clears that the locks are never locked at the same time by a single thread :
std::mutex lock1;
std::mutex lock2;
// One mutex in g => No deadlock possible
void g()
{
std::lock_guard <std::mutex> inner (lock1);
// do something
}
// One mutex in h => No deadlock possible
void h()
{
std::lock_guard <std::mutex> outer (lock2);
// do something
}
// No mutex in f => No deadlock possible
void f()
{
g();
h();
}
From this you can conclude that when a thread is a requesting a lock, it does not hold one. This makes deadlock impossible.
You can check this yourself by creating a BasicLockable object that simply wrap std::mutex
and add a trace :
class PrinterMutex {
public:
PrinterMutex(const std::string& _name) : name(_name) {}
~PrinterMutex() {}
void lock() {
std::cout << "lock : " << name << std::endl;
m.lock();
}
void unlock() {
std::cout << "unlock : " << name << std::endl;
m.unlock();
}
private:
std::mutex m;
std::string name;
};
PrinterMutex lock1("lock1");
PrinterMutex lock2("lock2");
int main()
{
{
std::lock_guard <PrinterMutex> inner (lock1);
// do something and conditionally return
}
std::lock_guard <PrinterMutex> outer (lock2);
// do something
}
The trace will show you that a thread will always release a lock before requesting one, making a deadlock impossible.
If you actually need multiple mutex in your code, you should use std::lock
with multiple Lockable object to lock mutexes with deadlock avoidance algorithm.
std::mutex lock1;
std::mutex lock2;
void g()
{
std::lock(lock1, lock2);
std::lock_guard<std::mutex> inner (lock1, std::adopt_lock);
std::lock_guard<std::mutex> outer (lock2, std::adopt_lock);
// Do something
}
Upvotes: 5
Reputation: 2754
If the only two accesses to your two locks (lock1
, lock2
) are the ones shown in your code snippet (i.e. the locks are never accessed anywhere else), a deadlock can't happen, as the two locks are never locked simultaneously.
If the pair of braces around the inner
lock guard weren't there, the two locks would be locked simultaneously, but since the order is always the same (first lock1
, then lock2
) deadlocks would still be impossible.
However, if f
were to call some other function that might lock some other lock (especially via function pointer or virtual functions), or f
would call itself recursively, a deadlock could happen (depending on the exact code).
Upvotes: 1