Pthread Interfaces

#include <pthread.h>
int pthread_create(pthread_t *tidp,
                   const pthread_attr_t attr,
		   void *(*start_rtn)(void *),
		   void *arg);
int pthread_join(pthread_t *tidp, void **rval_ptr);
int pthread_mutex_lock(pthread_mutex_t *mutex);
int pthread_mutex_unlock(pthread_mutex_t *mutex);
int pthread_cond_wait(pthread_cond_t *cond, pthread_mutex_t *mutex);
int pthread_cond_signal(pthread_cond_t *cond);

Precautions of pthread_cond_wait()

  • pthread_cond_wait() accepts two arguments: ‘cond’ and ‘mutex’.
  • ‘cond’ is a condition variable.
  • a thread should acquire ‘mutex’ before calling pthread_cond_wait().
  • pthread_cond_wait() ATOMICALLY releases ‘mutex’ and blocks on ‘cond’.
    • ATOMICALLY here means if another thread acquires ‘mutex’ just after the about-to-block thread has released it, then a subsequent call to pthread_cond_signal() in that thread shall behave AS IF it were issued after the about-to-block thread has blocked.
  • upon successfully return, ‘mutex’ shall have been locked and owned by the calling thread.

Typical Usage of pthread_cond_wait/signal()

  • When using condition variables, there is always a boolean predicate involving shared variables associated with each condition wait that is true if the thread should proceed.
  • Spurious wakeups from condition wait may occur, for example, on multi-processor platform, multiple threads are woken up when the condition variable is signaled simutaneously on different processors.
  • Since the return from pthread_cond_wait() does not imply anything about the value of the predicate, the thread has to re-evaluate the predicate to determine whether it can safely proceed or should wait again.
  • It is thus recommended that a condition wait be enclosed in a while loop that checks the predicate.
thread 1:
	pthread_mutex_lock(&mutex);
	while (!condition)
		pthread_cond_wait(&cond, &mutex);
	/* do something that requires holding the mutex and condition is true */
	pthread_mutex_unlock(&mutex);

thread 2:
	pthread_mutex_lock(&mutex);
	/* do something that might make condition true */
	pthread_cond_signal(&cond);
	pthread_mutex_unlock(&mutex);

Program that Runs Two Threads in Turn

#include <stdio.h>
#include <stdlib.h>
#include <stdbool.h>
#include <pthread.h>

pthread_mutex_t mutex_a = PTHREAD_MUTEX_INITIALIZER;
pthread_mutex_t mutex_b = PTHREAD_MUTEX_INITIALIZER;

pthread_cond_t cond_a = PTHREAD_COND_INITIALIZER;
pthread_cond_t cond_b = PTHREAD_COND_INITIALIZER;

bool is_round_of_a = false;
bool is_round_of_b = false;

void *print_a(void *arg)
{
	while (1) {
		pthread_mutex_lock(&mutex_a);
		/* If thread_c has already sent the signal which means
		 * is_round_of_a is also already set to true by thread_c,
		 * thread_a just does not wait for the signal anymore. */
		while (!is_round_of_a)
			pthread_cond_wait(&cond_a, &mutex_a);
		printf("a\n");
		is_round_of_a = false;
		pthread_mutex_unlock(&mutex_a);

		/* send signal to thread_b and set is_round_of_b to true */
		pthread_mutex_lock(&mutex_b);
		pthread_cond_signal(&cond_b);
		is_round_of_b = true;
		pthread_mutex_unlock(&mutex_b);
	}

	return (void *)0;
}

void *print_b(void *arg)
{
	while (1) {
		pthread_mutex_lock(&mutex_b);
		/* If thread_a has already sent the signal which means
		 * is_round_of_b is also already set to true by thread_a,
		 * thread_b just does not wait for the signal anymore. */
		while (!is_round_of_b)
			pthread_cond_wait(&cond_b, &mutex_b);
		printf("b\n");
		is_round_of_b = false;
		pthread_mutex_unlock(&mutex_b);

		/* send signal to thread_a and set is_round_of_a to true */
		pthread_mutex_lock(&mutex_a);
		pthread_cond_signal(&cond_a);
		is_round_of_a = true;
		pthread_mutex_unlock(&mutex_a);
	}

	return (void *)0;
}

int main(int argc, char **argv)
{
	pthread_t tid_a, tid_b;

	pthread_create(&tid_a, NULL, print_a, NULL);
	pthread_create(&tid_b, NULL, print_b, NULL);

	/* At this point, two threads are all waiting to be woken up.
	 * We let main send a signal and wake up thread_a to run first */
	pthread_mutex_lock(&mutex_a);
	pthread_cond_signal(&cond_a);
	is_round_of_a = true;
	pthread_mutex_unlock(&mutex_a);

	/* Waiting for two threads to complete.
	 * Actually we will never reach here since two threads are all
	 * executing in an infinite loop. */
	pthread_join(tid_a, NULL);
	pthread_join(tid_b, NULL);

	return 0;
}

Compile and Run

$ gcc ab.c -lpthread
$ ./a.out
a
b
a
b
a
b
a
...

References

[1] http://stackoverflow.com/questions/16522858/understanding-of-pthread-cond-wait-and-pthread-cond-signal
[2] http://linux.die.net/man/3/pthread_cond_wait
[3] http://www.ibm.com/developerworks/library/l-posix1
[4] http://www.ibm.com/developerworks/library/l-posix2
[5] http://www.ibm.com/developerworks/library/l-posix3