Condition variable and shared mutex - c++

I wonder why I can't do this in C++14 (or 17)
std::shared_timed_mutex mutex;
std::unique_lock<std::shared_timed_mutex> lock(mutex);
std::condition_variable var;
while(!some_condition)
var.wait(lock);
Condition variables only seem to work with std::mutex. But why?

This is defined by the standard to allow for maximum efficiency of the implementation. If you want to use a different lock with condition_variable you need to use condition_variable_any. Note that the condition_variable_any implementation has some overhead though.
Quote from the standard: 30.5 Condition variables
Class condition_variable provides a condition variable that can only
wait on an object of type unique_lock<mutex>, allowing maximum
effciency on some platforms.

Related

Why is lock_guard a template?

I just learned about std::lock_guard and I was wondering why it is a template.
Until now I have only seen std::lock_guard<std::mutex> with std::mutex inside the angle brackets.
Using std::lock_guard<std::mutex> is indeed quite common.
But you can use std::lock_guard with other mutex types:
Various standard mutex types, e.g.: std::recursive_mutex.
Your own mutex type. You can use any type, as long as it is a BasicLockable, i.e. it supports the required methods: lock(), unlock().

Any disadvantages for std::atomic_flag not providing load or store operations? (Spin-lock example)

Comparing a std::atomic_flag to an std::atomic_bool (aka std::atomic<bool>), it seems to me that a std::atomic_flag just has a simpler interface. It provides only testing+setting and clearing the flag while an std::atomic_bool also provides overloads to several operators.
One question of mine is about terminology: What is meant by "load or store operations"? Does it mean that it is not possible to arbitrarily read and modify a std::atomic_flag's value?
Furthermore, I am wondering, could a std::atomic_bool be faster when being used for a spin-lock? It seems to me that an std::atomic_flag always must read AND write during a spin-lock:
while (my_atomic_flag.test_and_set()); // spin-lock
while an std::atomic_bool would only have to perform a read operation (assuming that the atomic bool is implemented lock-free):
while (my_atomic_bool); // spin-lock
Is an std::atomic_flag strictly more efficient than an std::atomic_bool or could it also be the other way round? What should be used for a spin-lock?
What is meant by "load or store operations"? Does it mean that it is not possible to arbitrarily read and modify a std::atomic_flag's value?
The normal store/load operations are not supported on a std::atomic_flag.
It is a modify-only type; ie. you cannot read-access a std::atomic_flag object without performing a modifying operation.
In general, std::atomic_flag is meant as a building block for other operations. It's interface is deliberately simple; it is the only atomic type that has guaranteed atomic lock-free operations.
The operations it supports are:
std::atomic_flag::clear()
std::atomic_flag::test_and_set()
With that, you can easily build your own spinlock (although generally not recommended):
class my_spinlock {
std::atomic_flag flag = ATOMIC_FLAG_INIT;
public:
void lock()
{
while(flag.test_and_set());
}
void unlock()
{
flag.clear();
}
};
Furthermore, I am wondering, could a std::atomic_bool be faster when being used for a spin-lock? It seems to me that an std::atomic_flag always must read AND write during a spin-lock
Well, the thing is, a spinlock always has to modify its state when acquiring the lock. You simply cannot take a lock without telling others.
The implementation for lock() based on a std::atomic<bool> looks very similar:
while (flag.exchange(true));
Whether a spinlock based on std::atomic_flag is faster ?
On my platform, the compiler emits the same assembly for both types so I would be very surprised.

Does Making std::mutex as static creates race-condition for the mutex itself

