10 int threshold; /* number of threads required */
11 int counter; /* current number of threads * /
12 int cycle; /* alternate cycles (0 or 1) * /
13 } barrier_t;
14
15 #define BARRIER_VALID 0xdbcafe
Part 2 shows definitions and prototypes that allow you to do something with the barrier_t structure. First, you will want to initialize a new barrier. 4-6 You can initialize a static barrier at compile time by using the macro BARRIER_ INITIALIZER. You can instead dynamically initialize a barrier by calling the function barrier_init.
11-13 Once you have initialized a barrier, you will want to be able to use it. and the main thing to be done with a barrier is to wait on it. When we're done with a barrier, it would be nice to be able to destroy the barrier and reclaim the resources it used. We'll call these operations barrier_init
, barrier_wait
, and barrier_ destroy. All the operations need to specify upon which barrier they will operate. Because barriers are synchronization objects, and contain both a mutex and a condition variable (neither of which can be copied), we always pass a pointer to a barrier. Only the initialization operation requires a second parameter, the number of waiters required before the barrier opens.
To be consistent with Pthreads conventions, the functions all return an integer value, representing an error number defined in
■ barrier.h part 2 interfaces
1 /*
2 * Support static initialization of barriers.
3 */
4 #define BARRIER_INITIALIZER(cnt) \
5 {PTHREAD_MUTEX_INITIALIZER, PTHREAD_COND_INITIALIZER, \
6 BARRIER_VALID, cnt, cnt, 0}
7
8 /*
9 * Define barrier functions
10 */
11 extern int barrier_init (barrier_t *barrier, int count);
12 extern int barrier_destroy (barrier_t *barrier);
13 extern int barrier_wait (barrier_t *barrier);
Now that you know the interface definition, you could write a program using barriers. But then, the point of this section is not to tell you how to use barriers, but to help improve your understanding of threaded programming by showing how to build a barrier. The following examples show the functions provided by barrier .c, to implement the interfaces we've just seen in barrier.h.
Part 1 shows barrier_init
, which you would call to dynamically initialize a barrier, for example, if you allocate a barrier with malloc
.
12 Both the counter and threshold are set to the same value. The counter is the "working counter" and will be reset to threshold for each barrier cycle.
14-16 If mutex initialization fails, barrier_init
returns the failing status to the caller.
17-21 If condition variable (cv
) initialization fails, barrier_init
destroys the mutex it had already created and returns the failure status — the status of pthread_mutex_destroy
is ignored because the failure to create the condition variable is more important than the failure to destroy the mutex.
22 The barrier is marked valid only after all initialization is complete. This does not completely guarantee that another thread erroneously trying to wait on that barrier will detect the invalid barrier rather than failing in some less easily diag-nosable manner, but at least it is a token attempt.
■ barrier.c part 1 barrier_init
1 #include
2 #include "errors.h"
3 #include "barrier.h"
4
5 /*
6 * Initialize a barrier for use.
7 */
8 int barrier_init (barrier_t *barrier, int count)
9 {
10 int status; 11
12 barrier->threshold = barrier->counter = count;
13 barrier->cycle = 0;
14 status = pthread_mutex_init (&barrier->mutex, NULL);
15 if (status != 0)
16 return status;
17 status = pthread_cond_init (&barrier->cv, NULL);
18 if (status != 0) {
19 pthread_mutex_destroy (&barrier->mutex);
20 return status;
21 }
22 barrier->valid = BARRIER_VALID;
23 return 0;
24 }