threads/pthread_barrier_demo.cThis is threads/pthread_barrier_demo.c, an example to accompany the book, The Linux Programming Interface. This file is not printed in the book; it is a supplementary file for Chapter 30. The source code file is copyright 2024, Michael Kerrisk, and is licensed under the GNU General Public License, version 3. In the listing below, the names of Linux system calls and C library functions are hyperlinked to manual pages from the Linux man-pages project, and the names of functions implemented in the book are hyperlinked to the implementations of those functions.
|
/* pthread_barrier_demo.c A demonstration of the use of the pthreads barrier API. Usage: pthread_barrier_demo num-barriers num-threads The program creates 'num-threads' threads, each of which loop 'num-threads' times, waiting on the same barrier. */ #include <pthread.h> #include "tlpi_hdr.h" static pthread_barrier_t barrier; /* Barrier waited on by all threads */ static int numBarriers; /* Number of times the threads will pass the barrier */
static void * threadFunc(void *arg) { long threadNum = (long) arg; printf("Thread %ld started\n", threadNum); /* Seed the random number generator based on the current time (so that we get different seeds on each run) plus thread number (so that each thread gets a unique seed). */ srandom(time(NULL) + threadNum); /* Each thread loops, sleeping for a few seconds and then waiting on the barrier. The loop terminates when each thread has passed the barrier 'numBarriers' times. */ for (int j = 0; j < numBarriers; j++) { int nsecs = random() % 5 + 1; /* Sleep for 1 to 5 seconds */ sleep(nsecs); /* Calling pthread_barrier_wait() causes each thread to block until the call has been made by number of threads specified in the pthread_barrier_init() call. */ printf("Thread %ld about to wait on barrier %d " "after sleeping %d seconds\n", threadNum, j, nsecs); int s = pthread_barrier_wait(&barrier); /* After the required number of threads have called pthread_barrier_wait(), all of the threads unblock, and the barrier is reset to the state it had after the call to pthread_barrier_init(). In other words, the barrier can be once again used by the threads as a synchronization point. On success, pthread_barrier_wait() returns the special value PTHREAD_BARRIER_SERIAL_THREAD in exactly one of the waiting threads, and 0 in all of the other threads. This permits the program to ensure that some action is performed exactly once each time a barrier is passed. */ if (s == 0) { printf("Thread %ld passed barrier %d: return value was 0\n", threadNum, j); } else if (s == PTHREAD_BARRIER_SERIAL_THREAD) { printf("Thread %ld passed barrier %d: return value was " "PTHREAD_BARRIER_SERIAL_THREAD\n", threadNum, j); /* In the thread that gets the PTHREAD_BARRIER_SERIAL_THREAD return value, we briefly delay, and then print a newline character. This should give all of the threads a chance to print the message saying they have passed the barrier, and then provide a newline that separates those messages from subsequent output. (The only purpose of this step is to make the program output a little easier to read.) */ usleep(100000); printf("\n"); } else { /* Error */ errExitEN(s, "pthread_barrier_wait (%ld)", threadNum); } } /* Print out thread termination message after a briefly delaying, so that the other threads have a chance to display the return value they received from pthread_barrier_wait(). (This simply makes the program output a little easier to read.)*/ usleep(200000); printf("Thread %ld terminating\n", threadNum); return NULL; }
int main(int argc, char *argv[]) { if (argc != 3 || strcmp(argv[1], "--help") == 0) usageErr("%s num-barriers num-threads\n", argv[0]); numBarriers = atoi(argv[1]); int numThreads = atoi(argv[2]); /* Allocate array to hold thread IDs */ pthread_t *tid = calloc(numThreads, sizeof(pthread_t)); if (tid == NULL) errExit("calloc"); /* Initialize the barrier. The final argument specifies the number of threads that must call pthread_barrier_wait() before any thread will unblock from that call. */ int s = pthread_barrier_init(&barrier, NULL, numThreads); if (s != 0) errExitEN(s, "pthread_barrier_init"); /* Create 'numThreads' threads */ for (long threadNum = 0; threadNum < numThreads; threadNum++) { s = pthread_create(&tid[threadNum], NULL, threadFunc, (void *) threadNum); if (s != 0) errExitEN(s, "pthread_create"); } /* Each thread prints a start-up message. We briefly delay, and then print a newline character so that an empty line appears after the start-up messages. */ usleep(100000); printf("\n"); /* Wait for all of the threads to terminate */ for (int threadNum = 0; threadNum < numThreads; threadNum++) { s = pthread_join(tid[threadNum], NULL); if (s != 0) errExitEN(s, "pthread_join"); } exit(EXIT_SUCCESS); }
Note that, in most cases, the programs rendered in these web pages are not free standing: you'll typically also need a few other source files (mostly in the lib/ subdirectory) as well. Generally, it's easier to just download the entire source tarball and build the programs with make(1). By hovering your mouse over the various hyperlinked include files and function calls above, you can see which other source files this file depends on.