It may sound dummy but,Am sort of confused, I have gone through this question,when looking into it we both where in the same situation it seems, I have to make my map as static so it will be common to all instances that will be created in separate threads and I want to synchronize the functions that gonna act on my map, so i thought of making a std::mutex as static in my class like what was suggested as an answer in the given link.. in this case will there be any race-condition occur for acquiring and locking the mutex itself? is there any better way we can synchronize the functions on static map using mutex
Does Making std::mutex as static creates race-condition for the mutex
itself
No, a Mutex isn't vulnerable to race-conditions. And as for initializing it as static, you are safe.
$6.7: 4: Dynamic initialization of a block-scope variable with static storage duration ([basic.stc.static]) or thread storage
duration ([basic.stc.thread]) is performed the first time control
passes through its declaration; such a variable is considered
initialized upon the completion of its initialization. If the
initialization exits by throwing an exception, the initialization is
not complete, so it will be tried again the next time control enters
the declaration. If control enters the declaration concurrently while
the variable is being initialized, the concurrent execution shall wait
for completion of the initialization
You said:
i thought of making a std::mutex as static in my class like what was
suggested as an answer in the given link.
Do that if you are trying to protect static class member variables as well. Otherwise, make it a mutable member. The fact that you said the map will be globally initialized as static is okay, since the mutex as a member variable, will follow suite.
class Map{
public:
Map(...){}
std::size_t size() const{
std::lock_guard<std::mutex> lck(m_m);
return m_size;
}
iterator add(....) {
std::lock_guard<std::mutex> lck(m_m);
....
return your_iterator;
}
...etc
private:
mutable std::mutex m_m; //FREE ADVICE: Use a std::recursive_mutex instead
...others
};
Now:
//Somewhere at global scope:
Map mp(... ...);
// NOTES
// 1. `mp` will be initialized in a thread safe way by the runtime.
// 2. Since you've protected all Read or Write member functions of the class `Map`,
// you are safe to call it from any function and from any thread
No.
Mutexes (and other synchronisation primitives) are implemented using support from the operating system. That's the only way that they can do their job.
A direct corollorary of their ability to perform this job is that they are themselves not prone to race conditions — locking and unlocking operations on mutexes are atomic.
Otherwise, they wouldn't be much use! Every time you used a mutex, you'd have to protect it with another mutex, then protect that mutex with another mutex, and so on and so forth until you had an infinite number of mutexes, none of them actually achieving anything of any use at all. :)
The std::mutex object having static storage duration doesn't change this in any way. Presumably you were thinking of function-static variables (that, assuming they're not already immune to race conditions, must be synchronised because they may be accessed concurrently by different threads; but still, ideally you wouldn't use them at all because they make functions not be re-entrant).

Event notification without mutex

C++11 has the std::condition_variable, its wait function is
template< class Predicate >
void wait( std::unique_lock<std::mutex>& lock, Predicate pred );
It requires a mutex.
As far as I understand - its notify_one can be called without synchronization (I know the idiomatic way is to use it with a mutex).
I have an object which is already internally synchronized - so I don't need a mutex to protect it. One thread should wait for some event associated with that object, and others would be notified.
How to do such notification without a mutex in C++11? I.e. it is easy to do with a condition_variable, but it needs a mutex. I thought about using a fake mutex type, but std::mutex is nailed in the wait interface.
An option is to poll a std::atomic_flag + sleep, but I don't like sleeping.
Use std::condition_variable_any you can use any class with it which implements the BasicLockable Concept.
Given a bad feeling about this I checked the implementation of std::condition_variable_any of libc++. It turns out that it uses a plain std::condition_variable together with a std::shared_ptr to a std::mutex, so there is definitely some overhead involved without digging any deeper. (There is some other post here on SO which covers this, though I first have to search that)
As a matter of that I would probably recommend to redesign your case so that synchronization is really only done by a mutex protecting a plain condition variable.
In some threading models (although I doubt in modern ones) the mutex is needed to protect the condition variable itself (not the object you're synchronizing) from concurrent access. If the condition variable wasn't protected by a mutex you could encounter problems on the condition itself.
See Why do pthreads’ condition variable functions require a mutex?
I have some object, which already internally synchronized - I don't need mutex to protect it. One thread should wait for some event associated with that object, and others would notify.
If you don't hold the mutex the waiting thread is going to miss notifications, regardless whether you use condition_variable or condition_variable_any with the internal mutex.
You need to associate at least one bit of extra information with the condition variable, and this bit should be protected by a mutex.

Can you combine std::recursive_mutex with std::condition_variable?

Can you combine std::recursive_mutex with std::condition_variable, meaning do something like this:
std::unique_lock<std::recursive_mutex> lock(some_recursive_mutex)
some_condition_var.wait(lock);
If it's not allowed, then why not?
I am using VC++11.
You can, if you use std::condition_variable_any, which allows for any type of object that supports the Lockable concept.
However, in the case of recursive mutex, you do have to ensure that the given thread has only locked the recursive mutex once, since the condition variable only will use the unlock method on the unique_lock once during the wait.
You can do that with a std::condition_variable_any which can take any kind of lockable but plain std::condition_variable is specialized for std::unique_lock<std::mutex>.