I'm probably missing something obvious, but I can't see any difference between std::condition_variable and std::condition_variable_any. Why do we need both?
std::condition_variable The condition_variable class is a synchronization primitive that can be used to block a thread, or multiple threads at the same time, until another thread both modifies a shared variable (the condition), and notifies the condition_variable .
@Prab, just to give the explanation: unique_lock() is automatically released when the destructor is called meaning it is exception safe and that it automatically unlocks when you leave scope.
condition_variable::wait wait causes the current thread to block until the condition variable is notified or a spurious wakeup occurs, optionally looping until some predicate is satisfied (bool(stop_waiting()) == true).
You need condition variables, to be used with a mutex (each cond. var. belongs to a mutex) to signal changing states (conditions) from one thread to another one. The idea is that a thread can wait till some condition becomes true.
The difference is the parameter to the wait() functions. All the wait functions in std::condition_variable take a lock parameter of type std::unique_lock<std::mutex>&, whereas the wait functions for std::condition_variable_any are all templates, and take a lock parameter of type Lockable&, where Lockable is a template parameter.
This means that std::condition_variable_any can work with user-defined mutex and lock types, and with things like boost::shared_lock --- anything that has lock() and unlock() member functions.
e.g.
std::condition_variable_any cond;
boost::shared_mutex m;
void foo() {
boost::shared_lock<boost::shared_mutex> lk(m);
while(!some_condition()) {
cond.wait(lk);
}
}
As of C++20, condition_variable_any also supports stop tokens for the new jthread class. This means that if you have a condition variable of this type, it will give up the mutex if a stop request is made, without you having to write extra polling code. This feature does not work on condition_variable for some technical reasons that were causing "races, deadlocks, and undefined behavior."
void testInterruptibleCVWait()
{
bool ready = false;
std::mutex readyMutex;
std::condition_variable_any readyCV;
std::jthread t([&ready, &readyMutex, &readyCV] (std::stop_token st)
{
while (...)
{
...
{
std::unique_lock lg{readyMutex};
readyCV.wait_until(lg, [&ready] {return ready; }, st);
// also ends wait on stop request for st
}
...
}
});
...
} // jthread destructor signals stop request and therefore unblocks the CV wait and ends the started thread
See the documentation for details:
std::condition_variable documentation
std::condition_variable_any documentation and specifically look at the wait, wait_for and wait_until member functions that now honor stop requests on jthreads.
or check out the latest jthread and stop token C++20 proposal revision
std::condition_variable is more specialized, and therefore can be more efficient when you don't need the flexibility of std::condition_variable_any.
From N3290 §30.5[thread.condition]/1
Class
condition_variableprovides a condition variable that can only wait on an object of typeunique_lock<mutex>, allowing maximum efficiency on some platforms. Classcondition_variable_anyprovides a general condition variable that can wait on objects of user-supplied lock types.
Actually, in LLVM's libc++, condition_variable_any is implemented using the more specialized condition_variable (which uses pthread_cond_t) on a shared_mutex.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With