Search code examples
cmultithreadingpthreadsmutexcondition-variable

If pthread_cond_wait() requires locked mutex, doesn't it block the thread which changed condition?


I need a two-threaded application. One thread prepare data, one process it. If there is no data to process, the second thread should sleep. Doing as tons of tutorials suggests:

pthread_mutex_t mtx;
pthread_cond_t cond;

char *shared_data = NULL;

char *process_data() {
    char *dt;

    pthread_mutex_lock(&mtx);
    if(!shared_data) pthread_cond_wait(&cond, &mtx);

    dt = strdup(shared_data);
    free(shared_data);
    shared_data = NULL;

    pthread_mutex_unlock(&mtx);

    return dt;
}

void create_data(char *dt) {
    pthread_mutex_lock(&mtx);

    shared_data = strdup(dt);

    pthread_cond_signal(&cond);
    pthread_mutex_unlock(&mtx);
}

So, assuming the reader thread starts first.

  1. The shared_data is currently NULL and there is nothing to read. So it locks the mutex and waits till cond is triggered.
  2. The creator thread starts, it tries to lock the mutex, but it is already locked by the waiting reader...

Aren't we in a deadlock? What am I missing here?


Solution

  • In a simplified world, this is what happens:

    pthread_mutex_lock(&mtx);
    
    // the mutex is now locked so it's safe to access shared_data
    
    if(!shared_data) // safe
    
        // now, unlock the mutex to let others lock it and when the condition
        // variable gets signalled, re-lock the mutex and return
    
        pthread_cond_wait(&cond, &mtx);
    

    So, there's no catch 22. The mutex is unlocked/relocked (sometimes multiple times) inside pthread_cond_wait. When pthread_cond_wait returns, it will be locked.

    The most important part here that isn't quite true is that pthread_cond_wait may actually return even before the condition variable is signalled. This is called spurious wakeups and happens for implementation specific reasons. The only way to be sure that what you are waiting for has actually happend is to check that the shared resource has actually changed into the state you are waiting for.

    Simply replace if with while.

    while(!shared_data)
        pthread_cond_wait(&cond, &mtx);
    

    Not replacing the if with while in your case will eventually bite you and you'll suddenly be using a NULL pointer in the following code.