diff options
| -rw-r--r-- | ChangeLog | 22 | ||||
| -rw-r--r-- | Nmakefile.tests | 18 | ||||
| -rw-r--r-- | attr.c | 2 | ||||
| -rw-r--r-- | barrier.c | 69 | ||||
| -rw-r--r-- | cancel.c | 2 | ||||
| -rw-r--r-- | condvar.c | 4 | ||||
| -rw-r--r-- | implement.h | 2 | ||||
| -rw-r--r-- | misc.c | 2 | ||||
| -rw-r--r-- | private.c | 4 | ||||
| -rw-r--r-- | tests/ChangeLog | 11 | ||||
| -rw-r--r-- | tests/GNUmakefile | 3 | ||||
| -rw-r--r-- | tests/Makefile | 3 | ||||
| -rw-r--r-- | tests/barrier3.c | 2 | ||||
| -rw-r--r-- | tests/barrier4.c | 12 | ||||
| -rw-r--r-- | tests/barrier5.c | 93 | ||||
| -rw-r--r-- | tests/spin4.c | 2 | ||||
| -rw-r--r-- | tests/test.h | 2 | 
17 files changed, 215 insertions, 38 deletions
| @@ -1,3 +1,25 @@ +2001-07-09  Ross Johnson  <rpj@setup1.ise.canberra.edu.au>
 +
 +	* barrier.c: Fix several bugs in all routines. Now passes
 +	tests/barrier5.c which is fairly rigorous. There is still
 +	a non-optimal work-around for a race condition between
 +	the barrier breeched event signal and event wait. Basically
 +	the last (signalling) thread to hit the barrier yields
 +	to allow any other threads, which may have lost the race,
 +	to complete.
 +
 +2001-07-07  Ross Johnson  <rpj@setup1.ise.canberra.edu.au>
 +
 +	* barrier.c: Changed synchronisation mechanism to a
 +	Win32 manual reset Event and use PulseEvent to signal
 +	waiting threads. If the implementation continued to use
 +	a semaphore it would require a second semaphore and
 +	some management to use them alternately as barriers. A
 +	single semaphore allows threads to cascade from one barrier
 +	through the next, leaving some threads blocked at the first.
 +	* implement.h (pthread_barrier_t_): As per above.
 +	* general: Made a number of other routines inlinable.
 +
  2001-07-07  Ross Johnson  <rpj@setup1.ise.canberra.edu.au>
  	* spin.c: Revamped and working; included static initialiser.
 diff --git a/Nmakefile.tests b/Nmakefile.tests index 8e124ab..109a995 100644 --- a/Nmakefile.tests +++ b/Nmakefile.tests @@ -79,6 +79,15 @@ cleanup3::	cleanup3.c  priority1::     priority1.c
  priority2::     priority2.c
  inherit1::      inherit1.c
 +spin1::         spin1.c
 +spin2::         spin2.c
 +spin3::         spin3.c
 +spin4::         spin4.c
 +barrier1::      barrier1.c
 +barrier2::      barrier2.c
 +barrier3::      barrier3.c
 +barrier4::      barrier4.c
 +barrier5::      barrier5.c
  exception1::	exception1.c
  exception2::	exception2.c
  exception3::	exception3.c
 @@ -139,6 +148,15 @@ cleanup3	:test:	cleanup2  priority1       :test:  join1
  priority2       :test:  priority1
  inherit1        :test:  join1
 +spin1           :test:
 +spin2           :test:  spin1.c
 +spin3           :test:  spin2.c
 +spin4           :test:  spin3.c
 +barrier1        :test:
 +barrier2        :test:  barrier1.c
 +barrier3        :test:  barrier2.c
 +barrier4        :test:  barrier3.c
 +barrier5        :test:  barrier4.c
  benchtest1      :test:  mutex3
  benchtest2      :test:  benchtest1
  benchtest3      :test:  benchtest2
 @@ -31,7 +31,7 @@  #include "pthread.h"  #include "implement.h" -static int +static INLINE int  is_attr(const pthread_attr_t *attr)  {    /* Return 0 if the attr object is valid, non-zero otherwise. */ @@ -59,23 +59,26 @@ pthread_barrier_init(pthread_barrier_t * barrier,    result = pthread_mutex_init(&(b->mtxExclusiveAccess), NULL);    if (0 != result)      { -      goto FAIL0; -    } - -  result = sem_init(&(b->semBarrierBreeched), pshared, 0); -  if (0 != result) -    {        goto FAIL1;      } -  goto DONE; +  b->eventBarrierBreeched = CreateEvent(NULL,   /* Security attributes */ +                                        TRUE,   /* Manual reset        */ +                                        FALSE,  /* Initially signaled  */ +                                        NULL);  /* Name                */ - FAIL1: +  if (NULL != b->eventBarrierBreeched) +    { +      goto DONE; +    }    (void) pthread_mutex_destroy(&(b->mtxExclusiveAccess)); +  result = ENOMEM; - FAIL0: + FAIL1:    (void) free(b); +  b = NULL; + FAIL0:   DONE:    *barrier = b;    return(result); @@ -94,7 +97,9 @@ pthread_barrier_destroy(pthread_barrier_t *barrier)    b = *barrier; -  if (0 == pthread_mutex_trylock(&(b->mtxExclusiveAccess))) +  result = pthread_mutex_trylock(&(b->mtxExclusiveAccess)); + +  if (0 == result)      {        /*         * FIXME!!! @@ -105,16 +110,24 @@ pthread_barrier_destroy(pthread_barrier_t *barrier)         */        *barrier = NULL; -      (void) sem_destroy(&(b->semBarrierBreeched)); +      result = CloseHandle(b->eventBarrierBreeched);        (void) pthread_mutex_unlock(&(b->mtxExclusiveAccess)); -      (void) pthread_mutex_destroy(&(b->mtxExclusiveAccess)); -      (void) free(b); +      if (result == TRUE) +        { +          (void) pthread_mutex_destroy(&(b->mtxExclusiveAccess)); +          (void) free(b); +          result = 0; +        } +      else +        { +          *barrier = b; +          result = EINVAL; +        }      }    return(result);  } -  int  pthread_barrier_wait(pthread_barrier_t *barrier)  { @@ -136,8 +149,14 @@ pthread_barrier_wait(pthread_barrier_t *barrier)          {            b->nCurrentBarrierHeight = b->nInitialBarrierHeight;            (void) pthread_mutex_unlock(&(b->mtxExclusiveAccess)); -          (void) sem_post_multiple(&(b->semBarrierBreeched), -                                   b->nInitialBarrierHeight); +          /* +           * 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. +           */ +          Sleep(0); +          (void) PulseEvent(b->eventBarrierBreeched);            /*             * Would be better if the first thread to return             * from this routine got this value. On a single @@ -151,23 +170,25 @@ pthread_barrier_wait(pthread_barrier_t *barrier)            pthread_t self;            int oldCancelState; -          (void) pthread_mutex_unlock(&(b->mtxExclusiveAccess)); -            self = pthread_self(); - +             /*             * pthread_barrier_wait() is not a cancelation point -           * so temporarily prevent sem_wait() from being one. +           * so temporarily prevent pthreadCancelableWait() from being one.             */            if (self->cancelType == PTHREAD_CANCEL_DEFERRED)              {                pthread_setcancelstate(PTHREAD_CANCEL_DISABLE, &oldCancelState);              } -          if (0 != sem_wait(&(b->semBarrierBreeched))) -            { -              result = errno; -            } +          (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. +           */ +          result = pthreadCancelableWait(b->eventBarrierBreeched);            if (self->cancelType == PTHREAD_CANCEL_DEFERRED)              { @@ -60,7 +60,7 @@ ptw32_cancel_self(void)  /*   * ptw32_cancel_thread implements asynchronous cancellation.   */ -static void  +static INLINE void   ptw32_cancel_thread(pthread_t thread)  {    HANDLE threadH = thread->threadH; @@ -807,7 +807,7 @@ ptw32_cond_wait_cleanup(void * args)  }                               /* ptw32_cond_wait_cleanup */ -static int +static INLINE int  ptw32_cond_timedwait (pthread_cond_t * cond,                         pthread_mutex_t * mutex,                        const struct timespec *abstime) @@ -912,7 +912,7 @@ ptw32_cond_timedwait (pthread_cond_t * cond,  }                               /* ptw32_cond_timedwait */ -static int +static INLINE int  ptw32_cond_unblock (pthread_cond_t * cond,                       int unblockAll)       /* diff --git a/implement.h b/implement.h index 5a146c0..5b30d1a 100644 --- a/implement.h +++ b/implement.h @@ -189,8 +189,8 @@ struct pthread_spinlock_t_ {  struct pthread_barrier_t_ {    LONG nCurrentBarrierHeight;    LONG nInitialBarrierHeight; -  sem_t semBarrierBreeched;    pthread_mutex_t mtxExclusiveAccess; +  HANDLE eventBarrierBreeched;  };  struct pthread_barrierattr_t_ { @@ -265,7 +265,7 @@ pthread_getconcurrency(void)  }                                                                                -static int +static INLINE int  ptw32_cancelable_wait (HANDLE waitHandle, DWORD timeout)       /*        * ------------------------------------------------------------------- @@ -766,7 +766,7 @@ ptw32_callUserDestroyRoutines (pthread_t thread)  #define TIMESPEC_TO_FILETIME_OFFSET \            ( ((LONGLONG) 27111902 << 32) + (LONGLONG) 3577643008 ) -static void +static INLINE void  timespec_to_filetime(const struct timespec *ts, FILETIME *ft)       /*        * ------------------------------------------------------------------- @@ -780,7 +780,7 @@ timespec_to_filetime(const struct timespec *ts, FILETIME *ft)  	*(LONGLONG *)ft = ts->tv_sec * 10000000 + (ts->tv_nsec + 50) / 100 + TIMESPEC_TO_FILETIME_OFFSET;  } -static void +static INLINE void  filetime_to_timespec(const FILETIME *ft, struct timespec *ts)       /*        * ------------------------------------------------------------------- diff --git a/tests/ChangeLog b/tests/ChangeLog index 1c5d766..144454a 100644 --- a/tests/ChangeLog +++ b/tests/ChangeLog @@ -1,3 +1,14 @@ +2001-07-09  Ross Johnson  <rpj@setup1.ise.canberra.edu.au> + +	* barrier3.c: Fixed. +	* barrier4.c: Fixed. +	* barrier5.c: New; proves that all threads in the group +	reaching the barrier wait and then resume together. Repeats the test +	using groups of 1 to 16 threads. Each group of threads must negotiate +	a large number of barriers (10000). +	* spin4.c: Fixed. +	* test.h (error_string): Modified the success (0) value. +  2001-07-07  Ross Johnson  <rpj@setup1.ise.canberra.edu.au>  	* spin3.c: Changed test and fixed. diff --git a/tests/GNUmakefile b/tests/GNUmakefile index f005664..d62e45c 100644 --- a/tests/GNUmakefile +++ b/tests/GNUmakefile @@ -49,7 +49,7 @@ TESTS	= loadfree \  	  cleanup0 cleanup1 cleanup2 cleanup3 \  	  priority1 priority2 inherit1 \  	  spin1 spin2 spin3 spin4 \ -	  barrier1 barrier2 barrier3 barrier4 \ +	  barrier1 barrier2 barrier3 barrier4 barrier5 \  	  exception1 exception2 exception3  BENCHTESTS = \ @@ -96,6 +96,7 @@ barrier1.pass:  barrier2.pass: barrier1.pass  barrier3.pass: barrier2.pass  barrier4.pass: barrier3.pass +barrier5.pass: barrier4.pass  cancel1.pass: create1.pass  cancel2.pass: cancel1.pass  cancel2_1.pass: cancel2.pass diff --git a/tests/Makefile b/tests/Makefile index f3a4f91..dba30d3 100644 --- a/tests/Makefile +++ b/tests/Makefile @@ -58,7 +58,7 @@ PASSES= loadfree.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  \
 +	  barrier1.pass  barrier2.pass  barrier3.pass  barrier4.pass  barrier5.pass  \
  	  exception1.pass  exception2.pass  exception3.pass
  BENCHRESULTS = \
 @@ -151,6 +151,7 @@ barrier1.pass:  barrier2.pass: barrier1.pass
  barrier3.pass: barrier2.pass
  barrier4.pass: barrier3.pass
 +barrier5.pass: barrier4.pass
  cancel1.pass: create1.pass
  cancel2.pass: cancel1.pass
  cancel3.pass: context1.pass
 diff --git a/tests/barrier3.c b/tests/barrier3.c index 97f6dc2..1fea5a1 100644 --- a/tests/barrier3.c +++ b/tests/barrier3.c @@ -28,7 +28,7 @@ main()    assert(pthread_create(&t, NULL, func, NULL) == 0); -  assert(pthread_join(t, (void *) &result) == 0); +  assert(pthread_join(t, (void **) &result) == 0);    assert(result == PTHREAD_BARRIER_SERIAL_THREAD); diff --git a/tests/barrier4.c b/tests/barrier4.c index 8f33e85..dd40b79 100644 --- a/tests/barrier4.c +++ b/tests/barrier4.c @@ -23,14 +23,24 @@ func(void * arg)    int result = pthread_barrier_wait(&barrier);    assert(pthread_mutex_lock(&mx) == 0); + +//  printf("Barrier wait returned %d [%d]\n", result, WAIT_FAILED); +//  fflush(stdout); +    if (result == PTHREAD_BARRIER_SERIAL_THREAD)      {        serialThreadCount++;      } -  else +  else if (0 == result)      {        otherThreadCount++;      } +  else +    { +      printf("Barrier wait failed: error = %s\n", error_string[result]); +      fflush(stdout); +      return NULL; +    }    assert(pthread_mutex_unlock(&mx) == 0);    return NULL; diff --git a/tests/barrier5.c b/tests/barrier5.c new file mode 100644 index 0000000..6576b05 --- /dev/null +++ b/tests/barrier5.c @@ -0,0 +1,93 @@ +/* + * barrier5.c + * + * Declare a single barrier object, set up a sequence of + * barrier points to prove lockstepness, and then destroy it. + * + */ + +#include "test.h" + +enum { +  NUMTHREADS = 16, +  ITERATIONS = 10000 +}; +  +pthread_barrier_t barrier = NULL; +pthread_mutex_t mx = PTHREAD_MUTEX_INITIALIZER; + +int barrierReleases[ITERATIONS + 1]; + +void * +func(void * barrierHeight) +{ +  int i; +  int result; + +  for (i = 1; i < ITERATIONS; i++) +    { +      result = pthread_barrier_wait(&barrier); + +      assert(pthread_mutex_lock(&mx) == 0); +      barrierReleases[i]++; +      assert(pthread_mutex_unlock(&mx) == 0); +      /* +       * Confirm the correct number of releases from the previous +       * barrier. We can't do the current barrier yet because there may +       * still be threads waking up. +       */ +      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) +        { +          printf("Barrier failed: result = %s\n", error_string[result]); +          fflush(stdout); +          return NULL; +        } +    } + +  return NULL; +} + +int +main() +{ +  int i, j; +  pthread_t t[NUMTHREADS + 1]; + +  for (j = 1; j <= NUMTHREADS; j++) +    { +      printf("Barrier height = %d\n", j); + +      barrierReleases[0] = j; +      barrierReleases[1] = 0; + +      assert(pthread_barrier_init(&barrier, NULL, j) == 0); + +      for (i = 1; i <= j; i++) +        { +          assert(pthread_create(&t[i], NULL, func, (void *) j) == 0); +        } + +      for (i = 1; i <= j; i++) +        { +          assert(pthread_join(t[i], NULL) == 0); +        } + +      assert(barrierReleases[ITERATIONS - 1] == j); +      assert(barrierReleases[ITERATIONS] == 0); + +      assert(pthread_barrier_destroy(&barrier) == 0); +    } + +  assert(pthread_mutex_destroy(&mx) == 0); + +  return 0; +} diff --git a/tests/spin4.c b/tests/spin4.c index 5f04a27..8f73008 100644 --- a/tests/spin4.c +++ b/tests/spin4.c @@ -59,7 +59,7 @@ main()    assert(pthread_spin_unlock(&lock) == 0); -  assert(pthread_join(t, (void *) &result) == 0); +  assert(pthread_join(t, (void **) &result) == 0);    assert(result > 1000);    assert(pthread_spin_destroy(&lock) == 0); diff --git a/tests/test.h b/tests/test.h index 018b215..c5b565d 100644 --- a/tests/test.h +++ b/tests/test.h @@ -13,7 +13,7 @@  #include <stdio.h>  char * error_string[] = { -  "ZERO", +  "ZERO_or_EOK",    "EPERM",    "ENOFILE_or_ENOENT",    "ESRCH", | 
