I learned that I should unlock reverse order to lock order.
For example.
A.lock();
B.lock();
B.unlock();
A.unlock();
But, what happen if I did like this :
A.lock();
B.lock();
A.unlock();
B.unlock();
I try to make a deadlock scenario,
but if I always lock A earlier then B, then I don't know how deadlock would happen.
Would you help me?
In the simple case given, unlocking in the reverse order is not necessary to avoid a deadlock.
However, as the code gets more complicated, unlocking in the reverse order helps you maintain proper lock ordering.
Consider:
A.lock();
B.lock();
Foo();
A.unlock();
Bar();
B.unlock();
If Bar() attempts to reacquire A, you've effectively broken your lock ordering. You're holding B and then trying to get A. Now it can deadlock.
If you unlock in the reverse order style (which is very natural if you use RAII):
A.lock();
B.lock();
Foo();
B.unlock();
Bar();
A.unlock();
then it doesn't matter if Bar() attempts to take a lock, as lock ordering will be preserved.
Lock ordering just means that you prevent deadlocks by obtaining locks in a fixed order, and do not obtain locks again after you start unlocking.
I do not think the order of unlocks makes any difference here (in fact, it should be beneficial to release a lock as soon as possible, even if out of order)
Your example isn't going to deadlock with itself ever. Unlocking in reverse order isn't important, it's locking in a consistent order. This will dead lock, even though unlocks are in reverse order
Thread 1
A.lock();
B.lock();
B.unlock();
A.unlock();
Thread 2
B.lock();
A.lock();
A.unlock();
B.unlock();
I do not think deadlock would happen here. The general deadlock concept is one thread waits for some resource locked by other thread, while other thread needs resource locked by first thread to finish and release resource needed by first.
Further reading
The order of unlock will not affect how prone your system is to deadlock, however there's one reasons to think about the order of unlock:
In order to avoid deadlocks you must make sure that your lock/unlocks are paired, in that you never miss an unlock. As a stylistic approach, by conspicuosly having blocks of code that are responsible for a particualr lock it's much easier to visually identifiy that locks and unlocks are paired. The end-effect is that clearly correct code will probably take and release the locks as you describe.
And for Java, unlocking is in the reverse order if synchronized
keyword is used for locking. There is no way to unlock in a different order for using synchronized keyword.
synchronized(a) {
synchronized(b) {
// statements
}
}
> lock(B)
> ---------- lock(C)
> ---------- lock(B) >>>> would block here
> ---------- release(B)
> ---------- release(C)
> lock(C) >>>>>> would block here
> release(B)
> release(C)
Their answer is great, here is another situation a deadlock may occur if unordered lock & release is performed. One word, Unordered release & lock break the assumption which we used to design our shared resource management and critical zone.
Related
https://en.cppreference.com/w/cpp/thread/lock_tag
void transfer(bank_account &from, bank_account &to, int amount)
{
// lock both mutexes without deadlock
std::lock(from.m, to.m);
// make sure both already-locked mutexes are unlocked at the end of scope
std::lock_guard<std::mutex> lock1(from.m, std::adopt_lock);
std::lock_guard<std::mutex> lock2(to.m, std::adopt_lock);
// equivalent approach:
// std::unique_lock<std::mutex> lock1(from.m, std::defer_lock);
// std::unique_lock<std::mutex> lock2(to.m, std::defer_lock);
// std::lock(lock1, lock2);
from.balance -= amount;
to.balance += amount;
}
What do they gain by locking two mutexes at once?
What have they gained by defered lock here?
Please explain the reason behind that decision of theirs.
If I modify a bank account without locking it, someone else could try to modify it at the same time. This is a race and the result will be undefined behaviour (usually lost or magically created money).
While transferring money, I am modifying 2 bank accounts. So they both need to be locked.
The problem is that when locking more than one thing, every locker must lock and unlock in the same order, otherwise we get deadlocks.
When it's bank accounts, there is no natural order of locks. Thousands of threads could be transferring money in all directions.
So we need a method of locking more than one mutex in a way that works around this - this is std::lock
std::lock merely locks the mutex - it does not guarantee unlocking on exit from the current code block.
std::lock_guard<> unlocks the mutex it's referring to upon destruction (see RAII). This makes the code behave correctly in all circumstances - even when there is an exception which might cause an early exit from the current code block without the code flowing over statement such as to.m.unlock()
A good explanation (with examples) here: https://wiki.sei.cmu.edu/confluence/display/cplusplus/CON53-CPP.+Avoid+deadlock+by+locking+in+a+predefined+order
Extension on Richard Hodges's answer
What do they gain by locking two mutexes at once?
Richard explained nicely already, just a little bit more explicit: we avoid dead-lock this way (std::lock is implemented such that dead-lock won't occur).
What have they gained by deferred lock here?
Deferring the lock results in not acquiring it immediately. That's important because if they did so, they would just do it without any protection against dead-lock (which the subsequent std::lock then achieves).
About dead lock avoidance (see std::lock):
Locks the given Lockable objects lock1, lock2, ..., lockn using a deadlock avoidance algorithm to avoid deadlock.
The objects are locked by an unspecified series of calls to lock, try_lock, and unlock. [...]
Side note: another, much simpler algorithm avoiding dead locks is always locking the bank account with e. g. lower account number (AN) first. If a thread is waiting for the lock of higher AN, then the other thread holding it either already has both of the locks acquired or is waiting for the second – which cannot be the one of the first thread as it must have a yet higher AN.
This does not change much for arbitrary number of threads, any thread holding a lower lock is waiting for a higher one, if hold as well. If you draw a directed graph with edges from A to B if A is waiting for second lock that B holds, you'll get a (multi-) tree structure, but you won't ever have circular substructures (which would indicate a dead lock).
The bank account data structure has a lock for each account.
When transferring money from one account to another, we need to lock both accounts (since we are removing money from one and adding it to another). We would like this operation not to deadlock, so lock both at once using std::lock, since doing it like that ensures there isn't a deadlock.
After we finish the transaction we need to make sure we release the lock. This code does that using RAII. With the adopt_lock tag, we make the object adopt an already locked mutex (which will be released when lock1 falls out of scope).
With the defer_lock tag, we create a unique_lock for a currently unlocked mutex, with the intention of locking it later. Again, it will be unlocked when the unique_lock falls out of scope.
from and to are 2 accounts which may be used anywhere in the application separatelly.
By having mutex for each account, you make sure nobody uses from nor to accounts while you do the transfer.
lock_guard will release mutex when exiting from function.
I thought the difference between std::lock() and std::try_lock() is only in try_lock() , if locks are not available , immediately it will return false while in the case of std::lock(), it will go in blocked state.
Cpp reference for std::lock
void lock( Lockable1& lock1, Lockable2& lock2, LockableN&... lockn );
Locks the given Lockable objects lock1, lock2, ..., lockn using a deadlock avoidance algorithm to avoid deadlock.
Cpp reference by try_lock
int try_lock( Lockable1& lock1, Lockable2& lock2, LockableN&... lockn);
Tries to lock each of the given Lockable objects lock1, lock2, ..., lockn by calling try_lock in order beginning with the first.
I have following two questions:
Why std::lock() provides deadlock avoidence but std::try_lock doesn't?
why in std::lock , order of lock doesn't matter( it could be lock2 , lock 3, lock1, ...) while in std::try_lock() order of locks are maintained (lock1, lock2, lock3....)
Why std::lock() provides deadlock avoidence but std::try_lock doesn't?
It doesn't need to. If the try_lock fails to lock all members then it releases all members. You can't get a deadlock from try_lock if another thread owns some or all of these resources since you will return immediately.
From try_lock:
If a call to try_lock fails, no further call to try_lock is performed, unlock is called for any locked objects and a 0-based index of the object that failed to lock is returned.
why in std::lock , order of lock doesn't matter( it could be lock2 , lock 3, lock1, ...) while in std::try_lock() order of locks are maintained (lock1, lock2, lock3....)
I suspect because of ease. There is no dead-lock avoidance algorithm required because you either lock all of them, or you can't lock one in which case you release all of them. For this reason the easiest locking approach is to try_lock beginning with the first and moving through the variadic template list. Furthermore the return value indicates that it will return the index of the first failed lock. In order to achieve this you must iterate from left to right.
The answer is simple. std::try_lock does not need deadlock avoidance mechanism, as it will return immediately if a particular lock is not available and unlock all the locks which were obtained. On the other hand, std::lock() should only return once all the locks are acquired and will have them locked - and this is why it needs deadlock avoidance.
Consider the case of 2 locks A and B. Let's say, thread 2 owns the lock B, and desires to lock A. At the same time, thread 1 wants to lock A and B. With std::try_lock called at thread 1, what is going to happen is that A is successfully locked, a failed attempt to lock B is made, after which A is unlocked (and function return false) - and thread 2 successfuly locks A.
On the other hand, if a std::lock would not use deadlock avoidance, and thread 1 were to call it, the sequence of events would be: thread 1 locks A, thread 1 attempts to lock B and is stopped here, thread 2 attempts to lock A and is stopped here. And this point, neither of the threads can make progress and we have a classical deadlock.
2nd Q 1st:
Because try_lock is supposed to return the ordinal number of 1st failed lock, but lock has to do lock all locks before returning.
1st Q 2nd:
In order to avoid deadlock, the order of locking the locks must be the same among all locking threads. Thus, deadlock prevention involves reordering the inputs in a deterministic and repeatable manor. Where the ordinal of the locks is relevant to the caller, reordering the input cannot take place and deadlock prevention cannot be used.
I am trying to use read/write lock in C++ using shared_mutex
typedef boost::shared_mutex Lock;
typedef boost::unique_lock< Lock > WriteLock;
typedef boost::shared_lock< Lock > ReadLock;
class Test {
Lock lock;
WriteLock writeLock;
ReadLock readLock;
Test() : writeLock(lock), readLock(lock) {}
readFn1() {
readLock.lock();
/*
Some Code
*/
readLock.unlock();
}
readFn2() {
readLock.lock();
/*
Some Code
*/
readLock.unlock();
}
writeFn1() {
writeLock.lock();
/*
Some Code
*/
writeLock.unlock();
}
writeFn2() {
writeLock.lock();
/*
Some Code
*/
writeLock.unlock();
}
}
The code seems to be working fine but I have a few conceptual questions.
Q1. I have seen the recommendations to use unique_lock and shared_lock on http://en.cppreference.com/w/cpp/thread/shared_mutex/lock, but I don't understand why because shared_mutex already supports lock and lock_shared methods?
Q2. Does this code have the potential to cause write starvation? If yes then how can I avoid the starvation?
Q3. Is there any other locking class I can try to implement read write lock?
Q1: use of a mutex wrapper
The recommendation to use a wrapper object instead of managing the mutex directly is to avoid unfortunate situation where your code is interrupted and the mutex is not released, leaving it locked forever.
This is the principle of RAII.
But this only works if your ReadLock or WriteLock are local to the function using it.
Example:
readFn1() {
boost::unique_lock< Lock > rl(lock);
/*
Some Code
==> imagine exception is thrown
*/
rl.unlock(); // this is never reached if exception thrown
} // fortunately local object are destroyed automatically in case
// an excpetion makes you leave the function prematurely
In your code this won't work if one of the function is interupted, becaus your ReadLock WriteLock object is a member of Test and not local to the function setting the lock.
Q2: Write starvation
It is not fully clear how you will invoke the readers and the writers, but yes, there is a risk:
as long as readers are active, the writer is blocked by the unique_lock waiting for the mutex to be aquirable in exclusive mode.
however as long as the wrtier is waiting, new readers can obtain access to the shared lock, causing the unique_lock to be further delayed.
If you want to avoid starvation, you have to ensure that waiting writers do get the opportunity to set their unique_lock. For example att in your readers some code to check if a writer is waiting before setting the lock.
Q3 Other locking classes
Not quite sure what you're looking for, but I have the impression that condition_variable could be of interest for you. But the logic is a little bit different.
Maybe, you could also find a solution by thinking out of the box: perhaps there's a suitable lock-free data structure that could facilitate coexistance of readers and writers by changing slightly the approach ?
The types for the locks are ok but instead of having them as member functions create then inside the member functions locktype lock(mymutex). That way they are released on destruction even in the case of an exception.
Q1. I have seen the recommendations to use unique_lock and shared_lock on http://en.cppreference.com/w/cpp/thread/shared_mutex/lock, but I don't understand why because shared_mutex already supports lock and lock_shared methods?
Possibly because unique_lock has been around since c++11 but shared_lock is coming onboard with c++17. Also, [possibly] unique_lock can be more efficient. Here's the original rationale for shared_lock [by the creator] http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2007/n2406.html and I defer to that.
Q2. Does this code have the potential to cause write starvation? If yes then how can I avoid the starvation?
Yes, absolutely. If you do:
while (1)
writeFn1();
You can end up with a time line of:
T1: writeLock.lock()
T2: writeLock.unlock()
T3: writeLock.lock()
T4: writeLock.unlock()
T5: writeLock.lock()
T6: writeLock.unlock()
...
The difference T2-T1 is arbitrary, based on amount of work being done. But, T3-T2 is near zero. This is the window for another thread to acquire the lock. Because the window is so small, it probably won't get it.
To solve this, the simplest method is to insert a small sleep (e.g. nanosleep) between T2 and T3. You could do this by adding it to the bottom of writeFn1.
Other methods can involve creating a queue for the lock. If a thread can't get the lock, it adds itself to a queue and the first thread on the queue gets the lock when the lock is released. In the linux kernel, this is implemented for a "queued spinlock"
Q3. Is there any other locking class I can try to implement read write lock?
While not a class, you could use pthread_mutex_lock and pthread_mutex_unlock. These implement recursive locks. You could add your own code to implement the equivalent of boost::scoped_lock. Your class can control the semantics.
Or, boost has its own locks.
I learned that I should unlock reverse order to lock order.
For example.
A.lock();
B.lock();
B.unlock();
A.unlock();
But, what happen if I did like this :
A.lock();
B.lock();
A.unlock();
B.unlock();
I try to make a deadlock scenario,
but if I always lock A earlier then B, then I don't know how deadlock would happen.
Would you help me?
In the simple case given, unlocking in the reverse order is not necessary to avoid a deadlock.
However, as the code gets more complicated, unlocking in the reverse order helps you maintain proper lock ordering.
Consider:
A.lock();
B.lock();
Foo();
A.unlock();
Bar();
B.unlock();
If Bar() attempts to reacquire A, you've effectively broken your lock ordering. You're holding B and then trying to get A. Now it can deadlock.
If you unlock in the reverse order style (which is very natural if you use RAII):
A.lock();
B.lock();
Foo();
B.unlock();
Bar();
A.unlock();
then it doesn't matter if Bar() attempts to take a lock, as lock ordering will be preserved.
Lock ordering just means that you prevent deadlocks by obtaining locks in a fixed order, and do not obtain locks again after you start unlocking.
I do not think the order of unlocks makes any difference here (in fact, it should be beneficial to release a lock as soon as possible, even if out of order)
Your example isn't going to deadlock with itself ever. Unlocking in reverse order isn't important, it's locking in a consistent order. This will dead lock, even though unlocks are in reverse order
Thread 1
A.lock();
B.lock();
B.unlock();
A.unlock();
Thread 2
B.lock();
A.lock();
A.unlock();
B.unlock();
I do not think deadlock would happen here. The general deadlock concept is one thread waits for some resource locked by other thread, while other thread needs resource locked by first thread to finish and release resource needed by first.
Further reading
The order of unlock will not affect how prone your system is to deadlock, however there's one reasons to think about the order of unlock:
In order to avoid deadlocks you must make sure that your lock/unlocks are paired, in that you never miss an unlock. As a stylistic approach, by conspicuosly having blocks of code that are responsible for a particualr lock it's much easier to visually identifiy that locks and unlocks are paired. The end-effect is that clearly correct code will probably take and release the locks as you describe.
And for Java, unlocking is in the reverse order if synchronized
keyword is used for locking. There is no way to unlock in a different order for using synchronized keyword.
synchronized(a) {
synchronized(b) {
// statements
}
}
> lock(B)
> ---------- lock(C)
> ---------- lock(B) >>>> would block here
> ---------- release(B)
> ---------- release(C)
> lock(C) >>>>>> would block here
> release(B)
> release(C)
Their answer is great, here is another situation a deadlock may occur if unordered lock & release is performed. One word, Unordered release & lock break the assumption which we used to design our shared resource management and critical zone.
Let's say I have two variables, protected_var1 and protected_var2. Let's further assume that these variables are updated via multiple threads, and are fairly independent in that usually one or the other but not both is worked on - so they both have their own mutex guard for efficiency.
Assuming:
-I always lock mutexes in order (mutex1 then mutex2) in my code in regions where both locks are required.
-Both mutexes are used many other places by them selves (like just lock mutex1, or just lock mutex2).
Does the order in which I unlock the mutexes at the end of a function using both make a difference in this situation?
void foo()
{
pthread_mutex_lock(&mutex1);
pthread_mutex_lock(&mutex2);
int x = protected_var1 + protected_var2;
pthread_mutex_unlock(&mutex1); //Does the order of the next two lines matter?
pthread_mutex_unlock(&mutex2);
}
I was asked a question a long time ago in an interview regarding this situation, and I came out feeling that the answer was yes - the order of those two unlocks does matter. I cannot for the life of me figure out how a deadlock could result from this though if the locks are always obtained in the same order wherever both are used.
The order shouldn't matter, as long as you don't attempt to acquire
another lock between the releases. The important thing is to always
acquire the locks in the same order; otherwise, you risk a deadlock.
EDIT:
To expand on the constraint: You must establish a strict ordering among
the mutexes, e.g. mutex1 precedes mutex2 (but this rule is valid for
any number of mutexes). You may only request a lock on a mutex if you
don't hold a mutex which comes after it in the order; e.g. you may not
request a lock on mutex1 if you hold a lock on mutex2. Anytime
these rules are respected, you should be safe. With regards to
releasing, if you release mutex1, then try to reacquire it before
releasing mutex2, you've violated the rule. In this regard, there may
be some advantage in respecting a stack-like order: last acquired is
always the first released. But it's sort of an indirect effect: the
rule is that you cannot request a lock on mutex1 if you hold one on
mutex2. Regardless of whether you had a lock on mutex1 when you
acquired the lock on mutex2 or not.
I cannot for the life of me figure out how a deadlock could result from this though if the locks are always obtained in the same order wherever both are used.
In these circumstances, I don't think the order of unlocking the mutexes could be the cause of a deadlock.
Since pthread_mutex_unlock() doesn't block, both mutexes would always get unlocked regardless of the order of the two calls.
Note that if you attempt to acquire any locks between the two unlock calls, this can change the picture completely.
It doesn't matter for correctness of locking. The reason is that, even supposing some other thread is waiting to lock mutex1 and then mutex2, the worst case is that it gets immediately scheduled as soon as you release mutex1 (and acquires mutex1). It then blocks waiting for mutex2, which the thread you're asking about will release as soon as it gets scheduled again, and there's no reason that shouldn't happen soon (immediately, if these are the only two threads in play).
So there might be a small cost in performance in that exact situation, compared with if you released mutex2 first and so there was only one rescheduling operation. Nothing you'd normally expect to predict or worry about, though, it's all within the boundaries of "scheduling often isn't deterministic".
The order you release the locks could certainly affect scheduling in general, though. Suppose that there are two threads waiting for your thread, and one of them is blocked on mutex1 while the other is blocked on mutex2. It might turn out that whichever lock you release first, that thread gets to run first, simply because your thread has outlived its welcome (consumed more than an entire time-slice), and hence gets descheduled as soon as anything else is runnable. But that can't cause a fault in an otherwise-correct program: you aren't allowed to rely on your thread being descheduled as soon as it releases the first lock. So either order of those two waiting threads running, both running simultaneously if you have multiple cores, or the two alternating on one core, must all be equally safe whichever order you release the locks.
The order of unlocking cannot cause deadlocks. However, if give the opportunity, I recommend unlocking them in reverse locking order. This has negligible effect on the running of the code. However, developers are used to thinking in terms of scopes, and scopes "close" in reverse order. Seeing them in reverse order to simply think of scoping locks. This brings me to the second point, which is that, in most cases, it is safest to replace the direct calls to lock and unlock with a stack based guard that calls them for you. Doing so yields the greatest degree of correctness for the least mental effort, and also happens to be safe in the presence of exceptions (which can really muck with a manual unlock)!
A simple guard (there are many out there.. this just just a quick roll-your-own):
class StMutexLock
{
public:
StMutexLock(pthread_mutex_t* inMutex)
: mMutex(inMutex)
{
pthread_mutex_lock(mMutex);
}
~StMutexUnlock()
{
pthread_mutex_unlock(mMutex);
}
private:
pthread_mutex_t* mMutex;
}
{
StMutexLock lock2(&mutex2);
StMutexLock lock1(&mutex1);
int x = protected_var1 + protected_var2;
doProtectedVar1ThingThatCouldThrow(); // exceptions are no problem!
// no explicit unlock required. Destructors take care of everything
}
No, it doesn't matter. A deadlock cannot result from this; both unlock operators are guaranteed to succeed (bar heap corruption or similar problems).
The order of unlocking isn't a problem here, but the order of locking can be a problem.
Consider:
void foo()
{
pthread_mutex_lock(&mutex1);
pthread_mutex_lock(&mutex2);
int x = protected_var1 + protected_var2;
pthread_mutex_unlock(&mutex1);
pthread_mutex_unlock(&mutex2);
}
void bar()
{
pthread_mutex_lock(&mutex2);
pthread_mutex_lock(&mutex1);
int x = protected_var1 + protected_var2;
pthread_mutex_unlock(&mutex1);
pthread_mutex_unlock(&mutex2);
}
This can lead to a deadlock since foo might have locked mutex1 and now waits for mutex2 while bar has locked mutex2 and now waits for mutex1. So it's a good idea to somehow ensure that nested mutex locks always get locked in the same order.
What I can see is that if another operation is taking mutex2 and keeps it for a long time, your foo() function will be stuck after pthread_mutex_lock(&mutex1); and will probably have some performance hit.
So long as whenever var1 and var2 are locked they are locked in the same order you are safe regardless of the freeing order. In fact freeing in the order they were locked is the RAII freeing behavior found in STL and BOOST locks.
void * threadHandle (void *arg)
{
// Try to lock the first mutex...
pthread_mutex_lock (&mutex_1);
// Try to lock the second mutex...
while (pthread_mutex_trylock(&mutex_2) != 0) // Test if already locked
{
// Second mutex is locked by some other thread. Unlock the first mutex so that other threads won't starve or deadlock
pthread_mutex_unlock (&mutex_1);
// stall here
usleep (100);
// Try to lock the first mutex again
pthread_mutex_lock(&mutex_1);
}
// If you are here, that means both mutexes are locked by this thread
// Modify the global data
count++;
// Unlock both mutexes!!!
pthread_mutex_unlock (&mutex_1);
pthread_mutex_unlock (&mutex_2);
}
I guess this can prevent the deadlock