/* * This source code was obtained from * http://www.awl.com/cseng/titles/0-201-63392-2/ * and is the source code for the programming examples of the book * entitled "Programming With POSIX(r) Threads" by Dave Butenhof * (butenhof@zko.dec.com) (thanks Dave!) The original header (which * contains no copyright) is included below. To honor what we perceive * as the intention of the original author, there is no claim of * Copyright asserted in this file. We have added the log/CVS control * info and this text, but otherwise, the code is unmodified from * it's original form. * * The routines in this file implement a useful barrier construct * built atop POSIX condition variables and mutexes. * * The contributors disclaim any representation of warranty: use this * code at your own risk. * * The Code Donkeys at R3vis Corporation. */ /* * $Revision: 1.3 $ * $Name: OpenRM-1-5-2-RC1 $ * $Log: barrier.c,v $ * Revision 1.3 2002/08/19 00:23:17 wes * Renamed routines and types to avoid name collision with those used * inside OpenRM - makes debug possible on Win32. * * Revision 1.2 2001/03/31 16:55:18 wes * Added procmode.h, which defines an RMpipe processing mode used in * most demonstration programs. The default processing mode is * RM_PIPE_MULTISTAGE_VIEW_PARALLEL. * * Revision 1.1 2000/12/02 17:22:55 wes * Initial entry. * */ /* * 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 <pthread.h> #include <errno.h> #include "barrier.h" /* * Initialize a barrier for use. */ int my_barrier_init (my_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 my_barrier_destroy (my_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 my_barrier_wait (my_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 */ }