Celeritas
Celeritas

Reputation: 15091

What's the point of locking and unlocking a mutex if pthread_cond_wait does that itself?

I'm trying to learn the difference between mutexes and conditional variables and am confused by the following code.

// Lock mutex and then wait for signal to relase mutex
pthread_mutex_lock( &count_mutex );

/*Wait while functionCount2() operates on count
mutex unlocked if condition varialbe in functionCount2() signaled. <-- so then why call pthread_mutex_unlock() 3 lines latter if it's already unlocked?*/
pthread_cond_wait( &condition_var, &count_mutex );
count++;
printf("Counter value functionCount1: %d\n",count);

pthread_mutex_unlock( &count_mutex );

According to the docs pthread_cond_wait() "atomically release mutex and cause the calling thread to block on the condition variable" so what's the point of pthread_mutex_lock( &count_mutex ); if pthread_cond_wait() just unlocks it and what's the point to the latter call pthread_mutex_unlock() since pthread_cond_wait() had already unlocked it?

To me it would make sense if pthread_cond_wait() did not take a mutex, and I thought that was its point.

Upvotes: 2

Views: 2730

Answers (2)

MichaelGoren
MichaelGoren

Reputation: 1001

Lets have a look at the following two functions, waiting and notifying. Both run in two separate threads and use globally defined variables value, mutex and cond. Let's assume that the waiting thread starts first and locks the mutex. Now the condition "value is zero" can be tested safely, because the lock guarantees no other thread can change the value. In the next step, our waiting thread is waiting on the conditional variable for a signal from the notifying function that the value has been changed and it should be checked again.

void* waiting(void* arg)
{
  pthread_mutex_lock(&mutex);
  while(value == 0)
  {
    pthread_cond_wait(&cond, &mutex);
  }

  printf("waiter %d releases\n", *tid);
  pthread_mutex_unlock(&mutex);

}

Now lets see what is going with the notifying function. Do you remember that we have locked the mutex in the waiting thread? Without releasing it, our notifying function never can obtain the mutex and change the value. That is the reason why the pthread_cond_wait releases it. No, the function does not release it finally, only for the period when the function is blocked. Now our notifying thread can change the value and send a signal to the pthread_cond_wait, that something has happened. When the pthread_cond_wait unblocks (returns), the mutex is locked again!

void* notifying(void* arg)
{
  pthread_mutex_lock(&mutex);
  value = 1;
  pthread_cond_signal(&cond);
  pthread_mutex_unlock(&mutex);
}

It is interesting, that it is common to wait on a condition variable in a loop, checking the condition again and again. Why? Sometimes pthread_cond_wait unblocks without getting signals from the notifying function (so called spurious signals).

Upvotes: 0

Michael Burr
Michael Burr

Reputation: 340426

First let's define some terms that might be a bit confusing:

  • condition: this is the expression that a thread uses to determine whether or not something has happened or that work needs to be done. For example a condition might be that a queue is not empty.

  • condition variable: this is a synchronization object that the pthread library (or something similar) makes available to let a thread wait for a condition to change. A thread can 'wait' on a condition variable, then when another thread signals that condition variable, the waiting thread will wake up.

Note that a "condition" is a different thing than a "condition variable".

When using condition variables, a thread needs to check the condition (whatever that is), then if the condition isn't met, it can wait on the condition variable until something signals that it should check the condition again.

However, this sequence of events:

  1. check the condition
  2. wait on the condition variable

isn't atomic by itself - if between step 1 and 2 another thread signals the condition variable, then the waiting thread may never wake up (condition variables don't remember that a signal has occurred in the past; when signaled they will only unblock a thread that is already waiting). To avoid this problem, condition variables must be used with a pattern that ensures that the two steps will happen atomically with respect to other threads dealing with the condition and condition variable. That pattern is:

  • any thread reading or updating the objects used in the condition must hold a mutex while doing so. Note that this includes the thread that may wait on the condition variable.
  • when waiting on a condition variable, the thread that checked the condition while holding the mutex must continue holding the mutex for the pthread_cond_wait() call. This ensures that the race condition between steps 1 and 2 above cannot occur, because anything that updates the condition will not be able to acquire the mutex until the pthread_cond_wait() call has prepared the thread to wait. At that point the pthread_cond_wait() function will release the mutex, which will allow the updating thread to acquire the mutex and update the condition.

Upvotes: 4

Related Questions