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 <pthread.h>
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 }
Part 2 shows the barrier_destroy function, which destroys the mutex and condition variable (cv) in the barrier_t structure. If we had allocated any additional resources for the barrier, we would need to release those resources also. 8-9 First check that the barrier appears to be valid, and initialized, by looking at the valid member. We don't lock the mutex first, because that will fail, possibly with something nasty like a segmentation fault, if the mutex has been destroyed or hasn't been initialized. Because we do not lock the mutex first, the validation check is not entirely reliable, but it is better than nothing, and will only fail to detect some race conditions where one thread attempts to destroy a barrier while another is initializing it, or where two threads attempt to destroy a barrier at nearly the same time.
19-22 If any thread is currently waiting on the barrier, return EBUSY.
24-27 At this point, the barrier is 'destroyed'—all that's left is cleanup. To minimize the chances of confusing errors should another thread try to wait on the barrier before we're done, mark the barrier 'not valid' by clearing valid, before changing any other state. Then, unlock the mutex, since we cannot destroy it while it is locked.
33-35 Destroy the mutex and condition variable. If the mutex destruction fails return the status; otherwise, return the status of the condition variable destruc-tion. Or, to put it another way, return an error status if either destruction failed otherwise, return success.
¦ barrier.c part 2 barrier_destroy
1 /*
2 * Destroy a barrier when done using it.
3 */
4 int barrier_destroy (barrier_t *barrier)
5 {
6 int status, status2;
7
8 if (barrier->valid != BARRIER_VALID)
9 return EINVAL;
10
11 status = pthread_mutex_lock (&barrier->mutex);
12 if (status != 0)
13 return status;
14
15 /*
16 * Check whether any threads are known to be waiting; report
17 * 'BUSY' if so.
18 */
19 if (barrier->counter != barrier->threshold) {
20 pthread_mutex_unlock (&barrier->mutex);
21 return EBUSY;
22 }
23
24 barrier->valid = 0;
25 status = pthread_mutex_unlock (&barrier->mutex);
26 if (status != 0)
27 return status;
28
29 /*
30 * If unable to destroy either 1003.1c synchronization
31 * object, return the error status.
32 */
33 status = pthread_mutex_destroy (&barrier->mutex);
34 status2 = pthread_cond_destroy (&barrier->cv);
35 return (status == 0 ? status : status2);
36 }
Finally, part 3 shows the implementation ofbarrier_wait.
10-11 First we verify that the argument barrier appears to be a valid barrier_t. We
perform this check before locking the mutex, so that barrier_destroy can safely destroy the mutex once it has cleared the valid member. This is a simple attempt to minimize the damage if one thread attempts to wait on a barrier while another thread is simultaneously either initializing or destroying that barrier.
We cannot entirely avoid problems, since without the mutex, barrier_wait has no guarantee that it will see the correct (up-to-date) value of valid. The valid check may succeed when the barrier is being made invalid, or fail when the barrier is being made valid. Locking the mutex first would do no good, because the mutex may not exist if the barrier is not fully initialized, or if it is being destroyed. This isn't a problem as long as you use the barrier correctly — that is, you initialize the barrier before any thread can possibly try to use it, and do not destroy the barrier until you are sure no thread will try to use it again.
17 Copy the current value of the barrier's cycle into a local variable. The comparison of our local cycle against the barrier_t structure's cycle member becomes our condition wait predicate. The predicate ensures that all currently waiting threads will return from barrier_wait when the last waiter broadcasts the condition variable, but that any thread that calls barrier_wait again will wait for the next broadcast. (This is the 'tricky part' of correctly implementing a barrier.)