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.
shared_data
is currently NULL and there is nothing to read. So it locks the mutex and waits till cond
is triggered.Aren't we in a deadlock? What am I missing here?
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.