diff options
| author | rpj <rpj> | 2001-07-05 15:19:22 +0000 | 
|---|---|---|
| committer | rpj <rpj> | 2001-07-05 15:19:22 +0000 | 
| commit | ec27b9c8303540de6b5a8ebefee114f3cdd436f0 (patch) | |
| tree | 965ddfd011b9deb0091d2930fd55ce250c54ed58 | |
| parent | 99e8ecc5759668fd3af379eaddd70b4ae50ecd7f (diff) | |
	* 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.
| -rw-r--r-- | ChangeLog | 9 | ||||
| -rw-r--r-- | barrier.c | 124 | ||||
| -rw-r--r-- | pthread.h | 5 | ||||
| -rw-r--r-- | tests/ChangeLog | 13 | ||||
| -rw-r--r-- | tests/GNUmakefile | 11 | ||||
| -rw-r--r-- | tests/Makefile | 10 | ||||
| -rw-r--r-- | tests/barrier1.c | 26 | ||||
| -rw-r--r-- | tests/barrier2.c | 23 | ||||
| -rw-r--r-- | tests/barrier3.c | 40 | ||||
| -rw-r--r-- | tests/barrier4.c | 64 | ||||
| -rw-r--r-- | tests/spin1.c | 31 | ||||
| -rw-r--r-- | tests/spin2.c | 43 | ||||
| -rw-r--r-- | tests/spin3.c | 40 | ||||
| -rw-r--r-- | tests/spin4.c | 65 | 
14 files changed, 388 insertions, 116 deletions
| @@ -1,5 +1,14 @@  2001-07-05  Ross Johnson  <rpj@setup1.ise.canberra.edu.au>
 +	* 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  <rpj@setup1.ise.canberra.edu.au>
 +
  	* spin.c: New module implementing spin locks.
  	* barrier.c: New module implementing barriers.
  	* pthread.h (_POSIX_SPIN_LOCKS): defined.
 @@ -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 */ - @@ -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  <rpj@special.ise.canberra.edu.au> + +	* 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  <rpj@special.ise.canberra.edu.au>  	* 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 <sys/timeb.h> +  +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; +} | 
