Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

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.

like image 504
umläute Avatar asked Dec 12 '22 00:12

umläute


2 Answers

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).

like image 137
Dmitry Poroh Avatar answered Dec 14 '22 14:12

Dmitry Poroh


The correct tool for that are sem_t. The main thread would initialize them with 0 and wait until it receives a token from the newly launched thread.

BTW your mutex/cond solution has a race condition because the child thread is not locking the mutex.

like image 42
Jens Gustedt Avatar answered Dec 14 '22 13:12

Jens Gustedt