0123 0123 - 1 month ago 5
C Question

Understanding pthread

I'm currently confused as to why the following code won't print the following:

My value is 0
My value is 1
My value is 2


Every time I run this I either get 1-2 printed lines or nothing and the program just sits their until I ctrl-c. I feel like it might have something to do with me using the same condition variable and mutex with 3 different threads, would this be correct? Any explanations are greatly appreciated.

#include <stdio.h>
#include <pthread.h>
#include <assert.h>
#include <unistd.h>
#include <stdlib.h>

struct id_holder
{
int id;
};

pthread_mutex_t intersectionMutex = PTHREAD_MUTEX_INITIALIZER;
pthread_cond_t directionCondition = PTHREAD_COND_INITIALIZER;
struct id_holder * holder;

void * logic(void* val)
{
struct id_holder * id_struct = (struct id_holder *) val;

pthread_cond_wait(&directionCondition, &intersectionMutex);
printf("My value is %d\n", id_struct->id);
free(id_struct);
return NULL;
}

int main(void)
{
pthread_t threads[3];

for(int i = 0; i <3; i++)
{
holder = (struct id_holder *) malloc(sizeof(struct id_holder));
holder->id = i;
pthread_create(&threads[i], NULL, logic, holder);
}

for(int i = 0; i < 3; i++)
{
sleep(1);
pthread_cond_signal(&directionCondition);
}

for(int i = 0; i < 3; i++)
{
pthread_join(threads[i], NULL);
}

return 0;
}

Answer

When condition is waited for or signalled, it must be done under the lock, otherwise the behavior is unpredictable as it could into race condition. Therefore your code should look like:

pthread_mutex_lock(&intersectionMutex);
pthread_cond_wait(&directionCondition, &intersectionMutex);
pthread_mutex_unlock(&intersectionMutex);

And the same for main (you can move the lock outside the loop if you wish):

for(int i = 0; i < 3; i++) {
    sleep(1);
    pthread_mutex_lock(&intersectionMutex);
    pthread_cond_signal(&directionCondition);
    pthread_mutex_unlock(&intersectionMutex);
}

Still the code is not 100% safe as the main thread can signal the condition before the child thread invokes the wait. Although it's highly improbable here due to sleep() in the main function, generally there should be a variable that identifies whether the wait is actually needed or not. In other words, conditions are not queue but can be used to create queue.

Comments