the situation is:
a thread acquires an upgradable ownership of a boost::shared_mutex and is calling unlock_upgrade_and_lock(), which blocks because the other threads are possessing shared ownership of the same shared_mutex at the moment.
Will upgradable ownership of the first thread prevent(block) other threads when they are trying to "lock_shared" the shared_mutex so that all those already sharing ownership will finally unlock_shared and exclusive ownership for the first thread be guaranteed?
Or there is a possibility for the first thread to stay blocked for as long as there is another thread sharing the mutex?
(Assuming the Boost implementation vaguely models the WG21 proposal from Howard Hinnant ...)
Converting from shared ownership to upgrade ownership prevents any new threads acquiring the lock, so eventually all the shared owners will release it and the thread with upgrade ownership can convert it to exclusive ownership. This is the point of the "upgrade lock" as opposed to just shared and exclusive locks, see the explanation in N3427:
Note that an alternative design of try-converting from shared to exclusive, instead of from shared to upgrade as shown, would be susceptible to update (writer) starvation. This is because as long as there are multiple searchers (shared locks), none of the searchers can ever successfully try-convert to updaters. It is only by successfully registering yourself as the single thread having upgrade ownership and then blocking on a conversion from upgrade to exclusive, do you enable the implementation to start blocking new searchers from obtaining a shared lock so that you can eventually obtain the exclusive lock as existing searchers are cleared out.
Related
How is C++ mutex implemented under the hood ? Does it only use Decker's, Peterson's or other algorithms to enforce mutual exclusion or does it also use hardware support such as interrupt ad compare-and-swap (CAS).
Is it possible to implement an entire multi-threading library consisting of mutex and condition variable without any hardware support ?
Standard requires that there are atomic operations. Not necessarily CAS, but at least exchange. std::atomic_flag is required to be true atomic, although CAS is superfluous for it, simple exchange is satisfactory.
Note that any algorithm such as Decker's, Peterson's or other would at least require atomic load and atomic store, and they will not work if load and store are non-atomic. Also non-atomic types do not enforce memory ordering, which is implied by those algorithms.
It is not really required that std::mutex will be based on operating system calls, and that there are operating system calls at all.
In theory, std::mutex could be spinning only mutex.
It could also be blocking only mutex.
In practice a good implementation would first try to handle blocking with atomic, but when it is resorted to wait, it would do OS wait.
It asks the operating system to do it.
On Linux that'll probably be using pthreads (How does pthread implemented in linux kernel 3.2?).
On Windows it's with the Windows API. You can try asking Microsoft about that one.
The standard library implementation won't be making direct hardware access. This is what operating systems are for.
On Linux, when the mutex is uncontended, the lock and unlock happen in user-space only using atomic instructions, such as compare-and-swap. In a contended case a call into the kernel is required to wait on the mutex till it's been unlocked (on lock) or to wake up waiters (on unlock). Locking user-space mutexes does not disable interrupts.
Here is the source code for low-level mutex primitives in glibc, the comments are instructive:
/* Low-level locks use a combination of atomic operations (to acquire and
release lock ownership) and futex operations (to block until the state
of a lock changes). A lock can be in one of three states:
0: not acquired,
1: acquired with no waiters; no other threads are blocked or about to block
for changes to the lock state,
>1: acquired, possibly with waiters; there may be other threads blocked or
about to block for changes to the lock state.
We expect that the common case is an uncontended lock, so we just need
to transition the lock between states 0 and 1; releasing the lock does
not need to wake any other blocked threads. If the lock is contended
and a thread decides to block using a futex operation, then this thread
needs to first change the state to >1; if this state is observed during
lock release, the releasing thread will wake one of the potentially
blocked threads.
Much of this code takes a 'private' parameter. This may be:
LLL_PRIVATE: lock only shared within a process
LLL_SHARED: lock may be shared across processes.
Condition variables contain an optimization for broadcasts that requeues
waiting threads on a lock's futex. Therefore, there is a special
variant of the locks (whose name contains "cond") that makes sure to
always set the lock state to >1 and not just 1.
Robust locks set the lock to the id of the owner. This allows detection
of the case where the owner exits without releasing the lock. Flags are
OR'd with the owner id to record additional information about lock state.
Therefore the states of robust locks are:
0: not acquired
id: acquired (by user identified by id & FUTEX_TID_MASK)
The following flags may be set in the robust lock value:
FUTEX_WAITERS - possibly has waiters
FUTEX_OWNER_DIED - owning user has exited without releasing the futex. */
And pthread_mutex_lock code.
Mutex implementation requires multiple threads to agree on a value of a shared variable - the mutex, whether it is locked or unlocked. This is also known as consensus problem and it is not possible to solve it using only atomic loads and stores, a compare-and-swap is required.
A coworker had an issue recently that boiled down to what we believe was the following sequence of events in a C++ application with two threads:
Thread A holds a mutex.
While thread A is holding the mutex, thread B attempts to lock it. Since it is held, thread B is suspended.
Thread A finishes the work that it was holding the mutex for, thus releasing the mutex.
Very shortly thereafter, thread A needs to touch a resource that is protected by the mutex, so it locks it again.
It appears that thread A is given the mutex again; thread B is still waiting, even though it "asked" for the lock first.
Does this sequence of events fit with the semantics of, say, C++11's std::mutex and/or pthreads? I can honestly say I've never thought about this aspect of mutexes before.
Are there any fairness guarantees to prevent starvation of other threads for too long, or any way to get such guarantees?
Known problem. C++ mutexes are thin layer on top of OS-provided mutexes, and OS-provided mutexes are often not fair. They do not care for FIFO.
The other side of the same coin is that threads are usually not pre-empted until they run out of their time slice. As a result, thread A in this scenario was likely to continue to be executed, and got the mutex right away because of that.
The guarantee of a std::mutex is enable exclusive access to shared resources. Its sole purpose is to eliminate the race condition when multiple threads attempt to access shared resources.
The implementer of a mutex may choose to favor the current thread acquiring a mutex (over another thread) for performance reasons. Allowing the current thread to acquire the mutex and make forward progress without requiring a context switch is often a preferred implementation choice supported by profiling/measurements.
Alternatively, the mutex could be constructed to prefer another (blocked) thread for acquisition (perhaps chosen according FIFO). This likely requires a thread context switch (on the same or other processor core) increasing latency/overhead. NOTE: FIFO mutexes can behave in surprising ways. E.g. Thread priorities must be considered in FIFO support - so acquisition won't be strictly FIFO unless all competing threads are the same priority.
Adding a FIFO requirement to a mutex's definition constrains implementers to provide suboptimal performance in nominal workloads. (see above)
Protecting a queue of callable objects (std::function) with a mutex would enable sequenced execution. Multiple threads can acquire the mutex, enqueue a callable object, and release the mutex. The callable objects can be executed by a single thread (or a pool of threads if synchrony is not required).
•Thread A finishes the work that it was holding the mutex for, thus
releasing the mutex.
•Very shortly thereafter, thread A needs to touch a resource that is
protected by the mutex, so it locks it again
In real world, when the program is running. there is no guarantee provided by any threading library or the OS. Here "shortly thereafter" may mean a lot to the OS and the hardware. If you say, 2 minutes, then thread B would definitely get it. If you say 200 ms or low, there is no promise of A or B getting it.
Number of cores, load on different processors/cores/threading units, contention, thread switching, kernel/user switches, pre-emption, priorities, deadlock detection schemes et. al. will make a lot of difference. Just by looking at green signal from far you cannot guarantee that you will get it green.
If you want that thread B must get the resource, you may use IPC mechanism to instruct the thread B to gain the resource.
You are inadvertently suggesting that threads should synchronise access to the synchronisation primitive. Mutexes are, as the name suggests, about Mutual Exclusion. They are not designed for control flow. If you want to signal a thread to run from another thread you need to use a synchronisation primitive designed for control flow i.e. a signal.
You can use a fair mutex to solve your task, i.e. a mutex that will guarantee the FIFO order of your operations. Unfortunately, C++ standard library doesn't have a fair mutex.
Thankfully, there are open-source implementations, for example yamc (a header-only library).
The logic here is very simple - the thread is not preempted based on mutexes, because that would require a cost incurred for each mutex operation, which is definitely not what you want. The cost of grabbing a mutex is high enough without forcing the scheduler to look for other threads to run.
If you want to fix this you can always yield the current thread. You can use std::this_thread::yield() - http://en.cppreference.com/w/cpp/thread/yield - and that might offer the chance to thread B to take over the mutex. But before you do that, allow me to tell you that this is a very fragile way of doing things, and offers no guarantee. You could, alternatively, investigate the issue deeper:
Why is it a problem that the B thread is not started when A releases the resource? Your code should not depend on such logic.
Consider using alternative thread synchronization objects like barriers (boost::barrier or http://linux.die.net/man/3/pthread_barrier_wait ) instead, if you really need this sort of logic.
Investigate if you really need to release the mutex from A at that point - I find the practice of locking and releasing fast a mutex for more than one time a code smell, it usually impacts terribly the performace. See if you can group extraction of data in immutable structures which you can play around with.
Ambitious, but try to work without mutexes - use instead lock-free structures and a more functional approach, including using a lot of immutable structures. I often found quite a performance gain from updating my code to not use mutexes (and still work correctly from the mt point of view)
How do you know this:
While thread A is holding the mutex, thread B attempts to lock it.
Since it is held, thread B is suspended.
How do you know thread B is suspended. How do you know that it is not just finished the line of code before trying to grab the lock, but not yet grabbed the lock:
Thread B:
x = 17; // is the thread here?
// or here? ('between' lines of code)
mtx.lock(); // or suspended in here?
// how can you tell?
You can't tell. At least not in theory.
Thus the order of acquiring the lock is, to the abstract machine (ie the language), not definable.
Is it possible to transfer the ownership of a mutex, critical section etc. from the first thread to the second thread without letting any third thread to grab ownership in the meantime?
The documentation for critical section prohibits to call LeaveCriticalSection in a thread other than the one who called EnterCriticalSection:
If a thread calls LeaveCriticalSection when it does not have ownership of the specified critical section object, an error occurs that may cause another thread using EnterCriticalSection to wait indefinitely.
But in my scenario I need to exactly release the synchronization object in a different thread than the one who acquired it, so that no other (third) thread can acquire the ownership of the synchronization object in the mean time (from the moment of acquisition by the first thread till the moment of release by the second thread). A solution in C++ threading or WinAPI calls would be suitable for my needs.
No, thread owenership transfer is not supported. Moreover, I firmly believe that any design which depends on lock ownership is flawed and will cause troubles down the road.
However, if ownership transfer is required only for releasing a lock from a thread different from the one receiving it, you have two options:
Use semaphores. By design, they are allowed to be released from any thread
Enagage in undefined behavior and release a mutex from the thread which doesn't own it. You need to understand consequences of this.
Undefined behaviour
Releasing a mutex from the thread which doesn't own it is undefined. However, it is possible to code your application with those. The problem you will have is that data synchronization might not happen on non-cache coherent chips if the thread which locked the mutex was running on one CPU, and unlocking happens on another. As a result, the data which was not protected by another synchronization will be 'dirty'. However, if there is no such data - for instance, mutex is trully used in a 'semaphore' fasion and does not protect any data - this might be acceptable.
I'm studying manual on boost::thread and they say that effect of calling lock_upgrade() of shared_mutex is
"The current thread blocks until upgrade ownership can be obtained for the current thread.".
But why should it block, as upgrade ownership it's not yet an exclusive ownership?
Thank you.
But why should it block, as upgrade ownership it's not yet an exclusive ownership?
If someone already has exclusive ownership, then no-one can obtain shared or upgradable ownership; so lock_shared and lock_upgrade will both block in that situation.
And, as noted in the comments, only one thread can have upgradable ownership; so lock_upgrade will also block if someone already has that.
I am new to threading concept in C++ . I Just wanted to know few things: How is a boost::unique_lock different from boost::upgrade_lock?
How actually an exclusive ownership differ from upgrade ownership.
Maybe one can say exclusive ownership is thread safe but not upgrade ownership,
in that case i would like to know how can an upgrade ownership can be harmful if
it can be? I want to know what is that upgrade_lock allows or not allows that
unique_lock does except exclusive lock thing. Not providing exclusive lock by
upgrade_lock makes it similar to shared_lock or what and if so then how is it
different from shared_lock?
The difference between upgrade_lock and unique_lock is simple. An instance of unique_lock is acquiring a full exclusive ownership of a shared_mutex. This means that no one else can get any type of ownership while the unique_lock is alive.
Unlike the unique_lock an instance of upgrade_lock is acquiring an upgrade ownership that exclusive only amongst threads trying to get the same upgrade ownership. All other threads that try to get a shared ownership could acquire it with no conflict until the upgrade_lock is upgraded to unique (with an instance of upgrade_to_unique_lock).
The upgrade_lock is useful when some of threads can be readers only and will not try to promote itself to writers. Otherwise (all readers may try to become writers at some point) upgrade_lock will operate as unique_lock.
#Xeo yes upgrade_lock can be upgraded to uniue_lock by
upgrade_yo_unique_lock . so why do we need shared_lock is that because
shared_lock cant be upgrade to unique_lock ? Is that the only thing ?
Yes.
shared_mutex exists only to supply the functionality of traditional read/write mutexes. Some clients will be understandably leery of a mutex changing lock-modes from one to another, and yet still need the traditional functionality of a rw/mutex. This is the role of shared_mutex.
Other clients will look at shared_mutex and say: I want more. I want to sometimes atomically upgrade my ownership from shared to exclusive. And this is the role of upgrade_mutex. upgrade_mutex is not better than shared_mutex, nor vice-versa. It is simply another tool in the toolbox.