/* * barrier.c * * This file implements the "barrier" synchronization construct. * * A barrier causes threads to wait until a set of threads has * all "reached" the barrier. The number of threads required is * set when the barrier is initialized, and cannot be changed * except by reinitializing. * * The barrier_init() and barrier_destroy() functions, * respectively, allow you to initialize and destroy the * barrier. * * The barrier_wait() function allows a thread to wait for a * barrier to be completed. One thread (the one that happens to * arrive last) will return from barrier_wait() with the status * -1 on success -- others will return with 0. The special * status makes it easy for the calling code to cause one thread * to do something in a serial region before entering another * parallel section of code. */ #include #include "errors.h" #include "barrier.h" /* * Initialize a barrier for use. */ int barrier_init (barrier_t *barrier, int count) { int status; barrier->threshold = barrier->counter = count; barrier->cycle = 0; status = pthread_mutex_init (&barrier->mutex, NULL); if (status != 0) return status; status = pthread_cond_init (&barrier->cv, NULL); if (status != 0) { pthread_mutex_destroy (&barrier->mutex); return status; } barrier->valid = BARRIER_VALID; return 0; } /* * Destroy a barrier when done using it. */ int barrier_destroy (barrier_t *barrier) { int status, status2; if (barrier->valid != BARRIER_VALID) return EINVAL; status = pthread_mutex_lock (&barrier->mutex); if (status != 0) return status; /* * Check whether any threads are known to be waiting; report * "BUSY" if so. */ if (barrier->counter != barrier->threshold) { pthread_mutex_unlock (&barrier->mutex); return EBUSY; } barrier->valid = 0; status = pthread_mutex_unlock (&barrier->mutex); if (status != 0) return status; /* * If unable to destroy either 1003.1c synchronization * object, return the error status. */ status = pthread_mutex_destroy (&barrier->mutex); status2 = pthread_cond_destroy (&barrier->cv); return (status == 0 ? status : status2); } /* * Wait for all members of a barrier to reach the barrier. When * the count (of remaining members) reaches 0, broadcast to wake * all threads waiting. */ int barrier_wait (barrier_t *barrier) { int status, cancel, tmp, cycle; if (barrier->valid != BARRIER_VALID) return EINVAL; status = pthread_mutex_lock (&barrier->mutex); if (status != 0) return status; cycle = barrier->cycle; /* Remember which cycle we're on */ if (--barrier->counter == 0) { barrier->cycle = !barrier->cycle; barrier->counter = barrier->threshold; status = pthread_cond_broadcast (&barrier->cv); /* * The last thread into the barrier will return status * -1 rather than 0, so that it can be used to perform * some special serial code following the barrier. */ if (status == 0) status = -1; } else { /* * Wait with cancellation disabled, because barrier_wait * should not be a cancellation point. */ pthread_setcancelstate (PTHREAD_CANCEL_DISABLE, &cancel); /* * Wait until the barrier's cycle changes, which means * that it has been broadcast, and we don't want to wait * anymore. */ while (cycle == barrier->cycle) { status = pthread_cond_wait ( &barrier->cv, &barrier->mutex); if (status != 0) break; } pthread_setcancelstate (cancel, &tmp); } /* * Ignore an error in unlocking. It shouldn't happen, and * reporting it here would be misleading -- the barrier wait * completed, after all, whereas returning, for example, * EINVAL would imply the wait had failed. The next attempt * to use the barrier *will* return an error, or hang, due * to whatever happened to the mutex. */ pthread_mutex_unlock (&barrier->mutex); return status; /* error, -1 for waker, or 0 */ }