#include #include #include /* these signal which thread has its turn to execute */ enum { THREAD1_OK_TO_GO, THREAD2_OK_TO_GO, THREAD3_OK_TO_GO }; /* the wait queue variable needs to be set * before threads execute */ int queue = THREAD1_OK_TO_GO; void *thread1_func (void*); void *thread2_func (void*); void *thread3_func (void*); /* mutex and cond for queue handling */ pthread_mutex_t cond_mutex1 = PTHREAD_MUTEX_INITIALIZER; pthread_cond_t cond1 = PTHREAD_COND_INITIALIZER; int main (void) { pthread_t thread1, thread2, thread3; /* we'll create the threads in reverse order to demonstrate * that the queueing works in spite of it */ pthread_create (&thread3, NULL, thread3_func, NULL); pthread_create (&thread2, NULL, thread2_func, NULL); pthread_create (&thread1, NULL, thread1_func, NULL); /* we'll wait for the threads to join, which in this case * will never happen because they loop indefinitely, but * that's OK for our purposes */ pthread_join (thread1, NULL); pthread_join (thread2, NULL); pthread_join (thread3, NULL); return 0; } /* notice that the thread functions are nearly identical, so the * second and third one don't need much further commenting */ void *thread1_func (void *ptr) { int i; while (1) { /* lock the queue check to prevent races, then wait * for our turn */ pthread_mutex_lock (&cond_mutex1); while (queue != THREAD1_OK_TO_GO) pthread_cond_wait (&cond1, &cond_mutex1); pthread_mutex_unlock (&cond_mutex1); /* notice that we don't need a separate mutex for the * relevant part because it's conveniently sandwiched * in between the queue handling code */ putchar ('1'); /* after we're done we specify who goes next, then * signal the other threads that they can proceed. * 3 or more threads requires pthread_cond_broadcast(), * for only 2 threads we could use pthread_cond_signal(). * note that locking is absolutely necessary here too! */ pthread_mutex_lock (&cond_mutex1); queue = THREAD2_OK_TO_GO; pthread_cond_broadcast (&cond1); pthread_mutex_unlock (&cond_mutex1); } } /* differing parts are commented */ void *thread2_func (void *ptr) { int i; while (1) { /* notice that we're now waiting for THREAD2_OK_TO_GO */ pthread_mutex_lock (&cond_mutex1); while (queue != THREAD2_OK_TO_GO) pthread_cond_wait (&cond1, &cond_mutex1); pthread_mutex_unlock (&cond_mutex1); putchar ('2'); /* notice that we're now signaling THREAD3_OK_TO_GO */ pthread_mutex_lock (&cond_mutex1); queue = THREAD3_OK_TO_GO; pthread_cond_broadcast (&cond1); pthread_mutex_unlock (&cond_mutex1); } } void *thread3_func (void *ptr) { int i; while (1) { pthread_mutex_lock (&cond_mutex1); while (queue != THREAD3_OK_TO_GO) pthread_cond_wait (&cond1, &cond_mutex1); pthread_mutex_unlock (&cond_mutex1); putchar ('3'); /* signaling the first thread again */ pthread_mutex_lock (&cond_mutex1); queue = THREAD1_OK_TO_GO; pthread_cond_broadcast (&cond1); pthread_mutex_unlock (&cond_mutex1); } }