Your implementation does not prevent the possibility of a false TIMEOUT when transmitting a stream. You immediately decrease awakenings on successful cond_wait, and you decrease awakenings on failed cond_wait if it looks like a signal intended for you (awakening has a larger number). However, the math you use to provide the signal is designed to prevent someone from actually doing this.
The problem is when you open all the mutexes after waiting
if (ret == 0) wakeups--; pthread_mutex_unlock(&mtx); // no locks held. If interrupted, ANYTHING can happen userMutex->lock(); pthread_mutex_lock(&mtx);
Now, to determine success and failure, I have to declare that your cond_wait spans from the initial pthread_mutex_lock to the final pthread_mutex_unlock . To state that you have no race, when the signal may look like a timeout, it should be like this. If you manage to prevent a strong wait time for pthread_cond_wait, just to enter another delayed timeout, the problem is not solved.
So, all that needs to be proven is the case where a stream is transmitted during operation, but wake-up checks are not performed. It turned out that the easiest way to do this is to trick the awakenings to be -1 if one thread stole another awakening. 3 threads will wait and one will signal twice. The trick to use is the abuse of min () in Wake. He also relies on a race event between two cond_waits that end immediately. One of them should get mtx , and this is undefined, which will succeed. In this case, I assume the worst (as you can always with race evidence)
initial state { waiters = 0 wakeups = 0 } Thread 1 Thread 2 Thread 3 Thread 4 1: {acquire userMutex} 1: wait(...) { 1: {acquire mtx} 1: {release userMutex} 1: waiters++; // = 1 1: oldWakeups = wakeups; // 0 1: pthread_cond_wait // releases mtx 1: ptrheads TIMES OUT // acquires mtx 1: sees timeout 1: {release mtx} 1: // world worst context switch occurs here 2: {acquire userMutex} 2: wait(...) { 2: {acquire mtx} 2: {release userMutex} 2: waiters++; // = 2 2: oldWakeups = wakeups; // = 0 2: pthread_cond_wait // releases mtx 3: {acquires userMutex} 3: wait(...) { 3: {acquire mtx} 3: {release userMutex} 3: waiters++; // = 3 3: oldWakeups = wakeups; // = 0 3: pthread_cond_wait // releases mtx 4: {acquire userMtx} 4: wake() { 4: {acquire mtx} 4: wakeups = min(wakeups + 1, waiters); 4: // = min(0 + 1, 3) = 1 4: pthread_cond_signal 4: {release mtx} 4: } 4: {release userMtx} RACE: 2: TIMEOUT 3: SIGNALED RACE: both of these threads need to acquire mtx 2: {acquires mtx} 2: sees that it times out 2: if (timeout && (wakeups > oldWakeups)) { // (1 > 0) 2: // thinks the wakeup was for this thread 2: waiters--; // = 2 2: wakeups--; // = 0 2: } 2: {releases mtx} 2: returns SIGNALED; 2: } 2: {releases userMtx} 3: {acquires mtx} 3: sees that it was signaled 3: wakeups--; // = -1 ... UH O! 3: waiters--; // = 1 3: {releases mtx} 3: returns SIGNALED; 3: } 3: {releases userMtx} --- some synchronization which makes it clear that both thread 2 --- --- and thread 3 were signaled occurs here. Thread 1 is still --- --- technically waiting in limbo. User decides to wake it up. --- 4: {acquire userMtx} 4: wake() { 4: {acquire mtx} 4: wakeups = min(wakeups + 1, waiters); 4: // = min(-1 + 1, 1) = 0 !!! 4: pthread_cond_signal 4: {release mtx} 4: } 4: {release userMtx} 1: {acquire userMtx} 1: {acquire mtx} 1: waiters--; // = 0 1: if (timeout && (wakeups > oldWakeups)) {..} (0 > 0) 1: // no signal detected 1: {release mtx} 1: return TIMEOUT; 1: } 1: {release userMtx}
Thanks to a fun racing game that allows you to get awakenings up to -1, the trick to avoid missing signals does not work. pthreads_cond_signal allowed to wake up multiple threads, so waking threads 2 and 3 at the same time is legal. However, the second signal obviously has only one stream for the signal, so stream1 must be signaled. However, we returned TIMEOUT, losing to the infamous inevitable racial affair.
As far as I can tell, the more difficult you are trying to block these awakenings in the correct flow, the more ways to reset all mutexes, and not technically wait for any conditional variable, the more deadly.