From 8c238590dc9ad996710abc28a1868e9f1a41ab0a Mon Sep 17 00:00:00 2001 From: rpj Date: Sat, 16 Oct 1999 16:24:42 +0000 Subject: 1999-10-17 Ross Johnson * rwlock.c (pthread_rwlock_destroy): Add cast to remove compile warning. * condvar.c (pthread_cond_broadcast): Only release semaphores if there are waiting threads. 1999-10-15 Ross Johnson * condvar.c (cond_wait_cleanup): New static cleanup handler for cond_timedwait; (cond_timedwait): pthread_cleanup_push args changed; canceling a thread while it's in pthread_cond_wait will now decrement the waiters count and cleanup if it's the last waiter. - Lorin Hochstein and Peter Slacik ; the last waiter will now reset the CV's wasBroadcast flag - Graham Dumpleton . --- ChangeLog | 21 +++++ condvar.c | 133 ++++++++++++++++++++----------- configure | 202 +++++++++++++++++++---------------------------- configure.in | 7 +- rwlock.c | 2 +- tests/ChangeLog | 7 ++ tests/Makefile | 4 +- tests/condvar7.c | 226 ++++++++++++++++++++++++++++++++++++++++++++++++++++ tests/condvar8.c | 226 ++++++++++++++++++++++++++++++++++++++++++++++++++++ tests/condvar9.c | 231 ++++++++++++++++++++++++++++++++++++++++++++++++++++++ tests/runall.bat | 82 ++++++++++++------- tests/runtest.bat | 25 +++++- 12 files changed, 960 insertions(+), 206 deletions(-) create mode 100644 tests/condvar7.c create mode 100644 tests/condvar8.c create mode 100644 tests/condvar9.c diff --git a/ChangeLog b/ChangeLog index c7c83cc..c0dc818 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,24 @@ +1999-10-17 Ross Johnson + + * rwlock.c (pthread_rwlock_destroy): Add cast to remove compile + warning. + + * condvar.c (pthread_cond_broadcast): Only release semaphores + if there are waiting threads. + +1999-10-15 Ross Johnson + + * condvar.c (cond_wait_cleanup): New static cleanup handler for + cond_timedwait; + (cond_timedwait): pthread_cleanup_push args changed; + canceling a thread while it's in pthread_cond_wait + will now decrement the waiters count and cleanup if it's the + last waiter. + - Lorin Hochstein and + Peter Slacik ; + the last waiter will now reset the CV's wasBroadcast flag + - Graham Dumpleton . + Thu Sep 16 1999 Ross Johnson * rwlock.c (pthread_rwlock_destroy): Add serialisation. diff --git a/condvar.c b/condvar.c index 3f9a7df..4b41a23 100644 --- a/condvar.c +++ b/condvar.c @@ -522,6 +522,68 @@ pthread_cond_destroy (pthread_cond_t * cond) return (result); } +/* + * Arguments for cond_wait_cleanup, since we can only pass a + * single void * to it. + */ +typedef struct { + pthread_mutex_t * mutexPtr; + pthread_cond_t cv; + int * resultPtr; +} cond_wait_cleanup_args_t; + +static void +cond_wait_cleanup(void * args) +{ + cond_wait_cleanup_args_t * cleanup_args = (cond_wait_cleanup_args_t *) args; + pthread_mutex_t * mutexPtr = cleanup_args->mutexPtr; + pthread_cond_t cv = cleanup_args->cv; + int * resultPtr = cleanup_args->resultPtr; + int lock_result; + int lastWaiter; + + if ((lock_result = pthread_mutex_lock (&(cv->waitersLock))) == 0) + { + /* + * The waiter is responsible for decrementing + * its count, protected by an internal mutex. + */ + + cv->waiters--; + + lastWaiter = cv->wasBroadcast && (cv->waiters == 0); + + if (lastWaiter) + { + cv->wasBroadcast = FALSE; + } + + lock_result = pthread_mutex_unlock (&(cv->waitersLock)); + } + + if ((*resultPtr == 0 || *resultPtr == ETIMEDOUT) && lock_result == 0) + { + if (lastWaiter) + { + /* + * If we are the last waiter on this broadcast + * let the thread doing the broadcast proceed + */ + if (!SetEvent (cv->waitersDone)) + { + *resultPtr = EINVAL; + } + } + } + + /* + * We must always regain the external mutex, even when + * errors occur, because that's the guarantee that we give + * to our callers + */ + (void) pthread_mutex_lock (mutexPtr); +} + static int cond_timedwait (pthread_cond_t * cond, pthread_mutex_t * mutex, @@ -531,6 +593,7 @@ cond_timedwait (pthread_cond_t * cond, int internal_result = 0; int lastWaiter = FALSE; pthread_cond_t cv; + cond_wait_cleanup_args_t cleanup_args; if (cond == NULL || *cond == NULL) { @@ -579,6 +642,12 @@ cond_timedwait (pthread_cond_t * cond, * call to sem_wait since that will deadlock other calls * to pthread_cond_signal */ + cleanup_args.mutexPtr = mutex; + cleanup_args.cv = cv; + cleanup_args.resultPtr = &result; + + pthread_cleanup_push (cond_wait_cleanup, (void *) &cleanup_args); + if ((result = pthread_mutex_unlock (mutex)) == 0) { /* @@ -588,59 +657,26 @@ cond_timedwait (pthread_cond_t * cond, * timeout * * Note: - * _pthread_sem_timedwait is a cancellation point, + * _pthread_sem_timedwait is a cancelation point, * hence providing the - * mechanism for making pthread_cond_wait a cancellation + * mechanism for making pthread_cond_wait a cancelation * point. We use the cleanup mechanism to ensure we - * re-lock the mutex if we are cancelled. + * re-lock the mutex and decrement the waiters count + * if we are canceled. */ - pthread_cleanup_push (pthread_mutex_lock, mutex); - if (_pthread_sem_timedwait (&(cv->sema), abstime) == -1) { result = errno; } - - pthread_cleanup_pop (0); - } - - if ((internal_result = pthread_mutex_lock (&(cv->waitersLock))) == 0) - { - /* - * The waiter is responsible for decrementing - * its count, protected by an internal mutex. - */ - - cv->waiters--; - - lastWaiter = cv->wasBroadcast && (cv->waiters == 0); - - internal_result = pthread_mutex_unlock (&(cv->waitersLock)); } - if ((result == 0 || result == ETIMEDOUT) && internal_result == 0) - { - if (lastWaiter) - { - /* - * If we are the last waiter on this broadcast - * let the thread doing the broadcast proceed - */ - if (!SetEvent (cv->waitersDone)) - { - result = EINVAL; - } - } - } + pthread_cleanup_pop (1); /* - * We must always regain the external mutex, even when - * errors occur, because that's the guarantee that we give - * to our callers + * "result" can be modified by the cleanup handler. + * Specifically, if we are the last waiting thread and failed + * to notify the broadcast thread to proceed. */ - (void) pthread_mutex_lock (mutex); - - return (result); } /* cond_timedwait */ @@ -905,12 +941,15 @@ pthread_cond_broadcast (pthread_cond_t * cond) cv->wasBroadcast = TRUE; wereWaiters = (cv->waiters > 0); - /* - * Wake up all waiters - */ - result = (ReleaseSemaphore( cv->sema, cv->waiters, NULL ) - ? 0 - : EINVAL); + if (wereWaiters) + { + /* + * Wake up all waiters + */ + result = (ReleaseSemaphore( cv->sema, cv->waiters, NULL ) + ? 0 + : EINVAL ); + } (void) pthread_mutex_unlock(&(cv->waitersLock)); diff --git a/configure b/configure index f9489a2..33b2a22 100755 --- a/configure +++ b/configure @@ -1,7 +1,7 @@ #! /bin/sh # Guess values for system-dependent variables and create Makefiles. -# Generated automatically using autoconf version 2.12.2 +# Generated automatically using autoconf version 2.13 # Copyright (C) 1992, 93, 94, 95, 96 Free Software Foundation, Inc. # # This configure script is free software; the Free Software Foundation @@ -333,7 +333,7 @@ EOF verbose=yes ;; -version | --version | --versio | --versi | --vers) - echo "configure generated by autoconf version 2.12.2" + echo "configure generated by autoconf version 2.13" exit 0 ;; -with-* | --with-*) @@ -579,7 +579,8 @@ else ac_cv_prog_CC="$CC" # Let the user override the test. else IFS="${IFS= }"; ac_save_ifs="$IFS"; IFS=":" - for ac_dir in $PATH; do + ac_dummy="$PATH" + for ac_dir in $ac_dummy; do test -z "$ac_dir" && ac_dir=. if test -f $ac_dir/$ac_word; then ac_cv_prog_CC="gcc" @@ -600,7 +601,7 @@ if test -z "$CC"; then # Extract the first word of "cc", so it can be a program name with args. set dummy cc; ac_word=$2 echo $ac_n "checking for $ac_word""... $ac_c" 1>&6 -echo "configure:604: checking for $ac_word" >&5 +echo "configure:605: checking for $ac_word" >&5 if eval "test \"`echo '$''{'ac_cv_prog_CC'+set}'`\" = set"; then echo $ac_n "(cached) $ac_c" 1>&6 else @@ -609,7 +610,8 @@ else else IFS="${IFS= }"; ac_save_ifs="$IFS"; IFS=":" ac_prog_rejected=no - for ac_dir in $PATH; do + ac_dummy="$PATH" + for ac_dir in $ac_dummy; do test -z "$ac_dir" && ac_dir=. if test -f $ac_dir/$ac_word; then if test "$ac_dir/$ac_word" = "/usr/ucb/cc"; then @@ -650,7 +652,7 @@ fi # Extract the first word of "cl", so it can be a program name with args. set dummy cl; ac_word=$2 echo $ac_n "checking for $ac_word""... $ac_c" 1>&6 -echo "configure:654: checking for $ac_word" >&5 +echo "configure:656: checking for $ac_word" >&5 if eval "test \"`echo '$''{'ac_cv_prog_CC'+set}'`\" = set"; then echo $ac_n "(cached) $ac_c" 1>&6 else @@ -658,7 +660,8 @@ else ac_cv_prog_CC="$CC" # Let the user override the test. else IFS="${IFS= }"; ac_save_ifs="$IFS"; IFS=":" - for ac_dir in $PATH; do + ac_dummy="$PATH" + for ac_dir in $ac_dummy; do test -z "$ac_dir" && ac_dir=. if test -f $ac_dir/$ac_word; then ac_cv_prog_CC="cl" @@ -681,7 +684,7 @@ fi fi echo $ac_n "checking whether the C compiler ($CC $CFLAGS $LDFLAGS) works""... $ac_c" 1>&6 -echo "configure:685: checking whether the C compiler ($CC $CFLAGS $LDFLAGS) works" >&5 +echo "configure:688: checking whether the C compiler ($CC $CFLAGS $LDFLAGS) works" >&5 ac_ext=c # CFLAGS is not in ac_cpp because -g, -O, etc. are not valid cpp options. @@ -690,12 +693,14 @@ ac_compile='${CC-cc} -c $CFLAGS $CPPFLAGS conftest.$ac_ext 1>&5' ac_link='${CC-cc} -o conftest${ac_exeext} $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS 1>&5' cross_compiling=$ac_cv_prog_cc_cross -cat > conftest.$ac_ext < conftest.$ac_ext << EOF + +#line 699 "configure" #include "confdefs.h" + main(){return(0);} EOF -if { (eval echo configure:699: \"$ac_link\") 1>&5; (eval $ac_link) 2>&5; } && test -s conftest${ac_exeext}; then +if { (eval echo configure:704: \"$ac_link\") 1>&5; (eval $ac_link) 2>&5; } && test -s conftest${ac_exeext}; then ac_cv_prog_cc_works=yes # If we can't run a trivial program, we are probably using a cross compiler. if (./conftest; exit) 2>/dev/null; then @@ -709,18 +714,24 @@ else ac_cv_prog_cc_works=no fi rm -fr conftest* +ac_ext=c +# CFLAGS is not in ac_cpp because -g, -O, etc. are not valid cpp options. +ac_cpp='$CPP $CPPFLAGS' +ac_compile='${CC-cc} -c $CFLAGS $CPPFLAGS conftest.$ac_ext 1>&5' +ac_link='${CC-cc} -o conftest${ac_exeext} $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS 1>&5' +cross_compiling=$ac_cv_prog_cc_cross echo "$ac_t""$ac_cv_prog_cc_works" 1>&6 if test $ac_cv_prog_cc_works = no; then { echo "configure: error: installation or configuration problem: C compiler cannot create executables." 1>&2; exit 1; } fi echo $ac_n "checking whether the C compiler ($CC $CFLAGS $LDFLAGS) is a cross-compiler""... $ac_c" 1>&6 -echo "configure:719: checking whether the C compiler ($CC $CFLAGS $LDFLAGS) is a cross-compiler" >&5 +echo "configure:730: checking whether the C compiler ($CC $CFLAGS $LDFLAGS) is a cross-compiler" >&5 echo "$ac_t""$ac_cv_prog_cc_cross" 1>&6 cross_compiling=$ac_cv_prog_cc_cross echo $ac_n "checking whether we are using GNU C""... $ac_c" 1>&6 -echo "configure:724: checking whether we are using GNU C" >&5 +echo "configure:735: checking whether we are using GNU C" >&5 if eval "test \"`echo '$''{'ac_cv_prog_gcc'+set}'`\" = set"; then echo $ac_n "(cached) $ac_c" 1>&6 else @@ -729,7 +740,7 @@ else yes; #endif EOF -if { ac_try='${CC-cc} -E conftest.c'; { (eval echo configure:733: \"$ac_try\") 1>&5; (eval $ac_try) 2>&5; }; } | egrep yes >/dev/null 2>&1; then +if { ac_try='${CC-cc} -E conftest.c'; { (eval echo configure:744: \"$ac_try\") 1>&5; (eval $ac_try) 2>&5; }; } | egrep yes >/dev/null 2>&1; then ac_cv_prog_gcc=yes else ac_cv_prog_gcc=no @@ -748,7 +759,7 @@ ac_test_CFLAGS="${CFLAGS+set}" ac_save_CFLAGS="$CFLAGS" CFLAGS= echo $ac_n "checking whether ${CC-cc} accepts -g""... $ac_c" 1>&6 -echo "configure:752: checking whether ${CC-cc} accepts -g" >&5 +echo "configure:763: checking whether ${CC-cc} accepts -g" >&5 if eval "test \"`echo '$''{'ac_cv_prog_cc_g'+set}'`\" = set"; then echo $ac_n "(cached) $ac_c" 1>&6 else @@ -779,8 +790,9 @@ else fi fi + echo $ac_n "checking how to run the C preprocessor""... $ac_c" 1>&6 -echo "configure:784: checking how to run the C preprocessor" >&5 +echo "configure:796: checking how to run the C preprocessor" >&5 # On Suns, sometimes $CPP names a directory. if test -n "$CPP" && test -d "$CPP"; then CPP= @@ -795,13 +807,13 @@ else # On the NeXT, cc -E runs the code through the compiler's parser, # not just through cpp. cat > conftest.$ac_ext < Syntax Error EOF ac_try="$ac_cpp conftest.$ac_ext >/dev/null 2>conftest.out" -{ (eval echo configure:805: \"$ac_try\") 1>&5; (eval $ac_try) 2>&5; } +{ (eval echo configure:817: \"$ac_try\") 1>&5; (eval $ac_try) 2>&5; } ac_err=`grep -v '^ *+' conftest.out | grep -v "^conftest.${ac_ext}\$"` if test -z "$ac_err"; then : @@ -812,13 +824,13 @@ else rm -rf conftest* CPP="${CC-cc} -E -traditional-cpp" cat > conftest.$ac_ext < Syntax Error EOF ac_try="$ac_cpp conftest.$ac_ext >/dev/null 2>conftest.out" -{ (eval echo configure:822: \"$ac_try\") 1>&5; (eval $ac_try) 2>&5; } +{ (eval echo configure:834: \"$ac_try\") 1>&5; (eval $ac_try) 2>&5; } ac_err=`grep -v '^ *+' conftest.out | grep -v "^conftest.${ac_ext}\$"` if test -z "$ac_err"; then : @@ -829,13 +841,13 @@ else rm -rf conftest* CPP="${CC-cc} -nologo -E" cat > conftest.$ac_ext < Syntax Error EOF ac_try="$ac_cpp conftest.$ac_ext >/dev/null 2>conftest.out" -{ (eval echo configure:839: \"$ac_try\") 1>&5; (eval $ac_try) 2>&5; } +{ (eval echo configure:851: \"$ac_try\") 1>&5; (eval $ac_try) 2>&5; } ac_err=`grep -v '^ *+' conftest.out | grep -v "^conftest.${ac_ext}\$"` if test -z "$ac_err"; then : @@ -859,21 +871,19 @@ else fi echo "$ac_t""$CPP" 1>&6 -for ac_hdr in signal.h -do -ac_safe=`echo "$ac_hdr" | sed 'y%./+-%__p_%'` -echo $ac_n "checking for $ac_hdr""... $ac_c" 1>&6 -echo "configure:867: checking for $ac_hdr" >&5 +ac_safe=`echo "windows.h" | sed 'y%./+-%__p_%'` +echo $ac_n "checking for windows.h""... $ac_c" 1>&6 +echo "configure:877: checking for windows.h" >&5 if eval "test \"`echo '$''{'ac_cv_header_$ac_safe'+set}'`\" = set"; then echo $ac_n "(cached) $ac_c" 1>&6 else cat > conftest.$ac_ext < +#include EOF -ac_try="$ac_cpp conftest.$ac_ext >a.out 2>conftest.out" -{ (eval echo configure:877: \"$ac_try\") 1>&5; (eval $ac_try) 2>&5; } +ac_try="$ac_cpp conftest.$ac_ext >/dev/null 2>conftest.out" +{ (eval echo configure:887: \"$ac_try\") 1>&5; (eval $ac_try) 2>&5; } ac_err=`grep -v '^ *+' conftest.out | grep -v "^conftest.${ac_ext}\$"` if test -z "$ac_err"; then rm -rf conftest* @@ -889,134 +899,83 @@ rm -f conftest* fi if eval "test \"`echo '$ac_cv_header_'$ac_safe`\" = yes"; then echo "$ac_t""yes" 1>&6 - ac_tr_hdr=HAVE_`echo $ac_hdr | sed 'y%abcdefghijklmnopqrstuvwxyz./-%ABCDEFGHIJKLMNOPQRSTUVWXYZ___%'` - cat >> confdefs.h <&6 +{ echo "configure: error: Target system must be Win32" 1>&2; exit 1; } fi -done -if test x$signal_h = xyes -then - echo $ac_n "checking for sigset_t""... $ac_c" 1>&6 -echo "configure:906: checking for sigset_t" >&5 -if eval "test \"`echo '$''{'p32_cv_sigset_t'+set}'`\" = set"; then + +ac_safe=`echo "signal.h" | sed 'y%./+-%__p_%'` +echo $ac_n "checking for signal.h""... $ac_c" 1>&6 +echo "configure:912: checking for signal.h" >&5 +if eval "test \"`echo '$''{'ac_cv_header_$ac_safe'+set}'`\" = set"; then echo $ac_n "(cached) $ac_c" 1>&6 else cat > conftest.$ac_ext < -int main() { -sigset_t x; -; return 0; } EOF -if { (eval echo configure:918: \"$ac_compile\") 1>&5; (eval $ac_compile) 2>&5; }; then +ac_try="$ac_cpp conftest.$ac_ext >/dev/null 2>conftest.out" +{ (eval echo configure:922: \"$ac_try\") 1>&5; (eval $ac_try) 2>&5; } +ac_err=`grep -v '^ *+' conftest.out | grep -v "^conftest.${ac_ext}\$"` +if test -z "$ac_err"; then rm -rf conftest* - p32_cv_sigset_t=yes + eval "ac_cv_header_$ac_safe=yes" else + echo "$ac_err" >&5 echo "configure: failed program was:" >&5 cat conftest.$ac_ext >&5 rm -rf conftest* - p32_cv_sigset_t=no + eval "ac_cv_header_$ac_safe=no" fi rm -f conftest* fi - -echo "$ac_t""$p32_cv_sigset_t" 1>&6 - - if test x$p32_cv_sigset_t = xyes ; then - cat >> confdefs.h <<\EOF -#define HAVE_SIGSET_T 1 -EOF - - fi +if eval "test \"`echo '$ac_cv_header_'$ac_safe`\" = yes"; then + echo "$ac_t""yes" 1>&6 + signal_h=yes +else + echo "$ac_t""no" 1>&6 fi -echo $ac_n "checking for _stdcall keyword""... $ac_c" 1>&6 -echo "configure:940: checking for _stdcall keyword" >&5 -if eval "test \"`echo '$''{'p32_cv_stdcall'+set}'`\" = set"; then + +if test x$signal_h = xyes +then + echo $ac_n "checking for sigset_t""... $ac_c" 1>&6 +echo "configure:946: checking for sigset_t" >&5 +if eval "test \"`echo '$''{'p32_cv_sigset_t'+set}'`\" = set"; then echo $ac_n "(cached) $ac_c" 1>&6 else cat > conftest.$ac_ext < int main() { -int __stdcall foo(); +sigset_t x; ; return 0; } EOF -if { (eval echo configure:952: \"$ac_compile\") 1>&5; (eval $ac_compile) 2>&5; }; then +if { (eval echo configure:958: \"$ac_compile\") 1>&5; (eval $ac_compile) 2>&5; }; then rm -rf conftest* - p32_cv_stdcall=yes + p32_cv_sigset_t=yes else echo "configure: failed program was:" >&5 cat conftest.$ac_ext >&5 rm -rf conftest* - p32_cv_stdcall=no + p32_cv_sigset_t=no fi rm -f conftest* fi -echo "$ac_t""$p32_cv_stdcall" 1>&6 - -if test x$p32_cv_stdcall = xyes ; then - cat >> confdefs.h <<\EOF -#define STDCALL __stdcall -EOF +echo "$ac_t""$p32_cv_sigset_t" 1>&6 -else - cat >> confdefs.h <<\EOF -#ifndef STDCALL -#define STDCALL -#endif + if test x$p32_cv_sigset_t = xyes ; then + cat >> confdefs.h <<\EOF +#define HAVE_SIGSET_T 1 EOF + fi fi -for ac_hdr in windows.h -do -ac_safe=`echo "$ac_hdr" | sed 'y%./+-%__p_%'` -echo $ac_n "checking for $ac_hdr""... $ac_c" 1>&6 -echo "configure:982: checking for $ac_hdr" >&5 -if eval "test \"`echo '$''{'ac_cv_header_$ac_safe'+set}'`\" = set"; then - echo $ac_n "(cached) $ac_c" 1>&6 -else - cat > conftest.$ac_ext < -#include "confdefs.h" -EOF -ac_try="$ac_cpp conftest.$ac_ext >a.out 2>conftest.out" -{ (eval echo configure:992: \"$ac_try\") 1>&5; (eval $ac_try) 2>&5; } -ac_err=`grep -v '^ *+' conftest.out | grep -v "^conftest.${ac_ext}\$"` -if test -z "$ac_err"; then - rm -rf conftest* - eval "ac_cv_header_$ac_safe=yes" -else - echo "$ac_err" >&5 - echo "configure: failed program was:" >&5 - cat conftest.$ac_ext >&5 - rm -rf conftest* - eval "ac_cv_header_$ac_safe=no" -fi -rm -f conftest* -fi -if eval "test \"`echo '$ac_cv_header_'$ac_safe`\" = yes"; then - echo "$ac_t""yes" 1>&6 - ac_tr_hdr=HAVE_`echo $ac_hdr | sed 'y%abcdefghijklmnopqrstuvwxyz./-%ABCDEFGHIJKLMNOPQRSTUVWXYZ___%'` - cat >> confdefs.h <&6 -echo "configure: warning: Target system must be Win32" 1>&2 -fi -done - trap '' 1 2 15 cat > confcache <<\EOF # This file is a shell script that caches the results of configure @@ -1040,7 +999,7 @@ EOF # Ultrix sh set writes to stderr and can't be redirected directly, # and sets the high bit in the cache file unless we assign to the vars. (set) 2>&1 | - case `(ac_space=' '; set) 2>&1 | grep ac_space` in + case `(ac_space=' '; set | grep ac_space) 2>&1` in *ac_space=\ *) # `set' does not quote correctly, so add quotes (double-quote substitution # turns \\\\ into \\, and sed turns \\ into \). @@ -1107,7 +1066,7 @@ do echo "running \${CONFIG_SHELL-/bin/sh} $0 $ac_configure_args --no-create --no-recursion" exec \${CONFIG_SHELL-/bin/sh} $0 $ac_configure_args --no-create --no-recursion ;; -version | --version | --versio | --versi | --vers | --ver | --ve | --v) - echo "$CONFIG_STATUS generated by autoconf version 2.12.2" + echo "$CONFIG_STATUS generated by autoconf version 2.13" exit 0 ;; -help | --help | --hel | --he | --h) echo "\$ac_cs_usage"; exit 0 ;; @@ -1130,6 +1089,7 @@ s%@SHELL@%$SHELL%g s%@CFLAGS@%$CFLAGS%g s%@CPPFLAGS@%$CPPFLAGS%g s%@CXXFLAGS@%$CXXFLAGS%g +s%@FFLAGS@%$FFLAGS%g s%@DEFS@%$DEFS%g s%@LDFLAGS@%$LDFLAGS%g s%@LIBS@%$LIBS%g diff --git a/configure.in b/configure.in index 0896edb..5c4dfac 100644 --- a/configure.in +++ b/configure.in @@ -4,7 +4,11 @@ AC_INIT(pthread.h) AC_CANONICAL_HOST AC_CONFIG_HEADER(config.h) AC_PROG_CC -AC_CHECK_HEADERS([signal.h],signal_h=yes,) + +dnl Abort here if there is no windows.h +AC_CHECK_HEADER([windows.h],,AC_MSG_ERROR([Target system must be Win32])) + +AC_CHECK_HEADER([signal.h],signal_h=yes,) if test x$signal_h = xyes then AC_CACHE_CHECK([for sigset_t], p32_cv_sigset_t, @@ -16,5 +20,4 @@ then fi fi -AC_CHECK_HEADERS(windows.h,,AC_MSG_WARN([Target system must be Win32])) AC_OUTPUT(Makefile) diff --git a/rwlock.c b/rwlock.c index d135c39..fd3f1f7 100644 --- a/rwlock.c +++ b/rwlock.c @@ -182,7 +182,7 @@ pthread_rwlock_destroy(pthread_rwlock_t *rwlock) } else { - rw->rw_magic = NULL; + rw->rw_magic = (int) NULL; (void) pthread_mutex_unlock(&(rw->rw_lock)); (void) pthread_cond_destroy(&(rw->rw_condreaders)); (void) pthread_cond_destroy(&(rw->rw_condwriters)); diff --git a/tests/ChangeLog b/tests/ChangeLog index 2283e3a..ee69577 100644 --- a/tests/ChangeLog +++ b/tests/ChangeLog @@ -1,3 +1,10 @@ +Oct 14 1999 Ross Johnson + + * condvar7.c: New. Test broadcast after waiting thread is canceled. + * condvar8.c: New. Test multiple broadcasts. + * condvar9.c: New. Test multiple broadcasts with thread + cancelation. + Sep 16 1999 Ross Johnson * rwlock6.c: New test. diff --git a/tests/Makefile b/tests/Makefile index e2c298d..0263769 100644 --- a/tests/Makefile +++ b/tests/Makefile @@ -40,7 +40,7 @@ TESTS = mutex1 condvar1 condvar2 exit1 create1 equal1 \ exit2 exit3 \ join1 join2 mutex2 mutex3 \ count1 once1 tsd1 self1 self2 eyal1 \ - condvar3 condvar4 condvar5 condvar6 \ + condvar3 condvar4 condvar5 condvar6 condvar7 condvar8 \ errno1 \ rwlock1 rwlock2 rwlock3 rwlock4 rwlock5 rwlock6 @@ -71,6 +71,8 @@ condvar3.pass: create1.pass condvar4.pass: create1.pass condvar5.pass: condvar4.pass condvar6.pass: condvar5.pass +condvar7.pass: condvar6.pass +condvar8.pass: condvar6.pass errno1.pass: mutex3.pass rwlock1.pass: condvar6.pass rwlock2.pass: rwlock1.pass diff --git a/tests/condvar7.c b/tests/condvar7.c new file mode 100644 index 0000000..d81dd78 --- /dev/null +++ b/tests/condvar7.c @@ -0,0 +1,226 @@ +/* + * File: condvar7.c + * + * Test Synopsis: + * - Test pthread_cond_broadcast with thread cancelation. + * + * Test Method (Validation or Falsification): + * - Validation + * + * Requirements Tested: + * - + * + * Features Tested: + * - + * + * Cases Tested: + * - + * + * Description: + * - Test broadcast with NUMTHREADS (=5) waiting CVs, one is canceled while waiting. + * + * Environment: + * - + * + * Input: + * - None. + * + * Output: + * - File name, Line number, and failed expression on failure. + * - No output on success. + * + * Assumptions: + * - + * + * Pass Criteria: + * - Process returns zero exit status. + * + * Fail Criteria: + * - Process returns non-zero exit status. + */ + +#include "test.h" +#include + +/* + * Create NUMTHREADS threads in addition to the Main thread. + */ +enum { + NUMTHREADS = 5 +}; + +typedef struct bag_t_ bag_t; +struct bag_t_ { + int threadnum; + int started; + /* Add more per-thread state variables here */ +}; + +static bag_t threadbag[NUMTHREADS + 1]; + +typedef struct cvthing_t_ cvthing_t; + +struct cvthing_t_ { + pthread_cond_t notbusy; + pthread_mutex_t lock; + int shared; +}; + +static cvthing_t cvthing = { + PTHREAD_COND_INITIALIZER, + PTHREAD_MUTEX_INITIALIZER, + 0 +}; + +static pthread_mutex_t start_flag = PTHREAD_MUTEX_INITIALIZER; + +static struct timespec abstime = { 0, 0 }; + +static int awoken; + +void * +mythread(void * arg) +{ + bag_t * bag = (bag_t *) arg; + + assert(bag == &threadbag[bag->threadnum]); + assert(bag->started == 0); + bag->started = 1; + + /* Wait for the start gun */ + assert(pthread_mutex_lock(&start_flag) == 0); + assert(pthread_mutex_unlock(&start_flag) == 0); + + assert(pthread_mutex_lock(&cvthing.lock) == 0); + + pthread_cleanup_push(pthread_mutex_unlock, (void *) &cvthing.lock); + + while (! cvthing.shared > 0) + assert(pthread_cond_timedwait(&cvthing.notbusy, &cvthing.lock, &abstime) == 0); + + pthread_cleanup_pop(0); + + assert(cvthing.shared > 0); + + awoken++; + + assert(pthread_mutex_unlock(&cvthing.lock) == 0); + + return 0; +} + +int +main() +{ + int failed = 0; + int i; + pthread_t t[NUMTHREADS + 1]; + +#if defined(__MINGW32__) + struct timeb currSysTime; +#else + struct _timeb currSysTime; +#endif + const DWORD NANOSEC_PER_MILLISEC = 1000000; + + cvthing.shared = 0; + + assert((t[0] = pthread_self()) != NULL); + + assert(cvthing.notbusy == PTHREAD_COND_INITIALIZER); + + assert(cvthing.lock == PTHREAD_MUTEX_INITIALIZER); + + assert(pthread_mutex_lock(&start_flag) == 0); + + _ftime(&currSysTime); + + abstime.tv_sec = currSysTime.time; + abstime.tv_nsec = NANOSEC_PER_MILLISEC * currSysTime.millitm; + + abstime.tv_sec += 5; + + assert((t[0] = pthread_self()) != NULL); + + awoken = 0; + + for (i = 1; i <= NUMTHREADS; i++) + { + threadbag[i].started = 0; + threadbag[i].threadnum = i; + assert(pthread_create(&t[i], NULL, mythread, (void *) &threadbag[i]) == 0); + } + + /* + * Code to control or munipulate child threads should probably go here. + */ + + assert(pthread_mutex_unlock(&start_flag) == 0); + + /* + * Give threads time to start. + */ + Sleep(2000); + + assert(pthread_mutex_lock(&cvthing.lock) == 0); + + cvthing.shared++; + + assert(pthread_mutex_unlock(&cvthing.lock) == 0); + + /* + * Cancel one of the threads. + */ + assert(pthread_cancel(t[3]) == 0); + Sleep(500); + + /* + * Signal all remaining waiting threads. + */ + assert(pthread_cond_broadcast(&cvthing.notbusy) == 0); + + /* + * Give threads time to complete. + */ + Sleep(2000); + + /* + * Cleanup the CV. + */ + + assert(pthread_mutex_destroy(&cvthing.lock) == 0); + + assert(cvthing.lock == NULL); + + assert(pthread_cond_destroy(&cvthing.notbusy) == 0); + + assert(cvthing.notbusy == NULL); + + /* + * Standard check that all threads started. + */ + for (i = 1; i <= NUMTHREADS; i++) + { + failed = !threadbag[i].started; + + if (failed) + { + fprintf(stderr, "Thread %d: started %d\n", i, threadbag[i].started); + } + } + + assert(!failed); + + /* + * Check any results here. + */ + + assert(awoken == (NUMTHREADS - 1)); + + /* + * Success. + */ + return 0; +} + + diff --git a/tests/condvar8.c b/tests/condvar8.c new file mode 100644 index 0000000..65da040 --- /dev/null +++ b/tests/condvar8.c @@ -0,0 +1,226 @@ +/* + * File: condvar8.c + * + * Test Synopsis: + * - Test multiple pthread_cond_broadcasts. + * + * Test Method (Validation or Falsification): + * - Validation + * + * Requirements Tested: + * - + * + * Features Tested: + * - + * + * Cases Tested: + * - + * + * Description: + * - Make NUMTHREADS threads wait on CV, broadcast signal them, and then repeat. + * + * Environment: + * - + * + * Input: + * - None. + * + * Output: + * - File name, Line number, and failed expression on failure. + * - No output on success. + * + * Assumptions: + * - + * + * Pass Criteria: + * - Process returns zero exit status. + * + * Fail Criteria: + * - Process returns non-zero exit status. + */ + +#include "test.h" +#include + +/* + * Create NUMTHREADS threads in addition to the Main thread. + */ +enum { + NUMTHREADS = 5 +}; + +typedef struct bag_t_ bag_t; +struct bag_t_ { + int threadnum; + int started; + /* Add more per-thread state variables here */ +}; + +static bag_t threadbag[NUMTHREADS + 1]; + +typedef struct cvthing_t_ cvthing_t; + +struct cvthing_t_ { + pthread_cond_t notbusy; + pthread_mutex_t lock; + int shared; +}; + +static cvthing_t cvthing = { + PTHREAD_COND_INITIALIZER, + PTHREAD_MUTEX_INITIALIZER, + 0 +}; + +static pthread_mutex_t start_flag = PTHREAD_MUTEX_INITIALIZER; + +static struct timespec abstime = { 0, 0 }; + +static int awoken; + +static void * +mythread(void * arg) +{ + bag_t * bag = (bag_t *) arg; + + assert(bag == &threadbag[bag->threadnum]); + assert(bag->started == 0); + bag->started = 1; + + /* Wait for the start gun */ + assert(pthread_mutex_lock(&start_flag) == 0); + assert(pthread_mutex_unlock(&start_flag) == 0); + + assert(pthread_mutex_lock(&cvthing.lock) == 0); + + /* + * pthread_cond_timedwait is a cancelation point and we + * going to cancel one deliberately. + */ + pthread_cleanup_push(pthread_mutex_unlock, (void *) &cvthing.lock); + + while (! cvthing.shared > 0) + assert(pthread_cond_timedwait(&cvthing.notbusy, &cvthing.lock, &abstime) == 0); + + pthread_cleanup_pop(0); + + assert(cvthing.shared > 0); + + awoken++; + + assert(pthread_mutex_unlock(&cvthing.lock) == 0); + + return 0; +} + +int +main() +{ + int failed = 0; + int i; + int first, last; + pthread_t t[NUMTHREADS + 1]; + +#if defined(__MINGW32__) + struct timeb currSysTime; +#else + struct _timeb currSysTime; +#endif + const DWORD NANOSEC_PER_MILLISEC = 1000000; + + assert((t[0] = pthread_self()) != NULL); + + assert(cvthing.notbusy == PTHREAD_COND_INITIALIZER); + + assert(cvthing.lock == PTHREAD_MUTEX_INITIALIZER); + + _ftime(&currSysTime); + + abstime.tv_sec = currSysTime.time; + abstime.tv_nsec = NANOSEC_PER_MILLISEC * currSysTime.millitm; + + abstime.tv_sec += 5; + + assert((t[0] = pthread_self()) != NULL); + + awoken = 0; + + for (first = 1, last = NUMTHREADS / 2; + first < NUMTHREADS; + first = last + 1, last = NUMTHREADS) + { + assert(pthread_mutex_lock(&start_flag) == 0); + + for (i = first; i <= last; i++) + { + threadbag[i].started = 0; + threadbag[i].threadnum = i; + assert(pthread_create(&t[i], NULL, mythread, (void *) &threadbag[i]) == 0); + assert(pthread_detach(t[i]) == 0); + } + + /* + * Code to control or munipulate child threads should probably go here. + */ + cvthing.shared = 0; + + assert(pthread_mutex_unlock(&start_flag) == 0); + + /* + * Give threads time to start. + */ + Sleep(1000); + + assert(pthread_mutex_lock(&cvthing.lock) == 0); + + cvthing.shared++; + + assert(pthread_mutex_unlock(&cvthing.lock) == 0); + + assert(pthread_cond_broadcast(&cvthing.notbusy) == 0); + + /* + * Give threads time to complete. + */ + Sleep(1000); + } + + + /* + * Standard check that all threads started. + */ + for (i = 1; i <= NUMTHREADS; i++) + { + failed = !threadbag[i].started; + + if (failed) + { + fprintf(stderr, "Thread %d: started %d\n", i, threadbag[i].started); + } + } + + /* + * Cleanup the CV. + */ + + assert(pthread_mutex_destroy(&cvthing.lock) == 0); + + assert(cvthing.lock == NULL); + + assert(pthread_cond_destroy(&cvthing.notbusy) == 0); + + assert(cvthing.notbusy == NULL); + + assert(!failed); + + /* + * Check any results here. + */ + + assert(awoken == NUMTHREADS); + + /* + * Success. + */ + return 0; +} diff --git a/tests/condvar9.c b/tests/condvar9.c new file mode 100644 index 0000000..49a8cab --- /dev/null +++ b/tests/condvar9.c @@ -0,0 +1,231 @@ +/* + * File: condvar9.c + * + * Test Synopsis: + * - Test multiple pthread_cond_broadcasts with thread cancelation. + * + * Test Method (Validation or Falsification): + * - Validation + * + * Requirements Tested: + * - + * + * Features Tested: + * - + * + * Cases Tested: + * - + * + * Description: + * - Make NUMTHREADS threads wait on CV, cancel one, broadcast signal them, + * and then repeat. + * + * Environment: + * - + * + * Input: + * - None. + * + * Output: + * - File name, Line number, and failed expression on failure. + * - No output on success. + * + * Assumptions: + * - + * + * Pass Criteria: + * - Process returns zero exit status. + * + * Fail Criteria: + * - Process returns non-zero exit status. + */ + +#include "test.h" +#include + +/* + * Create NUMTHREADS threads in addition to the Main thread. + */ +enum { + NUMTHREADS = 9 +}; + +typedef struct bag_t_ bag_t; +struct bag_t_ { + int threadnum; + int started; + /* Add more per-thread state variables here */ +}; + +static bag_t threadbag[NUMTHREADS + 1]; + +typedef struct cvthing_t_ cvthing_t; + +struct cvthing_t_ { + pthread_cond_t notbusy; + pthread_mutex_t lock; + int shared; +}; + +static cvthing_t cvthing = { + PTHREAD_COND_INITIALIZER, + PTHREAD_MUTEX_INITIALIZER, + 0 +}; + +static pthread_mutex_t start_flag = PTHREAD_MUTEX_INITIALIZER; + +static struct timespec abstime = { 0, 0 }; + +static int awoken; + +static void * +mythread(void * arg) +{ + bag_t * bag = (bag_t *) arg; + + assert(bag == &threadbag[bag->threadnum]); + assert(bag->started == 0); + bag->started = 1; + + /* Wait for the start gun */ + assert(pthread_mutex_lock(&start_flag) == 0); + assert(pthread_mutex_unlock(&start_flag) == 0); + + assert(pthread_mutex_lock(&cvthing.lock) == 0); + + /* + * pthread_cond_timedwait is a cancelation point and we + * going to cancel one deliberately. + */ + pthread_cleanup_push(pthread_mutex_unlock, (void *) &cvthing.lock); + + while (! cvthing.shared > 0) + assert(pthread_cond_timedwait(&cvthing.notbusy, &cvthing.lock, &abstime) == 0); + + pthread_cleanup_pop(0); + + assert(cvthing.shared > 0); + + awoken++; + + assert(pthread_mutex_unlock(&cvthing.lock) == 0); + + return 0; +} + +int +main() +{ + int failed = 0; + int i; + int first, last; + int canceledThreads = 0; + pthread_t t[NUMTHREADS + 1]; + +#if defined(__MINGW32__) + struct timeb currSysTime; +#else + struct _timeb currSysTime; +#endif + const DWORD NANOSEC_PER_MILLISEC = 1000000; + + assert((t[0] = pthread_self()) != NULL); + + assert(cvthing.notbusy == PTHREAD_COND_INITIALIZER); + + assert(cvthing.lock == PTHREAD_MUTEX_INITIALIZER); + + _ftime(&currSysTime); + + abstime.tv_sec = currSysTime.time; + abstime.tv_nsec = NANOSEC_PER_MILLISEC * currSysTime.millitm; + + abstime.tv_sec += 5; + + assert((t[0] = pthread_self()) != NULL); + + awoken = 0; + + for (first = 1, last = NUMTHREADS / 2; + first < NUMTHREADS; + first = last + 1, last = NUMTHREADS) + { + assert(pthread_mutex_lock(&start_flag) == 0); + + for (i = first; i <= last; i++) + { + threadbag[i].started = 0; + threadbag[i].threadnum = i; + assert(pthread_create(&t[i], NULL, mythread, (void *) &threadbag[i]) == 0); + assert(pthread_detach(t[i]) == 0); + } + + /* + * Code to control or munipulate child threads should probably go here. + */ + cvthing.shared = 0; + + assert(pthread_mutex_unlock(&start_flag) == 0); + + /* + * Give threads time to start. + */ + Sleep(1000); + + assert(pthread_mutex_lock(&cvthing.lock) == 0); + + cvthing.shared++; + + assert(pthread_mutex_unlock(&cvthing.lock) == 0); + + assert(pthread_cancel(t[(first + last) / 2]) == 0); + canceledThreads++; + + assert(pthread_cond_broadcast(&cvthing.notbusy) == 0); + + /* + * Give threads time to complete. + */ + Sleep(1000); + } + + + /* + * Standard check that all threads started. + */ + for (i = 1; i <= NUMTHREADS; i++) + { + failed = !threadbag[i].started; + + if (failed) + { + fprintf(stderr, "Thread %d: started %d\n", i, threadbag[i].started); + } + } + + /* + * Cleanup the CV. + */ + + assert(pthread_mutex_destroy(&cvthing.lock) == 0); + + assert(cvthing.lock == NULL); + + assert(pthread_cond_destroy(&cvthing.notbusy) == 0); + + assert(cvthing.notbusy == NULL); + + assert(!failed); + + /* + * Check any results here. + */ + + assert(awoken == NUMTHREADS - canceledThreads); + + /* + * Success. + */ + return 0; +} diff --git a/tests/runall.bat b/tests/runall.bat index a1e3862..e3a927b 100644 --- a/tests/runall.bat +++ b/tests/runall.bat @@ -1,33 +1,55 @@ @echo off -if x%1==x-f echo y | erase *.pass > nul: +if NOT x%1==x-f goto noforce +if EXIST *.pass echo y | erase *.pass > nul: +if EXIST *.fail echo y | erase *.fail > nul: +if EXIST *.notrun echo y | erase *.notrun > nul: -call runtest cl mutex1 -call runtest cl mutex2 -call runtest cl exit1 -call runtest cl condvar1 -call runtest cl self1 -call runtest cl condvar2 -call runtest cl create1 -call runtest cl mutex3 -call runtest cl equal1 -call runtest cl exit2 -call runtest cl exit3 -call runtest cl join1 -call runtest cl join2 -call runtest cl count1 -call runtest cl once1 -call runtest cl tsd1 -call runtest cl self2 -call runtest cl eyal1 -call runtest cl condvar3 -call runtest cl condvar4 -call runtest cl condvar5 -call runtest cl condvar6 -call runtest cl errno1 -call runtest cl rwlock1 -call runtest cl rwlock2 -call runtest cl rwlock3 -call runtest cl rwlock4 -call runtest cl rwlock5 -call runtest cl rwlock6 +:noforce +call runtest cl mutex1 _ +call runtest cl mutex2 _ +call runtest cl exit1 _ +call runtest cl condvar1 _ +call runtest cl self1 _ +call runtest cl condvar2 condvar1 +call runtest cl create1 mutex2 +call runtest cl mutex3 create1 +call runtest cl equal1 create1 +call runtest cl exit2 create1 +call runtest cl exit3 create1 +call runtest cl join1 create1 +call runtest cl join2 create1 +call runtest cl count1 join1 +call runtest cl once1 create1 +call runtest cl tsd1 join1 +call runtest cl self2 create1 +call runtest cl eyal1 tsd1 +call runtest cl condvar3 create1 +call runtest cl condvar4 create1 +call runtest cl condvar5 condvar4 +call runtest cl condvar6 condvar5 +call runtest cl condvar7 condvar6 +call runtest cl condvar8 condvar7 +call runtest cl condvar9 condvar8 +call runtest cl errno1 mutex3 +call runtest cl rwlock1 condvar6 +call runtest cl rwlock2 rwlock1 +call runtest cl rwlock3 rwlock2 +call runtest cl rwlock4 rwlock3 +call runtest cl rwlock5 rwlock4 +call runtest cl rwlock6 rwlock5 + +if NOT EXIST *.notrun goto skip1 +echo The following tests did not run (because prerequisite didn't pass?): +for %%f in (*.notrun) do echo %%f +goto skip2 +:skip1 +echo All tests ran. +:skip2 +if NOT EXIST *.fail goto skip3 +echo The following tests failed: +for %%f in (*.fail) do echo %%f +goto skip4 +:skip3 +echo No tests failed. +:skip4 diff --git a/tests/runtest.bat b/tests/runtest.bat index 4a06505..73fae97 100644 --- a/tests/runtest.bat +++ b/tests/runtest.bat @@ -1,7 +1,11 @@ @echo off -REM Usage: runtest cl|gcc testname testarg ... +REM Usage: runtest cl|gcc testname prerequisit testarg ... +if %3==_ goto noprereq +if NOT EXIST %3.pass goto needprereq + +:noprereq if EXIST %2.pass goto bypass REM Make sure we start with only those files we expect to need @@ -30,11 +34,15 @@ REM erase ..\%2.%1log echo TEST: %2 [%1] REM Run the test case -aout.exe %3 %4 %5 %6 %7 %8 %9 +if EXIST %2.pass erase %2.pass +if EXIST %2.fail erase %2.fail +if EXIST %2.notrun erase %2.notrun +aout.exe %4 %5 %6 %7 %8 %9 set RESULT=%ERRORLEVEL% -if %RESULT% EQU 0 echo Passed [%RESULT%] > ..\%2.pass +if %RESULT% NEQ 0 echo Failed [%RESULT%] > ..\%2.fail +if %RESULT% EQU 0 echo Passed > ..\%2.pass :cleanup @@ -43,6 +51,15 @@ cd .. REM Clean up if exist tmp\*.* echo y | erase tmp\*.* > nul: +if EXIST %2.fail echo Failed [%RESULT%] if EXIST %2.pass echo Passed [%RESULT%] -:bypass \ No newline at end of file +:bypass +goto end + +:needprereq +echo Test %2 requires %3 to pass before it can run. +echo No Prereq > ..\%2.notrun +goto end + +:end -- cgit v1.2.3