Versions Compared

Key

  • This line was added.
  • This line was removed.
  • Formatting was changed.
Comment: Minor edits; reviewed

...

  • All threads must perform the same set of operations after waking up, which means that any thread can be selected to wake up and resume for a single invocation of cnd_signal().
  • Only one thread is required to wake upon receiving the signal.

The use of of cnd_signal() is also safe if each thread each thread uses a unique condition variable.

The use of cnd_broadcast() avoids these problems because it wakes up all the threads associated with the condition variable, and because all the threads must reevaluate the predicate condition, one thread will find its test to be true, avoiding deadlock.

...

This noncompliant code example uses five threads that are intended to execute one after another execute sequentially according to the step level assigned to each thread when it is created (serialized processing). The current_step variable holds the current step level and is incremented when the respective thread completes. Finally, another thread is signaled so that the next step can be executed. Each thread waits until its step level is ready, and the cnd_wait() function call is wrapped inside a while loop, in compliance with CON44with CON36-C. Wrap functions that can fail spuriously wake up in a loop.

Code Block
bgColor#FFcccc
langc
#include <stdio.h>
#include <threads.h>

enum { NTHREADS = 5 };

mtx_t mutex;
cnd_t cond;

int run_step(void *t) {
  static int current_step = 0;
  size_t my_step = (size_t)t;

  if (thrd_success != mtx_lock(&mutex)) {
    /* Handle error condition */
  }

  printf("Thread %d has the lock\n", my_step);

  while (current_step != my_step) {
    printf("Thread %d is sleeping...\n", my_step);

    if (thrd_success != cnd_wait(&cond, &mutex)) {
      /* Handle error condition */
    }

    printf("Thread %d woke up\n", my_step);
  }

  /* Do processing ... */
  printf("Thread %d is processing...\n", my_step);
  current_step++;

  /* Signal a waiting task */
  if (thrd_success != cnd_signal(&cond)) {
    /* Handle error condition */
  }

  printf("Thread %d is exiting...\n", my_step);

  if (thrd_success != mtx_unlock(&mutex)) {
    /* Handle error condition */
  }
  return 0;
}

int main(int argc, char** argv) {
  thrd_t threads[NTHREADS];
  size_t step[NTHREADS];

  if (thrd_success != mtx_init(&mutex, mtx_plain)) {
    /* Handle error condition */
  }
 
  if (thrd_success != cnd_init(&cond)) {
    /* Handle error condition */
  }

  /* Create threads */
  for (size_t i = 0; i < NTHREADS; ++i) {
    step[i] = i;
    if (thrd_success != thrd_create(&threads[i], run_step,
                                    (void *)step[i])) {
      /* Handle error condition */
    }
  }

  /* Wait for all threads to complete */
  for (size_t i = NTHREADS; i != 0; --i) {
    if (thrd_success != thrd_join(threads[i-1], NULL)) {
      /* Handle error condition */
    }
  }

  mtx_destroy(&mutex);
  cnd_destroy(&cond);
  return 0;
}

...

Another compilant solution is to use a unique condition variable for each thread (all associated with a single mutex). In this case, the signal operation (cnd_signal()) wakes up only the thread that is waiting on it. This solution is more efficient than using cnd_broadcast() because only the desired thread is awakened.

NOTENote: The the condition predicate of the signaled thread must be true; otherwise, a deadlock will occur.

...

[IEEE Std 1003.1:2013]XSH, System Interfaces, pthread_cond_broadcast
XSH, System Interfaces, pthread_cond_signal

 

...