0

The main thread spawns > 1 threads, and each of them can call return with an error value. If this happens, it is meaningless for the other threads to continue, and so they should be canceled.

So, I want my main thread to:

  • Join with whichever thread finishes first;
  • Check if this thread returned error, and if so, cancel all other threads.

However, pthread_join requires me to specify which thread I want to join with. If I call, for example, pthread_join(thread1, thread1_ret), and thread2 finishes with error, then I won’t be able to know that thread2 finished on error before thread1 finishes, and the fact that thread2 finished prematurely might very well mean that thread1 is currently waiting on a conditional variable that will never be signaled, because only thread2 may signal that variable… So, bad.

I want my main thread to cancel thread1 if thread2 finishes and vice-versa.

How to accomplish this?

2 Answers2

0

How to accomplish this?

You need a separate communication channel.

A typical solution involves a queue (of finished threads) and a condition variable.

The thread that finishes with error puts itself into the queue and signals the condition before returning. The main thread waits on the condition, checks the queue, and joins the thread that it finds there, then cancels all the other threads.

Note also that async thread cancellation is tricky to get right. It is usually better to have a global variable that all the threads check periodically: while (!exit_requested) { do_work(); }

Employed Russian
  • 199,314
  • 34
  • 295
  • 362
0

You want something like this:

struct thread_data {
  int stop;
  pthread_cond_t * flag;
  pthread_mutex_t * lock;
  int * failCount;
  int * successCount;
};

void * foobar(void * ptr)
{
  struct thread_data * data = (struct thread_data*)ptr;
  int fail = 0;
  while (isWorkToBeDone() && !data->stop) {
    // do some work

    if (encounteredError()) {
      pthread_mutex_lock(data->lock);
      data->failCount += 1;
      fail = 1;
      pthread_cond_signal(data->flag);
      pthread_mutex_unlock(data->lock);
    }
  }

  // clean up
  if (!fail) {
    pthread_mutex_lock(data->lock);
    data->successCount += 1;
    pthread_cond_signal(data->flag);
    pthread_mutex_unlock(data->lock);
  }

  pthread_exit(NULL);
}


int runThreads()
{
  pthread_t * threads;

  pthread_mutex_t lock;
  pthread_cond_t flag;

  int i;
  struct thread_data data;

  threads = malloc(sizeof(*threads)*numThreads);
  if (!threads) {
    // handle malloc error
    return 0;
  }

  // initialize mutex and condition variable
  pthread_mutex_init(&lock, NULL);
  pthread_cond_init(&flag, NULL);

  data.stop = 0;
  data.flag = &flag;
  data.lock = &lock;
  data.failCount = 0;
  data.successCount = 0;

  for (i = 0; i < numThreads; ++i) {
    if (!pthread_create(threads+i, NULL, foobar,
        (void *)(threads+i))) {
      // handle thread creation error
    }
  }

  while (!data.stop) {
    pthread_mutex_lock(&lock);
    pthread_cond_wait(&cond, &lock);
    // a thread just finished
    if (data.failCount > 0) {
      data.stop = 1;
    } else if (data.successCount == numThreads) {
      data.stop = 1;
    }
    pthread_mutex_unlock(&lock);
  }

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

  pthread_mutex_destroy(&lock);
  pthread_cond_destroy(&flag);
  free(threads);
}

The way it works is you wait for either one thread to fail, or all threads to succeed, and then let all threads know to stop working and exit cleanly via the stop variable.

Instead of having threads check the stop variable, you could call pthread_kill(), but its much better to exit each thread cleanly.

dlasalle
  • 1,615
  • 3
  • 19
  • 25