My problem is illustrated below. Notification on std::condition_variable
can be missed by Thread II; Thread III can acquire lock before it and change the condition.
/*
Thread I Thread II Thread III
_____________________________________________________________
| lock M | wait for notify | wait for M | |
| cond = stateA | | | |
| notify | unblock | | |
| unlock M | wait for M | lock M | |
| | | cond = stateB | |
| | lock M | unblock M | |
| | check if cond == stateA | | |
| | ... | | \ / t
*
*/
#include <iostream>
#include <condition_variable>
#include <chrono>
#include <thread>
#include <limits>
#include <mutex>
int main()
{
using namespace std::chrono ;
std::mutex mtx ;
std::condition_variable cv ;
enum EState
{
A , B
} state = B ; // mtx
// possible workaround
using count_t = unsigned long long ;
count_t set_A_state_count = 0 ; // mtx
// 18,446,744,073,709,551,615 - number, that may cause missing ;
// ( if Thread III function would get executed exactly this number of times
// before Thread II acquire the mutex )
// believe it is not relevant for present days.
auto ThreadI = [ &set_A_state_count , &cv ,
&mtx , &state ] ()
{
std::lock_guard< std::mutex > lock { mtx } ;
state = A ;
++ set_A_state_count ;
cv.notify_one() ;
} ;
auto ThreadIII = [ &cv , &mtx , &state ] ()
{
std::lock_guard< std::mutex > lock { mtx } ;
state = B ;
} ;
std::unique_lock< std::mutex > lock { mtx } ;
std::thread thI ( ThreadI ) , thIII ( ThreadIII ) ;
const auto saved_count = set_A_state_count ;
if ( state != A ) {
while( saved_count == set_A_state_count ) { // pred ()
// releasing and waiting
cv.wait( lock ) ;
// acquiring - place where ThreadIII can outrun main thread ( ThreadII on the inlustration )
}
}
count_t times = ( saved_count < set_A_state_count ) ?
set_A_state_count - saved_count
: std::numeric_limits< count_t >::max() -
set_A_state_count + saved_count ;
std::cout << "state was changed to A " << times << " times." << std::flush ;
thI.join() ;
thIII.join() ;
return 0;
}
Is there any way to deal with this.?
(Application). Consider something like 'alarm' class with 'wait( state )', 'start' and 'cancel' methods. It has associated thread, that is a "waiter" thread. All of the methods can be called on single object. While cancel and start can be synced with additional mutex, it cannot be done for wait for obvious reasons. It can be workarounded by simply storing state of some ulong counter before each wait and then comparing stored and current - if they differ ( incremented by start or cancel), then state was switched, notification occured.
Condition variable notifications can be missed if there is no thread waiting for it when a notification is emitted. The code must rather wait for the change of the state. Condition variable notifications are a hint that the state may have changed and must be re-evaluated.
You have 2 threads racing to change
state
toA
andB
correspondingly. There is no ordering constraint between them, so thatstate
can be eitherA
orB
. In the latter case the thread waiting forstate == A
blocks forever.