1

In the following link: Why do pthreads’ condition variable functions require a mutex?

@nos describes the potential loopholes in implementing pthread_cond_wait() without a mutex:

while(1) {
    pthread_cond_wait(&cond); //imagine cond_wait did not have a mutex
    pthread_mutex_lock(&mutex);
    char *data = some_data;
    some_data = NULL;
    pthread_mutex_unlock(&mutex);
    handle(data);
}

"Will not work, there's still a chance of a race condition in between waking up and grabbing the mutex."

I don't understand how the race condition between waking up and acquiring the mutex arises?

wilx
  • 17,697
  • 6
  • 59
  • 114
kharesp
  • 83
  • 7

1 Answers1

0

Indeed the example does not show the issue but as a general principle when there is two instructions there is place for a race condition.

Here is an example that hopefully will illustrate the issue.

Say you manage the mutex yourself and you have two threads, thread#1 and thread#2:

  • thread#1 needs to modify some shared state
  • it acquires the mutex and changes the state
  • it releases the mutex and waits for something to happen before proceeding

Here is some code :

    pthread_mutex_lock(&mutex);
    // change state
    pthread_mutex_unlock(&mutex);
    pthread_cond_wait(&cond);

There is more:

  • thread#2 waits on the mutex, changes the shared state and signals thread#1 using the condition variable to give thread#1 a chance to do something before proceeding

Here is the code :

    pthread_mutex_lock(&mutex);
    // change state
    pthread_mutex_unlock(&mutex);
    pthread_cond_signal(&cond);

Here is a nasty scenario :

  • thread#1 acquires the lock and changes the shared state
  • thread#1 releases the lock, pthread_mutex_unlock(&mutex), and is preempted
  • thread#2 acquires the lock, pthread_mutex_lock(&mutex), changes state, unlocks, signals and is preempted
  • thread#1 is scheduled again and waits on the condition, pthread_cond_wait(&cond)

You have one issue that can escalate :

  • the signal has been lost: by itself it can be more or less critical depending on your application
  • but even if the signal is not important by itself thread#1 is now stuck waiting for a signal that already happened
  • and even if thread#1 didn't do something important you can have a dead lock if later thread#2 waits for thread#1

So to avoid this issue thread#1 must wait immediately when it releases the locks before any other thread has any chance to signal the condition.

IMHO it's more the unlock/wait transition than the wake-up/lock transition that needs to be atomic.

I'm curious about scenarios in which the wake-up/lock transition would absolutely need to be atomic...

Hope this helps.

Pragmateek
  • 13,174
  • 9
  • 74
  • 108