KaiserJohaan
KaiserJohaan

Reputation: 9240

Two Condition Variables and avoiding deadlock

I have two condition variables:

CondVar1
CondVar2

Used in two threads like this (pseudo-code):

// thread1 starts in 'waiting' mode, and then Thread2 signals
void Thread1()
{
    CondVar1->Wait();
    CondVar2->Signal();
}

void Thread2()
{
    CondVar1->Signal();
    CondVar2->Wait();
}

Can this cause a deadlock? meaning, thread1 waits, thread2 signals, and then can thread1 signals before thread2 enters Wait(), meaning thread2 will never return?

Thanks

Upvotes: 3

Views: 856

Answers (2)

You don't usually just wait on a condition variable. The common use pattern is holding a lock, checking a variable that determines whether you can proceed or not and if you cannot wait in the condition:

// pseudocode
void push( T data ) {
   Guard<Mutex> lock( m_mutex );   // Hold a lock on the queue
   while (m_queue.full())     // [1]
      m_cond1.wait(lock);         // Wait until a consumer leaves a slot for me to write
   // insert data
   m_cond2.signal_one();      // Signal consumers that might be waiting on an empty queue
}

Some things to note: most libraries allow for spurious wakes in condition variables. While it is possible to implement a condition variable that avoid spurious wakes, the cost of the operations would be higher, so it is considered a lesser evil to require users to recheck the state before continuing (while loop in [1]).

Some libraries, notably C++11, allow you to pass a predicate, and will implement the loop internally: cond.wait(lock, [&queue](){ return !queue.full(); } );

Upvotes: 4

Tudor
Tudor

Reputation: 62439

There are two situations that could lead to a deadlock here:

  1. In normal execution, the one you described. It is possible that the variable is signaled before the thread reaches the call to Wait, so the signal is lost.
  2. A spurious wake-up could happen, causing the first thread to leave the call to Wait before actually being signaled, hence signaling Thread 2 who is not yet waiting.

You should design your code as follows when using signaling mechanisms:

bool thread1Waits = true;
bool thread2Waits = true;

void Thread1()
{
    while(thread1Waits) CondVar1->Wait();
    thread2Waits = false; 
    CondVar2->Signal();
}

void Thread2()
{
    thread1Waits = false;
    CondVar1->Signal();
    while(thread2Waits) CondVar2->Wait();
}

Of course, this assumes there are locks protecting the condition variables and that additionally thread 1 runs before thread 2.

Upvotes: 2

Related Questions