From c2c184dc957551e4232391b657df33aec73ef8fb Mon Sep 17 00:00:00 2001 From: rpj Date: Sun, 1 May 2005 08:29:54 +0000 Subject: '' --- tests/Bmakefile | 5 +- tests/GNUmakefile | 22 +++- tests/Makefile | 66 +++++++++--- tests/README.BENCHTESTS | 37 ++----- tests/stress1.c | 271 ++++++++++++++++++++++++++++++++++++++++++++++++ 5 files changed, 351 insertions(+), 50 deletions(-) create mode 100644 tests/stress1.c 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 +#include + + +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; +} -- cgit v1.2.3