diff options
| -rw-r--r-- | tests/Bmakefile | 5 | ||||
| -rw-r--r-- | tests/GNUmakefile | 22 | ||||
| -rw-r--r-- | tests/Makefile | 66 | ||||
| -rw-r--r-- | tests/README.BENCHTESTS | 37 | ||||
| -rw-r--r-- | tests/stress1.c | 271 | 
5 files changed, 351 insertions, 50 deletions
| diff --git a/tests/Bmakefile b/tests/Bmakefile index 2e3d48b..ff8c95b 100644 --- a/tests/Bmakefile +++ b/tests/Bmakefile @@ -110,7 +110,7 @@ PASSES=   loadfree.pass \  	  spin1.pass  spin2.pass  spin3.pass  spin4.pass  \  	  barrier1.pass  barrier2.pass  barrier3.pass  barrier4.pass  barrier5.pass  \  	  exception1.pass  exception2.pass  exception3.pass  \ -	  cancel9.pass create3.pass +	  cancel9.pass  create3.pass  stress1.pass  BENCHRESULTS = \  	  benchtest1.bench benchtest2.bench benchtest3.bench benchtest4.bench benchtest5.bench @@ -234,6 +234,7 @@ cancel6a.pass: cancel3.pass  cancel6d.pass: cancel3.pass  cancel7.pass: kill1.pass  cancel8.pass: cancel7.pass +cancel9.pass: cancel8.pass  cleanup0.pass: cancel5.pass  cleanup1.pass: cleanup0.pass  cleanup2.pass: cleanup1.pass @@ -339,7 +340,7 @@ spin1.pass:  spin2.pass: spin1.pass  spin3.pass: spin2.pass  spin4.pass: spin3.pass +stress1.pass: barrier5.pass  tsd1.pass: join1.pass  valid1.pass: join1.pass  valid2.pass: valid1.pass -cancel9.pass: cancel8.pass diff --git a/tests/GNUmakefile b/tests/GNUmakefile index 8700572..fc36c32 100644 --- a/tests/GNUmakefile +++ b/tests/GNUmakefile @@ -104,6 +104,9 @@ TESTS	= sizes loadfree \  	  exception1 exception2 exception3 \  	  cancel9 create3 +STRESSTESTS = \ +	stress1 +  BENCHTESTS = \  	benchtest1 benchtest2 benchtest3 benchtest4 benchtest5 @@ -112,6 +115,7 @@ STATICTESTS = \  PASSES		= $(TESTS:%=%.pass)  BENCHRESULTS	= $(BENCHTESTS:%=%.bench) +STRESSRESULTS	= $(STRESSTESTS:%=%.pass)  STATICRESULTS	= $(STATICTESTS:%=%.pass)  help: @@ -121,6 +125,8 @@ help:  	@ $(ECHO) "make clean GCE   (to test using GCE dll with C++ (EH) applications)"  	@ $(ECHO) "make clean GC-bench	  (to benchtest using GNU C dll with C cleanup code)"  	@ $(ECHO) "make clean GCE-bench   (to benchtest using GNU C dll with C++ exception handling)" +	@ $(ECHO) "make clean GC-stress	  (to stresstest using GNU C dll with C cleanup code)" +	@ $(ECHO) "make clean GCE-stress   (to stresstest using GNU C dll with C++ exception handling)"  	@ $(ECHO) "make clean GC-static   (to test using GC static lib with C (no EH) applications)"  all: @@ -146,12 +152,24 @@ GCE-bench:  GC-static:  	$(MAKE) TEST=GC CC=gcc XXCFLAGS="-D__CLEANUP_C -DPTW32_STATIC_LIB" DLL="" all-static +GC-stress: +	$(ECHO) Stress tests can take a long time since they are trying to +	$(ECHO) expose weaknesses that may be intermittant or statistically rare. +	$(ECHO) A pass does not prove correctness, but may give greater confidence. +	$(MAKE) TEST=GC CC=gcc XXCFLAGS="-D__CLEANUP_C" all-stress + +GCE-stress: +	$(MAKE) TEST=GCE  CC=g++ XXCFLAGS="-mthreads -D__CLEANUP_CXX" all-stress +  all-pass: $(PASSES)  	@ $(ECHO) ALL TESTS PASSED! Congratulations!  all-bench: $(BENCHRESULTS)  	@ $(ECHO) BENCH TESTS COMPLETED. +all-stress: $(STRESSRESULTS) +	@ $(ECHO) STRESS TESTS COMPLETED. +  all-static: $(STATICRESULTS)  	@ $(ECHO) ALL STATIC TESTS PASSED! Congratulations!  	@ $(ECHO) Build and test the DLL to run all tests. @@ -163,6 +181,8 @@ benchtest3.bench:  benchtest4.bench:  benchtest5.bench: +stress1.pass: +  barrier1.pass:  barrier2.pass: barrier1.pass  barrier3.pass: barrier2.pass @@ -178,6 +198,7 @@ cancel6a.pass: cancel3.pass  cancel6d.pass: cancel3.pass  cancel7.pass: kill1.pass  cancel8.pass: cancel7.pass +cancel9.pass: cancel8.pass  cleanup0.pass: cancel5.pass  cleanup1.pass: cleanup0.pass  cleanup2.pass: cleanup1.pass @@ -286,7 +307,6 @@ spin4.pass: spin3.pass  tsd1.pass: join1.pass  valid1.pass: join1.pass  valid2.pass: valid1.pass -cancel9.pass: cancel8.pass  sizes.pass: sizes.exe  	@ $(ECHO) Running $* diff --git a/tests/Makefile b/tests/Makefile index 6a82a58..f227914 100644 --- a/tests/Makefile +++ b/tests/Makefile @@ -115,25 +115,32 @@ PASSES= sizes.pass  loadfree.pass \  	  spin1.pass  spin2.pass  spin3.pass  spin4.pass  \  	  barrier1.pass  barrier2.pass  barrier3.pass  barrier4.pass  barrier5.pass  \  	  exception1.pass  exception2.pass  exception3.pass  \ -	  cancel9.pass create3.pass +	  cancel9.pass  create3.pass  BENCHRESULTS = \  	  benchtest1.bench benchtest2.bench benchtest3.bench benchtest4.bench benchtest5.bench +STRESSRESULTS = \ +	  stress1.pass +  STATICRESULTS = \  	  self1.pass  help:  	@ $(ECHO) Run one of the following command lines: -	@ $(ECHO) nmake clean VC    (to test using VC dll with VC (no EH) applications) -	@ $(ECHO) nmake clean VCX   (to test using VC dll with VC++ (EH) applications) -	@ $(ECHO) nmake clean VCE   (to test using the VCE dll with VC++ EH applications) -	@ $(ECHO) nmake clean VSE   (to test using VSE dll with VC (SEH) applications) -	@ $(ECHO) nmake clean VC-bench    (to benchtest using VC dll with C bench app) -	@ $(ECHO) nmake clean VCX-bench   (to benchtest using VC dll with C++ bench app) -	@ $(ECHO) nmake clean VCE-bench   (to benchtest using VCE dll with C++ bench app) -	@ $(ECHO) nmake clean VSE-bench   (to benchtest using VSE dll with SEH bench app) -	@ $(ECHO) nmake clean VC-static   (to test using VC static lib with VC (no EH) applications) +	@ $(ECHO) nmake clean VC          (to test using VC dll with VC (no EH) apps) +	@ $(ECHO) nmake clean VC-bench    (to benchtest using VC dll with C bench apps) +	@ $(ECHO) nmake clean VC-stress   (to stresstest using VC dll with C stress apps) +	@ $(ECHO) nmake clean VC-static   (to test using VC static lib with VC (no EH) apps) +	@ $(ECHO) nmake clean VCX         (to test using VC dll with VC++ (EH) applications) +	@ $(ECHO) nmake clean VCX-bench   (to benchtest using VC dll with C++ bench apps) +	@ $(ECHO) nmake clean VCX-stress  (to stresstest using VC dll with C++ stress apps) +	@ $(ECHO) nmake clean VCE         (to test using the VCE dll with VC++ EH applications) +	@ $(ECHO) nmake clean VCE-bench   (to benchtest using VCE dll with C++ bench apps) +	@ $(ECHO) nmake clean VCE-stress  (to stresstest using VCE dll with C++ stress apps) +	@ $(ECHO) nmake clean VSE         (to test using VSE dll with VC (SEH) apps) +	@ $(ECHO) nmake clean VSE-bench   (to benchtest using VSE dll with SEH bench apps) +	@ $(ECHO) nmake clean VSE-stress  (to stresstest using VSE dll with SEH stress apps)  all:  	@ nmake clean VC @@ -141,6 +148,7 @@ all:  	@ nmake clean VCE  	@ nmake clean VSE  	@ nmake clean VC-bench +	@ nmake clean VC-stress  # This allows an individual test application to be made using the default lib.  # e.g. nmake clean test cancel3.exe @@ -152,6 +160,9 @@ tests: $(CPLIB) $(CPDLL) $(CPHDR) $(QAPC) $(PASSES)  benchtests: $(CPLIB) $(CPDLL) $(CPHDR) $(XXLIBS) $(BENCHRESULTS)  	@ $(ECHO) ALL BENCH TESTS DONE. +stresstests: $(CPLIB) $(CPDLL) $(CPHDR) $(STRESSRESULTS) +	@ $(ECHO) ALL STRESS TESTS DONE. +  statictests: $(CPLIB) $(CPDLL) $(CPHDR) $(STATICRESULTS)  	@ $(ECHO) ALL STATIC TESTS DONE.  	@ $(ECHO) Build and test the DLL to run all tests. @@ -176,30 +187,48 @@ $(BENCHRESULTS): $*.exe  	@ $(ECHO) ...... Done  	@ $(TOUCH) $*.bench +$(STRESSRESULTS): $*.exe +	@ $(ECHO) ... Running $(TEST) stresstest: $*.exe +	@ .\$*.exe +	@ $(ECHO) ...... Done +	@ $(TOUCH) $*.pass + +VC: +	@ nmake TEST="$@" CPLIB="$(VCLIB)" CPDLL="$(VCDLL)" EHFLAGS="$(VCFLAGS)" tests +  VCE:  	@ nmake TEST="$@" CPLIB="$(VCELIB)" CPDLL="$(VCEDLL)" EHFLAGS="$(VCEFLAGS)" tests  VSE:	  	@ nmake TEST="$@" CPLIB="$(VSELIB)" CPDLL="$(VSEDLL)" EHFLAGS="$(VSEFLAGS)" tests -VC: -	@ nmake TEST="$@" CPLIB="$(VCLIB)" CPDLL="$(VCDLL)" EHFLAGS="$(VCFLAGS)" tests -  VCX:  	@ nmake TEST="$@" CPLIB="$(VCLIB)" CPDLL="$(VCDLL)" EHFLAGS="$(VCXFLAGS)" tests +VC-bench: +	@ nmake TEST="$@" CPLIB="$(VCLIB)" CPDLL="$(VCDLL)" EHFLAGS="$(VCFLAGS)" XXLIBS="benchlib.o" benchtests +  VCE-bench:  	@ nmake TEST="$@" CPLIB="$(VCELIB)" CPDLL="$(VCEDLL)" EHFLAGS="$(VCEFLAGS)" XXLIBS="benchlib.o" benchtests  VSE-bench:  	@ nmake TEST="$@" CPLIB="$(VSELIB)" CPDLL="$(VSEDLL)" EHFLAGS="$(VSEFLAGS)" XXLIBS="benchlib.o" benchtests -VC-bench: -	@ nmake TEST="$@" CPLIB="$(VCLIB)" CPDLL="$(VCDLL)" EHFLAGS="$(VCFLAGS)" XXLIBS="benchlib.o" benchtests -  VCX-bench:  	@ nmake TEST="$@" CPLIB="$(VCLIB)" CPDLL="$(VCDLL)" EHFLAGS="$(VCXFLAGS)" XXLIBS="benchlib.o" benchtests +VC-stress: +	@ nmake TEST="$@" CPLIB="$(VCLIB)" CPDLL="$(VCDLL)" EHFLAGS="$(VCFLAGS)" stresstests + +VCE-stress: +	@ nmake TEST="$@" CPLIB="$(VCELIB)" CPDLL="$(VCEDLL)" EHFLAGS="$(VCEFLAGS)" stresstests + +VSE-stress: +	@ nmake TEST="$@" CPLIB="$(VSELIB)" CPDLL="$(VSEDLL)" EHFLAGS="$(VSEFLAGS)" stresstests + +VCX-stress: +	@ nmake TEST="$@" CPLIB="$(VCLIB)" CPDLL="$(VCDLL)" EHFLAGS="$(VCXFLAGS)" stresstests +  VC-static:  	@ nmake TEST="$@" CPLIB="$(VCLIB)" CPDLL="" EHFLAGS="$(VCFLAGS) /DPTW32_STATIC_LIB" statictests @@ -244,6 +273,9 @@ benchtest2.bench:  benchtest3.bench:  benchtest4.bench:  benchtest5.bench: + +stress1.pass: +  barrier1.pass:  barrier2.pass: barrier1.pass  barrier3.pass: barrier2.pass @@ -258,6 +290,7 @@ cancel6a.pass: cancel3.pass  cancel6d.pass: cancel3.pass  cancel7.pass: kill1.pass  cancel8.pass: cancel7.pass +cancel9.pass: cancel8.pass  cleanup0.pass: cancel5.pass  cleanup1.pass: cleanup0.pass  cleanup2.pass: cleanup1.pass @@ -366,4 +399,3 @@ spin4.pass: spin3.pass  tsd1.pass: join1.pass  valid1.pass: join1.pass  valid2.pass: valid1.pass -cancel9.pass: cancel8.pass diff --git a/tests/README.BENCHTESTS b/tests/README.BENCHTESTS index e02cb3e..448570c 100644 --- a/tests/README.BENCHTESTS +++ b/tests/README.BENCHTESTS @@ -2,7 +2,7 @@  ------------  Benchmarking  ------------ -There is a new but growing set a benchmarking programs in the +There is a set a benchmarking programs in the  "tests" directory. These should be runnable using the  following command-lines corresponding to each of the possible  library builds: @@ -62,36 +62,13 @@ irrespective of the Windows variant, and should therefore  have consistent performance. +Semaphore benchtests +-------------------- + +benchtest5 - Timing for various uncontended cases. + +  In all benchtests, the operation is repeated a large  number of times and an average is calculated. Loop  overhead is measured and subtracted from all test times. -Comment on the results ----------------------- -The gain in performance for Win9x systems is enormous - up to -40 times faster for unlocked mutexes (2 times faster for locked -mutexes). - -Pthread_mutex_trylock also appears to be faster for locked mutexes. - -The price for the new consistency between WinNT and Win9x is -slower performance (up to twice as long) across a lock/unlock -sequence. It is difficult to get a good split timing for lock -and unlock operations, but by code inspection, it is the unlock -operation that is slowing the pair down in comparison with the -old-style CS mutexes, even for the fast PTHREAD_MUTEX_NORMAL mutex -type with no other waiting threads. However, comparitive -performance for operations on already locked mutexes is very close. - -When this is translated to real-world applications, the overall -camparitive performance should be almost identical on NT class -systems. That is, applications with heavy mutex contention should -have almost equal performance, while applications with only light -mutex contention should also have almost equal performance because -the most critical operation in this case is the lock operation. - -Overall, the newer pthreads-win32 mutex routines are only slower -(on NT class systems) where and when it is least critical. - -Thanks go to Thomas Pfaff for the current implementation of mutex -routines. diff --git a/tests/stress1.c b/tests/stress1.c new file mode 100644 index 0000000..b8d1c77 --- /dev/null +++ b/tests/stress1.c @@ -0,0 +1,271 @@ +/* + * stress1.c + * + * + * -------------------------------------------------------------------------- + * + *      Pthreads-win32 - POSIX Threads Library for Win32 + *      Copyright(C) 1998 John E. Bossom + *      Copyright(C) 1999,2005 Pthreads-win32 contributors + *  + *      Contact Email: rpj@callisto.canberra.edu.au + *  + *      The current list of contributors is contained + *      in the file CONTRIBUTORS included with the source + *      code distribution. The list can also be seen at the + *      following World Wide Web location: + *      http://sources.redhat.com/pthreads-win32/contributors.html + *  + *      This library is free software; you can redistribute it and/or + *      modify it under the terms of the GNU Lesser General Public + *      License as published by the Free Software Foundation; either + *      version 2 of the License, or (at your option) any later version. + *  + *      This library is distributed in the hope that it will be useful, + *      but WITHOUT ANY WARRANTY; without even the implied warranty of + *      MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU + *      Lesser General Public License for more details. + *  + *      You should have received a copy of the GNU Lesser General Public + *      License along with this library in the file COPYING.LIB; + *      if not, write to the Free Software Foundation, Inc., + *      59 Temple Place - Suite 330, Boston, MA 02111-1307, USA + * + * -------------------------------------------------------------------------- + * + * Test Synopsis: + * - Stress test condition variables, mutexes, semaphores. + * + * Test Method (Validation or Falsification): + * - Validation + * + * Requirements Tested: + * - Correct accounting of semaphore and condition variable waiters. + * + * Features Tested: + * -  + * + * Cases Tested: + * -  + * + * Description: + * Attempting to expose race conditions in cond vars, semaphores etc. + * - Master attempts to signal slave close to when timeout is due. + * - Master and slave do battle continuously until main tells them to stop. + * - Afterwards, the CV must be successfully destroyed (will return an + * error if there are waiters (including any internal semaphore waiters, + * which, if there are, cannot not be real waiters). + * + * Environment: + * -  + * + * Input: + * - None. + * + * Output: + * - File name, Line number, and failed expression on failure. + * - No output on success. + * + * Assumptions: + * -  + * + * Pass Criteria: + * - CV is successfully destroyed. + * + * Fail Criteria: + * - CV destroy fails. + */ + +#include "test.h" +#include <string.h> +#include <sys/timeb.h> + + +const unsigned int ITERATIONS = 1000; + +static pthread_t master, slave; +typedef struct { +  int value; +  pthread_cond_t cv; +  pthread_mutex_t mx; +} mysig_t; + +static int allExit; +static mysig_t control = {0, PTHREAD_COND_INITIALIZER, PTHREAD_MUTEX_INITIALIZER}; +static pthread_barrier_t startBarrier, readyBarrier, holdBarrier; +static int timeoutCount = 0; +static int signalsTakenCount = 0; +static int signalsSent = 0; +static int bias = 0; +static int timeout = 10;
 // Must be > 0 + +enum { +  CTL_STOP     = -1 +}; + +/* + * Returns abstime 'milliseconds' from 'now'. + */ +struct timespec * +millisecondsFromNow (struct timespec * time, int millisecs) +{ +  struct _timeb currSysTime; +  int64_t nanosecs, secs; +  const int64_t NANOSEC_PER_MILLISEC = 1000000; +  const int64_t NANOSEC_PER_SEC = 1000000000; + +  /* get current system time and add millisecs */ +  _ftime(&currSysTime); + +  nanosecs = ((int64_t) (millisecs + currSysTime.millitm)) * NANOSEC_PER_MILLISEC; +  if (nanosecs >= NANOSEC_PER_SEC) +    { +      secs = currSysTime.time + 1; +      nanosecs %= NANOSEC_PER_SEC; +    } +  else +    { +      secs = currSysTime.time; +    } + +  time->tv_nsec = (long)nanosecs; +  time->tv_sec = (long)secs; + +  return time; +} + +void * +masterThread (void * arg) +{ +  int dither = (int) arg; + +  timeout = (int) arg; + +  pthread_barrier_wait(&startBarrier); + +  do +    { +      int sleepTime; + +      assert(pthread_mutex_lock(&control.mx) == 0); +      control.value = timeout; +      assert(pthread_mutex_unlock(&control.mx) == 0); + +      /* +       * We are attempting to send the signal close to when the slave +       * is due to timeout. We feel around by adding some [non-random] dither. +       * +       * with dither added, sleep time range is 2*timeout peak-to-peak centred on timeout, +       * e.g. +       * if timeout = 10 then dither = 20 and +       * sleep millisecs is: 5 <= ms <= 15 +       * +       * The bias value attempts to apply some negative feedback to keep +       * the ratio of timeouts to signals taken close to 1:1. +       * bias changes more slowly than dither so as to average more. +       */ +      if (signalsSent % timeout == 0) +	{ +          if (timeoutCount > signalsTakenCount) +	    { +	      bias++; +	    } +          else if (timeoutCount < signalsTakenCount) +	    { +	      bias--; +	    } +	  if (bias < -timeout || bias > timeout) +	    { +	      timeout++; +	    } +	} +      dither = (dither + 1 ) % (timeout * 2); +      sleepTime = (timeout - bias + dither) / 2; +      Sleep(sleepTime); +      assert(pthread_cond_signal(&control.cv) == 0); +      signalsSent++; + +      pthread_barrier_wait(&holdBarrier); +      pthread_barrier_wait(&readyBarrier); +    } +  while (!allExit); + +  return NULL; +} + +void * +slaveThread (void * arg) +{ +  struct timespec time; + +  pthread_barrier_wait(&startBarrier); + +  do +    { +      assert(pthread_mutex_lock(&control.mx) == 0); +      if (pthread_cond_timedwait(&control.cv, +				 &control.mx, +				 millisecondsFromNow(&time, control.value)) == ETIMEDOUT) +	{ +	  timeoutCount++; +	} +      else +	{ +	  signalsTakenCount++; +	} +      assert(pthread_mutex_unlock(&control.mx) == 0); + +      pthread_barrier_wait(&holdBarrier); +      pthread_barrier_wait(&readyBarrier); +    } +  while (!allExit); + +  return NULL; +} + +int +main () +{ +  unsigned int i; + +  assert(pthread_barrier_init(&startBarrier, NULL, 3) == 0); +  assert(pthread_barrier_init(&readyBarrier, NULL, 3) == 0); +  assert(pthread_barrier_init(&holdBarrier, NULL, 3) == 0); + +  assert(pthread_create(&master, NULL, masterThread, (void *) timeout) == 0); +  assert(pthread_create(&slave, NULL, slaveThread, NULL) == 0); + +  allExit = FALSE; + +  pthread_barrier_wait(&startBarrier); + +  for (i = 1; !allExit; i++) +    { +      pthread_barrier_wait(&holdBarrier); +      if (i >= ITERATIONS) +	{ +	  allExit = TRUE; +	} +      pthread_barrier_wait(&readyBarrier); +    } + +  assert(pthread_join(slave, NULL) == 0); +  assert(pthread_join(master, NULL) == 0); + +  printf("Signals sent = %d\nWait timeouts = %d\nSignals taken = %d\nBias = %d\nTimeout = %d\n", +	 signalsSent, +	 timeoutCount, +	 signalsTakenCount, +	 (int) bias, +	 timeout); + +  /* Cleanup */ +  assert(pthread_barrier_destroy(&holdBarrier) == 0); +  assert(pthread_barrier_destroy(&readyBarrier) == 0); +  assert(pthread_barrier_destroy(&startBarrier) == 0); +  assert(pthread_cond_destroy(&control.cv) == 0); +  assert(pthread_mutex_destroy(&control.mx) == 0); + +  /* Success. */ +  return 0; +} | 
