diff options
-rw-r--r-- | ChangeLog | 8 | ||||
-rw-r--r-- | barrier.c | 149 | ||||
-rw-r--r-- | implement.h | 8 | ||||
-rw-r--r-- | pthread.h | 2 | ||||
-rw-r--r-- | tests/barrier5.c | 14 |
5 files changed, 95 insertions, 86 deletions
@@ -1,3 +1,11 @@ +2001-07-10 Ross Johnson <rpj@setup1.ise.canberra.edu.au>
+
+ * barrier.c: Revamped to fix the race condition. Two alternating
+ semaphores are used instead of the PulseEvent. Also improved
+ overall throughput by returning PTHREAD_BARRIER_SERIAL_THREAD
+ to the first waking thread.
+ * implement.h (pthread_barrier_t_): Revamped.
+
2001-07-09 Ross Johnson <rpj@setup1.ise.canberra.edu.au>
* barrier.c: Fix several bugs in all routines. Now passes
@@ -27,61 +27,62 @@ #include "implement.h" +#ifdef __MINGW32__ +#define _LONG long +#define _LPLONG long* +#else +#define _LONG PVOID +#define _LPLONG PVOID* +#endif + int pthread_barrier_init(pthread_barrier_t * barrier, const pthread_barrierattr_t * attr, - int count) + unsigned int count) { - int result = 0; int pshared = PTHREAD_PROCESS_PRIVATE; pthread_barrier_t b; + pthread_mutexattr_t ma; - if (barrier == NULL) + if (barrier == NULL || count == 0) { return EINVAL; } - b = (pthread_barrier_t) calloc(1, sizeof(*b)); - - if (b == NULL) - { - result = ENOMEM; - goto FAIL0; - } - - if (attr != NULL && *attr != NULL) - { - pshared = (*attr)->pshared; - } - - b->nCurrentBarrierHeight = b->nInitialBarrierHeight = count; - - result = pthread_mutex_init(&(b->mtxExclusiveAccess), NULL); - if (0 != result) + if (NULL != (b = (pthread_barrier_t) calloc(1, sizeof(*b)))) { - goto FAIL1; - } + if (attr != NULL && *attr != NULL) + { + pshared = (*attr)->pshared; + } - b->eventBarrierBreeched = CreateEvent(NULL, /* Security attributes */ - TRUE, /* Manual reset */ - FALSE, /* Initially signaled */ - NULL); /* Name */ + b->nCurrentBarrierHeight = b->nInitialBarrierHeight = count; + b->iStep = 0; + b->nSerial = 0; - if (NULL != b->eventBarrierBreeched) - { - goto DONE; + if (0 == pthread_mutexattr_init(&ma) && + 0 == pthread_mutexattr_setpshared(&ma, pshared)) + { + if (0 == pthread_mutex_init(&(b->mtxExclusiveAccess), &ma)) + { + pthread_mutexattr_destroy(&ma); + if (0 == sem_init(&(b->semBarrierBreeched[0]), pshared, 0)) + { + if (0 == sem_init(&(b->semBarrierBreeched[1]), pshared, 0)) + { + *barrier = b; + return 0; + } + (void) sem_destroy(&(b->semBarrierBreeched[0])); + } + (void) pthread_mutex_destroy(&(b->mtxExclusiveAccess)); + } + pthread_mutexattr_destroy(&ma); + } + (void) free(b); } - (void) pthread_mutex_destroy(&(b->mtxExclusiveAccess)); - result = ENOMEM; - FAIL1: - (void) free(b); - b = NULL; - - FAIL0: - DONE: - *barrier = b; - return(result); + return ENOMEM; } int @@ -109,20 +110,12 @@ pthread_barrier_destroy(pthread_barrier_t *barrier) * *barrier != NULL. */ *barrier = NULL; - - result = CloseHandle(b->eventBarrierBreeched); (void) pthread_mutex_unlock(&(b->mtxExclusiveAccess)); - if (result == TRUE) - { - (void) pthread_mutex_destroy(&(b->mtxExclusiveAccess)); - (void) free(b); - result = 0; - } - else - { - *barrier = b; - result = EINVAL; - } + + (void) sem_destroy(&(b->semBarrierBreeched[1])); + (void) sem_destroy(&(b->semBarrierBreeched[0])); + (void) pthread_mutex_destroy(&(b->mtxExclusiveAccess)); + (void) free(b); } return(result); @@ -145,31 +138,28 @@ pthread_barrier_wait(pthread_barrier_t *barrier) if (0 == result) { + int step = b->iStep; + unsigned int serial = b->nSerial; + if (0 == --(b->nCurrentBarrierHeight)) { - b->nCurrentBarrierHeight = b->nInitialBarrierHeight; (void) pthread_mutex_unlock(&(b->mtxExclusiveAccess)); /* - * This is a work-around for the FIXME below. We - * give any threads that didn't quite get to register - * their wait another quantum. This is temporary - * - there is a better way to do this. + * All other threads in this barrier set have at least passed + * through the decrement and test above, so its safe to + * raise the barrier to its full height. */ - Sleep(0); - (void) PulseEvent(b->eventBarrierBreeched); - /* - * Would be better if the first thread to return - * from this routine got this value. On a single - * processor machine that will be the last thread - * to reach the barrier (us), most of the time. - */ - result = PTHREAD_BARRIER_SERIAL_THREAD; + b->iStep = 1 - step; + b->nCurrentBarrierHeight = b->nInitialBarrierHeight; + (void) sem_post_multiple(&(b->semBarrierBreeched[step]), b->nInitialBarrierHeight - 1); } else { pthread_t self; int oldCancelState; + (void) pthread_mutex_unlock(&(b->mtxExclusiveAccess)); + self = pthread_self(); /* @@ -181,20 +171,33 @@ pthread_barrier_wait(pthread_barrier_t *barrier) pthread_setcancelstate(PTHREAD_CANCEL_DISABLE, &oldCancelState); } - (void) pthread_mutex_unlock(&(b->mtxExclusiveAccess)); - - /* FIXME!!! It's possible for a thread to be left behind at a - * barrier because of the time gap between the unlock - * and the registration that the thread is waiting on the - * event. + /* + * There is no race condition between the semaphore wait and post + * because we are using two alternating semas and all threads have + * entered barrier_wait and checked nCurrentBarrierHeight before this + * barrier's sema can be posted. Any threads that have not quite + * entered sem_wait below when the multiple_post above is called + * will nevertheless continue through the sema (and the barrier). + * We have fulfilled our synchronisation function. */ - result = pthreadCancelableWait(b->eventBarrierBreeched); + result = sem_wait(&(b->semBarrierBreeched[step])); if (self->cancelType == PTHREAD_CANCEL_DEFERRED) { pthread_setcancelstate(oldCancelState, NULL); } } + + /* + * The first thread to return from the wait will be the + * PTHREAD_BARRIER_SERIAL_THREAD. + */ + result = ((_LONG) serial == InterlockedCompareExchange((_LPLONG) &(b->nSerial), + (_LONG) ((serial + 1) + & 0x7FFFFFFF), + (_LONG) serial) + ? PTHREAD_BARRIER_SERIAL_THREAD + : 0); } return(result); diff --git a/implement.h b/implement.h index 5b30d1a..bc72b3f 100644 --- a/implement.h +++ b/implement.h @@ -187,10 +187,12 @@ struct pthread_spinlock_t_ { }; struct pthread_barrier_t_ { - LONG nCurrentBarrierHeight; - LONG nInitialBarrierHeight; + unsigned int nCurrentBarrierHeight; + unsigned int nInitialBarrierHeight; + unsigned int nSerial; + int iStep; pthread_mutex_t mtxExclusiveAccess; - HANDLE eventBarrierBreeched; + sem_t semBarrierBreeched[2]; }; struct pthread_barrierattr_t_ { @@ -825,7 +825,7 @@ int pthread_spin_unlock (pthread_spinlock_t * lock); */ int pthread_barrier_init (pthread_barrier_t * barrier, const pthread_barrierattr_t * attr, - int count); + unsigned int count); int pthread_barrier_destroy (pthread_barrier_t * barrier); diff --git a/tests/barrier5.c b/tests/barrier5.c index 6576b05..77f51da 100644 --- a/tests/barrier5.c +++ b/tests/barrier5.c @@ -10,13 +10,13 @@ enum { NUMTHREADS = 16, - ITERATIONS = 10000 + BARRIERS = 10000 }; pthread_barrier_t barrier = NULL; pthread_mutex_t mx = PTHREAD_MUTEX_INITIALIZER; -int barrierReleases[ITERATIONS + 1]; +int barrierReleases[BARRIERS + 1]; void * func(void * barrierHeight) @@ -24,7 +24,7 @@ func(void * barrierHeight) int i; int result; - for (i = 1; i < ITERATIONS; i++) + for (i = 1; i < BARRIERS; i++) { result = pthread_barrier_wait(&barrier); @@ -38,12 +38,8 @@ func(void * barrierHeight) */ if (result == PTHREAD_BARRIER_SERIAL_THREAD) { - assert(pthread_mutex_lock(&mx) == 0); -//printf("Releases bucket %d = %d\n", i - 1, barrierReleases[i - 1]); -//fflush(stdout); assert(barrierReleases[i - 1] == (int) barrierHeight); barrierReleases[i + 1] = 0; - assert(pthread_mutex_unlock(&mx) == 0); } else if (result != 0) { @@ -81,8 +77,8 @@ main() assert(pthread_join(t[i], NULL) == 0); } - assert(barrierReleases[ITERATIONS - 1] == j); - assert(barrierReleases[ITERATIONS] == 0); + assert(barrierReleases[BARRIERS - 1] == j); + assert(barrierReleases[BARRIERS] == 0); assert(pthread_barrier_destroy(&barrier) == 0); } |