umläute umläute - 28 days ago 6
C Question

how to wait for starting thread to have executed init code

I'm having trouble synchronizing a master thread to a recently started child thread.

What I want to do is:


  • master thread creates a new child thread and blocks

  • child thread starts and initializes (might take some time)

  • once the child thread is initialized, the main thread continues (and the two threads run in parallel)



My first attempt was something like:

typedef struct threaddata_ {
int running;
} threaddata_t;

void*child_thread(void*arg) {
threaddata_t*x=(threaddata_t)arg;
/* ... INITIALIZE ... */
x->running=1; /* signal that we are running */

/* CHILD THREAD BODY */

return 0;
}

void start_thread(void) {
threaddata_t*x=(threaddata_t*)malloc(sizeof(threaddata_t));
x->running=0;
int result=pthread_create(&threadid, 0, child_thread, &running);
while(!x->running) usleep(100); /* wait till child is initialized */

/* MAIN THREAD BODY */
}


Now I didn't like this at all, because it forces the main thread to sleep for probably a longer period than necessary.
So I did a 2nd attempt, using mutexes&conditions

typedef struct threaddata_ {
pthread_mutex_t x_mutex;
pthread_cond_t x_cond;
} threaddata_t;

void*child_thread(void*arg) {
threaddata_t*x=(threaddata_t)arg;
/* ... INITIALIZE ... */

pthread_cond_signal(&x->x_cond); /* signal that we are running */

/* CHILD THREAD BODY */

return 0;
}

void start_thread(void) {
threaddata_t*x=(threaddata_t*)malloc(sizeof(threaddata_t));
pthread_mutex_init(&x->x_mutex, 0);
pthread_cond_init (&x->x_cond , 0);

pthread_mutex_lock(&x->x_mutex);
int result=pthread_create(&threadid, 0, child_thread, &running);
if(!result)pthread_cond_wait(&x->x_cond, &x->x_mutex);
pthread_mutex_unlock(&x->x_mutex);

/* MAIN THREAD BODY */
}


This seemed more sane than the first attempt (using proper signals rather than rolling my own wait loop), until I discovered, that this includes a race condition:
If the child thread has finished the initialization fast enough (before the main thread waits for the condition), it will deadlock the main thread.

I guess that my case is not so uncommon, so there must be a really easy solution, but I cannot see it right now.

Answer

Proper way of condvar/mutex pair usage:

bool initialised = false;
mutex mt;
convar cv;

void *thread_proc(void *)
{
   ...
   mt.lock();
   initialised = true;
   cv.signal();
   mt.unlock();
}

int main()
{
   ...
   mt.lock();
   while(!initialised) cv.wait(mt);
   mt.unlock();
}

This algorithm avoids any possible races. You can use any complex condition modified when mutex locked (instead of the simple !initialised).