Versions Compared

Key

  • This line was added.
  • This line was removed.
  • Formatting was changed.
Comment: Removing spurious calls to thrd_exit; fixing some whitespace in the coding examples.

...

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

enum { NTHREADS = 5 };

mtx_t mutex;
cnd_t cond;


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

  if ((result = mtx_lock(&mutex)) != thrd_success) {
    /* 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 ((result = cnd_wait(&cond, &mutex)) != thrd_success) {
      /* 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 ((result = cnd_signal(&cond)) != thrd_success) {
    /* Handle error condition */
  }

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

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

  thrd_exit(0);

  return 0;
}


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

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

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

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

  mtx_destroy(&mutex);

  cnd_destroy(&cond);

  return thrd_exit(0);
}

In this example, each thread has its own predicate because each requires current_step to have a different value before proceeding. Upon the signal operation (pthread_cond_signal()), any of the waiting threads can wake up. If, by chance, it is not the thread with the next step value, that thread will wait again (pthread_cond_wait()), resulting in a deadlock situation because no more signal operations will occur.

...

Code Block
bgColor#ccccff
langc
#include <threads.h>
#include <stdio.h>
 
int run_step(void *t) {
  static int current_step = 0;
  int my_step = (int)t;
  int result;

  if ((result = mtx_lock(&mutex)) != thrd_success) {
    /* 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 ((result = cnd_wait(&cond, &mutex)) != thrd_success) {
      /* Handle error condition */
    }

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

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

  current_step++;

  /* Signal ALL waiting tasks */
  if ((result = cnd_broadcast(&cond)) != thrd_success) {
    /* Handle error condition */
  }

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

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

  thrd_exit(0);

  return 0;
}

The fact that all threads will be awake solves the problem because each one ends up executing its predicate test; one will find its test to be true and will continue the execution until the end.

...

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

enum { NTHREADS = 5 };

mtx_t mutex;
cnd_t cond[NTHREADS];


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

  if ((result = mtx_lock(&mutex)) != thrd_success) {
    /* 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 ((result = cnd_wait(&cond[my_step], &mutex)) != thrd_success) {
      /* Handle error condition */
    }

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

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

  current_step++;

  /* Signal next step thread */
  if ((my_step + 1) < NTHREADS) {
    if ((result = cnd_signal(&cond[my_step+1])) != thrd_success) {
      /* Handle error condition */
    }
  }

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

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

  thrd_exit(0);

  return 0;
}


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

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

  for (i = 0; i< NTHREADS; i++) {
    if ((result = cnd_init(&cond[i])) != thrd_success) {
      /* Handle error condition */
    }
  }

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

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

  mtx_destroy(&mutex);

  for (i = 0; i < NTHREADS; i++) {
    cnd_destroy(&cond[i]);
  }

  return thrd_exit(0);
}

In this compliant code, each thread has associated a unique condition variable that is signaled when that particular thread needs to be awakened. This solution turns out to be more efficient because only the desired thread is awakened.

...