Search code examples
cmultithreadingpthreadsmutexcondition-variable

Mutex status after spurious wakeup


Consider this basic multithreading program using pthreads. We have a main thread, creating another thread that does some work.

bool done = false;
mutex m; 
condition c;

void foo() {
    pthread_mutex_lock(&m);
    //while(!done) {
        pthread_cond_wait(&c, &m);
        // Spuriously wakeup while child is doing work.
        // child thread has NOT unlocked the mutex yet
        // Do I now own the mutex?
        // or am I waiting for child to unlock it?
    //}
    pthread_mutex_unlock(&m);
}

void * child(void *arg) {
    pthread_mutex_lock(&m);

    some_intense_work(); // this work is done while mutex is held!
    // the main thread spuriously wakes up
    // while this work is being done
    // (while this child thread is holding the mutex)

    done = true;
    pthread_cond_broadcast(&c);
    pthread_mutex_unlock(&m);
}

int main(int argc, char *argv[]) {
    pthread_t p;
    pthread_create(&p, NULL, child, NULL);
    foo();
}

Pretend that we implement a wait without a surrounding while-clause checking for the predicate, even though we are aware that nobody should ever do this.

Now, if, while the child thread is doing its work, a spurious wakeup occurs in the main thread, what will the status of the mutex m be? Will the main thread own it without the child unlocking it first, so that both own it?

Or does a spurious wakeup only skip the wait for the condition, but not the wait for the mutex to be freed?


Solution

  • The pthread_cond_wait() call cannot 'spuriously' wake while some other thread is holding the associated mutex. When pthread_cond_wait() returns successfully, it will have claimed the mutex, so it cannot successfully return until the mutex is available.

    In your example, a spurious wake could occur because foo() can call pthread_cond_wait() and have the spurious wake occur before child() ever gets a chance to call pthread_mutex_lock() in the first place.

    Another problem in your example (with the commented code left disabled) has is that it's possible for the pthread_cond_wait() call to never wake. This scenario can happen if child() completes all of it's processing before foo() manages to acquire the mutex. in that scenario, child() will call pthread_cond_broadcast() before the main thread is waiting in pthread_cond_wait() so the main thread will miss the broadcast. Since foo() never checks done while holding the mutex, it won't notice that child() has finished its work.

    That's why pthread_cond_wait() pretty much always has to be performed in a loop that checks the condition.