From ec27b9c8303540de6b5a8ebefee114f3cdd436f0 Mon Sep 17 00:00:00 2001 From: rpj Date: Thu, 5 Jul 2001 15:19:22 +0000 Subject: * barrier.c: Remove static initialisation - irrelevent for this object. * pthread.h (PTHREAD_BARRIER_INITIALIZER): Removed. * rwlock.c (pthread_rwlock_wrlock): This routine is not a cancelation point - disable deferred cancelation around call to pthread_cond_wait(). tests/ChangeLog: * spin1.c: New; testing spinlocks. * spin2.c: New; testing spinlocks. * spin3.c: New; testing spinlocks. * spin4.c: New; testing spinlocks. * barrier1.c: New; testing barriers. * barrier2.c: New; testing barriers. * barrier3.c: New; testing barriers. * barrier4.c: New; testing barriers. * GNUmakefile: Add new tests. * Makefile: Add new tests. --- ChangeLog | 9 ++++ barrier.c | 124 ++++++------------------------------------------------ pthread.h | 5 --- tests/ChangeLog | 13 ++++++ tests/GNUmakefile | 11 +++++ tests/Makefile | 10 +++++ tests/barrier1.c | 26 ++++++++++++ tests/barrier2.c | 23 ++++++++++ tests/barrier3.c | 40 ++++++++++++++++++ tests/barrier4.c | 64 ++++++++++++++++++++++++++++ tests/spin1.c | 31 ++++++++++++++ tests/spin2.c | 43 +++++++++++++++++++ tests/spin3.c | 40 ++++++++++++++++++ tests/spin4.c | 65 ++++++++++++++++++++++++++++ 14 files changed, 388 insertions(+), 116 deletions(-) create mode 100644 tests/barrier1.c create mode 100644 tests/barrier2.c create mode 100644 tests/barrier3.c create mode 100644 tests/barrier4.c create mode 100644 tests/spin1.c create mode 100644 tests/spin2.c create mode 100644 tests/spin3.c create mode 100644 tests/spin4.c diff --git a/ChangeLog b/ChangeLog index 767f7fc..1fc37d0 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,12 @@ +2001-07-05 Ross Johnson + + * barrier.c: Remove static initialisation - irrelevent + for this object. + * pthread.h (PTHREAD_BARRIER_INITIALIZER): Removed. + * rwlock.c (pthread_rwlock_wrlock): This routine is + not a cancelation point - disable deferred + cancelation around call to pthread_cond_wait(). + 2001-07-05 Ross Johnson * spin.c: New module implementing spin locks. diff --git a/barrier.c b/barrier.c index 4495e33..5e22109 100644 --- a/barrier.c +++ b/barrier.c @@ -27,62 +27,6 @@ #include "implement.h" -static int -ptw32_barrier_check_need_init(pthread_barrier_t *barrier) -{ - int result = 0; - - /* - * The following guarded test is specifically for statically - * initialised barriers (via PTHREAD_BARRIER_INITIALIZER). - * - * Note that by not providing this synchronisation we risk - * introducing race conditions into applications which are - * correctly written. - * - * Approach - * -------- - * We know that static barriers will not be PROCESS_SHARED - * so we can serialise access to internal state using - * Win32 Critical Sections rather than Win32 Mutexes. - * - * If using a single global lock slows applications down too much, - * multiple global locks could be created and hashed on some random - * value associated with each barrier, the pointer perhaps. At a guess, - * a good value for the optimal number of global locks might be - * the number of processors + 1. - * - */ - EnterCriticalSection(&ptw32_barrier_test_init_lock); - - /* - * We got here possibly under race - * conditions. Check again inside the critical section - * and only initialise if the barrier is valid (not been destroyed). - * If a static barrier has been destroyed, the application can - * re-initialise it only by calling pthread_barrier_init() - * explicitly. - */ - if (*barrier == (pthread_barrier_t) PTW32_OBJECT_AUTO_INIT) - { - result = pthread_barrier_init(barrier, NULL); - } - else if (*barrier == NULL) - { - /* - * The barrier has been destroyed while we were waiting to - * initialise it, so the operation that caused the - * auto-initialisation should fail. - */ - result = EINVAL; - } - - LeaveCriticalSection(&ptw32_barrier_test_init_lock); - - return(result); -} - - int pthread_barrier_init(pthread_barrier_t * barrier, const pthread_barrierattr_t * attr, @@ -147,57 +91,23 @@ pthread_barrier_destroy(pthread_barrier_t *barrier) return EINVAL; } - if (*barrier != (pthread_barrier_t) PTW32_OBJECT_AUTO_INIT) - { - b = *barrier; + b = *barrier; - if (0 == pthread_mutex_trylock(&b->mtxExclusiveAccess)) - { - /* - * FIXME!!! - * The mutex isn't held by another thread but we could still - * be too late invalidating the barrier below since another thread - * may alredy have entered barrier_wait and the check for a valid - * *barrier != NULL. - */ - *barrier = NULL; - - (void) sem_destroy(&(b->semBarrierBreeched)); - (void) pthread_mutex_unlock(&(b->mtxExclusiveAccess)); - (void) pthread_mutex_destroy(&(b->mtxExclusiveAccess)); - (void) free(b); - } - } - else + if (0 == pthread_mutex_trylock(&b->mtxExclusiveAccess)) { /* - * See notes in ptw32_barrier_check_need_init() above also. - */ - EnterCriticalSection(&ptw32_barrier_test_init_lock); - - /* - * Check again. + * FIXME!!! + * The mutex isn't held by another thread but we could still + * be too late invalidating the barrier below since another thread + * may alredy have entered barrier_wait and the check for a valid + * *barrier != NULL. */ - if (*barrier == (pthread_barrier_t) PTW32_OBJECT_AUTO_INIT) - { - /* - * This is all we need to do to destroy a statically - * initialised barrier that has not yet been used (initialised). - * If we get to here, another thread - * waiting to initialise this barrier will get an EINVAL. - */ - *barrier = NULL; - } - else - { - /* - * The barrier has been initialised while we were waiting - * so assume it's in use. - */ - result = EBUSY; - } + *barrier = NULL; - LeaveCriticalSection(&ptw32_barrier_test_init_lock); + (void) sem_destroy(&(b->semBarrierBreeched)); + (void) pthread_mutex_unlock(&(b->mtxExclusiveAccess)); + (void) pthread_mutex_destroy(&(b->mtxExclusiveAccess)); + (void) free(b); } return(result); @@ -215,17 +125,10 @@ pthread_barrier_wait(pthread_barrier_t *barrier) return EINVAL; } - if (*barrier == (pthread_barrier_t) PTHREAD_OBJECT_AUTO_INIT) - { - if ((result = ptw32_barrier_check_need_init(barrier)) != 0) - { - return(result); - } - } - b = *barrier; result = pthread_mutex_lock(b->mtxExclusiveAccess); + if (0 == result) { if (0 == --(b->nCurrentBarrierHeight)) @@ -514,4 +417,3 @@ pthread_barrierattr_setpshared (pthread_barrierattr_t * attr, return (result); } /* pthread_barrierattr_setpshared */ - diff --git a/pthread.h b/pthread.h index ca36b8b..f6d02fe 100644 --- a/pthread.h +++ b/pthread.h @@ -458,7 +458,6 @@ struct pthread_once_t_ #define PTHREAD_SPINLOCK_INITIALIZER {1} -#define PTHREAD_BARRIER_INITIALIZER ((pthread_barrier_t) -1) enum { @@ -477,10 +476,6 @@ enum * compilation units and also internally for the library. * The code here and within the library aims to work * for all reasonable combinations of environments. - * For example, although the library itself can't be - * built (yet) in C, an application written in C can - * be linked and run against a library built using - * either WIN32 SEH or C++ EH. * * The three implementations are: * diff --git a/tests/ChangeLog b/tests/ChangeLog index f99d1be..565f0ad 100644 --- a/tests/ChangeLog +++ b/tests/ChangeLog @@ -1,3 +1,16 @@ +2001-07-05 Ross Johnson + + * spin1.c: New; testing spinlocks. + * spin2.c: New; testing spinlocks. + * spin3.c: New; testing spinlocks. + * spin4.c: New; testing spinlocks. + * barrier1.c: New; testing barriers. + * barrier2.c: New; testing barriers. + * barrier3.c: New; testing barriers. + * barrier4.c: New; testing barriers. + * GNUmakefile: Add new tests. + * Makefile: Add new tests. + 2001-07-01 Ross Johnson * benchtest3.c: New; timing mutexes. diff --git a/tests/GNUmakefile b/tests/GNUmakefile index b952eed..f005664 100644 --- a/tests/GNUmakefile +++ b/tests/GNUmakefile @@ -48,6 +48,8 @@ TESTS = loadfree \ context1 cancel3 cancel4 cancel5 \ cleanup0 cleanup1 cleanup2 cleanup3 \ priority1 priority2 inherit1 \ + spin1 spin2 spin3 spin4 \ + barrier1 barrier2 barrier3 barrier4 \ exception1 exception2 exception3 BENCHTESTS = \ @@ -89,6 +91,11 @@ benchtest1.bench: benchtest2.bench: benchtest3.bench: benchtest4.bench: + +barrier1.pass: +barrier2.pass: barrier1.pass +barrier3.pass: barrier2.pass +barrier4.pass: barrier3.pass cancel1.pass: create1.pass cancel2.pass: cancel1.pass cancel2_1.pass: cancel2.pass @@ -149,6 +156,10 @@ rwlock6.pass: rwlock5.pass rwlock7.pass: rwlock6.pass self1.pass: self2.pass: create1.pass +spin1.pass: +spin2.pass: spin1.pass +spin3.pass: spin2.pass +spin4.pass: spin3.pass tsd1.pass: join1.pass #%.pass: %.exe $(HDR) diff --git a/tests/Makefile b/tests/Makefile index 6ac342d..f3a4f91 100644 --- a/tests/Makefile +++ b/tests/Makefile @@ -57,6 +57,8 @@ PASSES= loadfree.pass \ cancel3.pass cancel4.pass cancel5.pass \ cleanup0.pass cleanup1.pass cleanup2.pass cleanup3.pass \ priority1.pass priority2.pass inherit1.pass \ + spin1.pass spin2.pass spin3.pass spin4.pass \ + barrier1.pass barrier2.pass barrier3.pass barrier4.pass \ exception1.pass exception2.pass exception3.pass BENCHRESULTS = \ @@ -145,6 +147,10 @@ benchtest1.bench: benchtest2.bench: benchtest3.bench: benchtest4.bench: +barrier1.pass: +barrier2.pass: barrier1.pass +barrier3.pass: barrier2.pass +barrier4.pass: barrier3.pass cancel1.pass: create1.pass cancel2.pass: cancel1.pass cancel3.pass: context1.pass @@ -204,4 +210,8 @@ rwlock6.pass: rwlock5.pass rwlock7.pass: rwlock6.pass self1.pass: self2.pass: create1.pass +spin1.pass: +spin2.pass: spin1.pass +spin3.pass: spin2.pass +spin4.pass: spin3.pass tsd1.pass: join1.pass diff --git a/tests/barrier1.c b/tests/barrier1.c new file mode 100644 index 0000000..bc0eb52 --- /dev/null +++ b/tests/barrier1.c @@ -0,0 +1,26 @@ +/* + * barrier1.c + * + * Create a barrier object and then destroy it. + * + */ + +#include "test.h" + +pthread_barrier_t barrier = NULL; + +int +main() +{ + assert(barrier == NULL); + + assert(pthread_barrier_init(&barrier, NULL, 1) == 0); + + assert(barrier != NULL); + + assert(pthread_barrier_destroy(&barrier) == 0); + + assert(barrier == NULL); + + return 0; +} diff --git a/tests/barrier2.c b/tests/barrier2.c new file mode 100644 index 0000000..c7b174c --- /dev/null +++ b/tests/barrier2.c @@ -0,0 +1,23 @@ +/* + * barrier2.c + * + * Declare a single barrier object, wait on it, + * and then destroy it. + * + */ + +#include "test.h" + +pthread_barrier_t barrier = NULL; + +int +main() +{ + assert(pthread_barrier_init(&barrier, NULL, 1) == 0); + + assert(pthread_barrier_wait(&barrier) == PTHREAD_BARRIER_SERIAL_THREAD); + + assert(pthread_barrier_destroy(&barrier) == 0); + + return 0; +} diff --git a/tests/barrier3.c b/tests/barrier3.c new file mode 100644 index 0000000..497b76a --- /dev/null +++ b/tests/barrier3.c @@ -0,0 +1,40 @@ +/* + * barrier3.c + * + * Declare a single barrier object, multiple wait on it, + * and then destroy it. + * + */ + +#include "test.h" + +pthread_barrier_t barrier = NULL; +static int result1 = -1; +static int result2 = -1; + +void * func(void * arg) +{ + return (void *) pthread_barrier_wait(&barrier); +} + +int +main() +{ + pthread_t t; + + assert(pthread_barrier_init(&barrier, NULL, 2) == 0); + + assert(pthread_create(&t, NULL, func, NULL) == 0); + + result1 = pthread_barrier_wait(&barrier); + + assert(pthread_join(t, &result2) == 0); + + assert(result1 != result2); + assert(result1 == 0 || result1 == PTHREAD_BARRIER_SERIAL_THREAD); + assert(result2 == 0 || result2 == PTHREAD_BARRIER_SERIAL_THREAD); + + assert(pthread_barrier_destroy(&barrier) == 0); + + return 0; +} diff --git a/tests/barrier4.c b/tests/barrier4.c new file mode 100644 index 0000000..1dd8291 --- /dev/null +++ b/tests/barrier4.c @@ -0,0 +1,64 @@ +/* + * barrier4.c + * + * Declare a single barrier object, multiple wait on it, + * and then destroy it. + * + */ + +#include "test.h" + +enum { + NUMTHREADS = 16 +}; + +pthread_barrier_t barrier = NULL; +pthread_mutex_t mx = PTHREAD_MUTEX_INITIALIZER; +static int result1 = -1; +static int result2 = -1; +static int serialThreadCount = 0; +static int otherThreadCount = 0; + +void * func(void * arg) +{ + int result = pthread_barrier_wait(&barrier); + + assert(pthread_mutex_lock(&mx) == 0); + if (result == PTHREAD_BARRIER_SERIAL_THREAD) + { + serialThreadCount++; + } + else + { + otherThreadCount++; + } + assert(pthread_mutex_lock(&mx) == 0); + + return NULL; +} + +int +main() +{ + pthread_t t[NUMTHREADS + 1]; + + assert(pthread_barrier_init(&barrier, NULL, NUMTHREADS) == 0); + + for (i = 0; i < NUMTHREADS; i++) + { + assert(pthread_create(&t[i], NULL, func, NULL) == 0); + } + + for (i = 0; i < NUMTHREADS; i++) + { + assert(pthread_join(t[i], NULL) == 0); + } + + assert(serialThreadCount == 1); + + assert(pthread_barrier_destroy(&barrier) == 0); + + assert(pthread_mutex_destroy(&mx) == 0); + + return 0; +} diff --git a/tests/spin1.c b/tests/spin1.c new file mode 100644 index 0000000..e9e5731 --- /dev/null +++ b/tests/spin1.c @@ -0,0 +1,31 @@ +/* + * spin1.c + * + * Create a simple spinlock object, lock it, and then unlock it again. + * This is the simplest test of the pthread mutex family that we can do. + * + */ + +#include "test.h" + +pthread_spinlock_t lock = NULL; + +int +main() +{ + assert(lock == NULL); + + assert(pthread_spin_init(&lock, PTHREAD_PROCESS_PRIVATE) == 0); + + assert(lock != NULL); + + assert(pthread_spin_lock(&lock) == 0); + + assert(pthread_spin_unlock(&lock) == 0); + + assert(pthread_spin_destroy(&lock) == 0); + + assert(lock == NULL); + + return 0; +} diff --git a/tests/spin2.c b/tests/spin2.c new file mode 100644 index 0000000..4a6a2bb --- /dev/null +++ b/tests/spin2.c @@ -0,0 +1,43 @@ +/* + * spin2.c + * + * Declare a spinlock object, lock it, trylock it, + * and then unlock it again. + * + */ + +#include "test.h" + +pthread_spinlock_t lock = NULL; + +static int washere = 0; + +void * func(void * arg) +{ + assert(pthread_spin_trylock(&lock) == EBUSY); + + washere = 1; + + return 0; +} + +int +main() +{ + pthread_t t; + + assert(pthread_spin_init(&lock, PTHREAD_PROCESS_PRIVATE) == 0); + + assert(pthread_spin_lock(&lock) == 0); + + assert(pthread_create(&t, NULL, func, NULL) == 0); + assert(pthread_join(t, NULL) == 0); + + assert(pthread_spin_unlock(&lock) == 0); + + assert(pthread_spin_destroy(&lock) == 0); + + assert(washere == 1); + + return 0; +} diff --git a/tests/spin3.c b/tests/spin3.c new file mode 100644 index 0000000..8b383de --- /dev/null +++ b/tests/spin3.c @@ -0,0 +1,40 @@ +/* + * spin3.c + * + * Thread A locks spin - thread B tries to unlock. + * This should succeed, but it's undefined behaviour. + * + */ + +#include "test.h" + +static int wasHere = 0; + +static pthread_spinlock_t spin; + +void * unlocker(void * arg) +{ + int expectedResult = (int) arg; + + wasHere++; + assert(pthread_spin_unlock(&spin) == expectedResult); + wasHere++; + return NULL; +} + +int +main() +{ + pthread_t t; + pthread_spinattr_t ma; + + wasHere = 0; + assert(pthread_spin_init(&spin, PTHREAD_PROCESS_PRIVATE) == 0); + assert(pthread_spin_lock(&spin) == 0); + assert(pthread_create(&t, NULL, unlocker, (void *) 0) == 0); + assert(pthread_join(t, NULL) == 0); + assert(pthread_spin_unlock(&spin) == EPERM); + assert(wasHere == 2); + + return 0; +} diff --git a/tests/spin4.c b/tests/spin4.c new file mode 100644 index 0000000..a435d04 --- /dev/null +++ b/tests/spin4.c @@ -0,0 +1,65 @@ +/* + * spin4.c + * + * Declare a spinlock object, lock it, spin on it, + * and then unlock it again. + * + * For this to work on a single processor machine we have + * to static initialise the spinlock. This bypasses the + * check of the number of processors done by pthread_spin_init. + * This is a non-portable side-effect of this implementation. + */ + +#include "test.h" +#include + +pthread_spinlock_t lock = PTHREADS_SPINLOCK_INITIALIZER; +struct _timeb currSysTimeStart; +struct _timeb currSysTimeStop; + +#define GetDurationMilliSecs(_TStart, _TStop) ((_TStop.time*1000+_TStop.millitm) \ + - (_TStart.time*1000+_TStart.millitm)) + +static int washere = 0; + +void * func(void * arg) +{ + _ftime(&currSysTimeStart); + assert(pthread_spin_lock(&lock) == 0); + assert(pthread_spin_unlock(&lock) == 0); + _ftime(&currSysTimeStop); + washere = 1; + + return (void *) GetDurationMilliSecs(currSysTimeStart, currSysTimeStop); +} + +int +main() +{ + long result = 0; + pthread_t t; + + assert(pthread_spin_lock(&lock) == 0); + + assert(pthread_create(&t, NULL, func, NULL) == 0); + + /* + * This should relinqish the CPU to the func thread enough times + * to waste approximately 2000 millisecs only if the lock really + * is spinning in the func thread (assuming 10 millisec CPU quantum). + for (i = 0; i < 200; i++) + { + sched_yield(); + } + + assert(pthread_spin_unlock(&lock) == 0); + + assert(pthread_join(t, (void *) &result) == 0); + assert(result > 1000); + + assert(pthread_spin_destroy(&lock) == 0); + + assert(washere == 1); + + return 0; +} -- cgit v1.2.3