From 0c2cb3fb140fb0d12586587001cb1ca238cf8c25 Mon Sep 17 00:00:00 2001 From: rpj Date: Fri, 29 Dec 2000 07:08:44 +0000 Subject: ./ChangeLog: 2000-12-29 Ross Johnson * Makefile: Back-out "for" loops which don't work. * GNUmakefile: Remove the fake.a target; add the "realclean" target; don't remove built libs under the "clean" target. * config.h: Add a guard against multiple inclusion. * semaphore.h: Add some defines from config.h to make semaphore.h independent of config.h when building apps. * pthread.h (_errno): Back-out previous fix until we know how to fix it properly. * implement.h (lockCount): Add missing element to pthread_mutex_t_. * sync.c (pthread_join): Spelling fix in comment. * private.c (ptw32_threadStart): Reset original termination function (C++). (ptw32_threadStart): Cleanup detached threads early in case the library is statically linked. (ptw32_callUserDestroyRoutines): Remove [SEH] __try block from destructor call so that unhandled exceptions will be passed through to the system; call terminate() from [C++] try block for the same reason. * tsd.c (pthread_getspecific): Add comment. * mutex.c (pthread_mutex_init): Initialise new elements in pthread_mutex_t. (pthread_mutex_unlock): Invert "pthread_equal()" test. 2000-12-28 Ross Johnson * semaphore.c (mode_t): Use ifndef HAVE_MODE_T to include definition. * config.h.in (HAVE_MODE_T): Added. (_UWIN): Start adding defines for the UWIN package. ./tests/ChangeLog: 2000-12-29 Ross Johnson * GNUmakefile: Add mutex4 test; ensure libpthreadw32.a is removed for "clean" target. * Makefile: Add mutex4 test. * exception3.c: Remove SEH code; automatically pass the test under SEH (which is an N/A environment). * mutex4.c: New test. * eyal1.c (do_work_unit): Add a dummy "if" to force the optimiser to retain code; reduce thread work loads. * condvar8.c (main): Add an additional "assert" for debugging; increase pthread_cond_signal timeout. --- ChangeLog | 39 +++++++++++++++++++++++++++++ GNUmakefile | 13 +++------- Makefile | 20 ++++++--------- config.h | 20 +++++++++++++++ config.h.in | 20 +++++++++++++++ implement.h | 1 + mutex.c | 4 ++- private.c | 57 ++++++++++++++++++++++++++++++------------ pthread.h | 6 +++++ semaphore.h | 23 +++++++++++++++-- sync.c | 4 +-- tests/ChangeLog | 17 +++++++++++++ tests/GNUmakefile | 5 ++-- tests/Makefile | 3 ++- tests/condvar8.c | 8 +++--- tests/exception3.c | 72 ++++++++++++------------------------------------------ tests/eyal1.c | 12 ++++++--- tests/mutex4.c | 34 ++++++++++---------------- tsd.c | 2 +- 19 files changed, 228 insertions(+), 132 deletions(-) diff --git a/ChangeLog b/ChangeLog index 28ff9d4..e8d72a8 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,5 +1,44 @@ +2000-12-29 Ross Johnson + + * Makefile: Back-out "for" loops which don't work. + + * GNUmakefile: Remove the fake.a target; add the "realclean" + target; don't remove built libs under the "clean" target. + + * config.h: Add a guard against multiple inclusion. + + * semaphore.h: Add some defines from config.h to make + semaphore.h independent of config.h when building apps. + + * pthread.h (_errno): Back-out previous fix until we know how to + fix it properly. + + * implement.h (lockCount): Add missing element to pthread_mutex_t_. + + * sync.c (pthread_join): Spelling fix in comment. + + * private.c (ptw32_threadStart): Reset original termination + function (C++). + (ptw32_threadStart): Cleanup detached threads early in case + the library is statically linked. + (ptw32_callUserDestroyRoutines): Remove [SEH] __try block from + destructor call so that unhandled exceptions will be passed through + to the system; call terminate() from [C++] try block for the same + reason. + + * tsd.c (pthread_getspecific): Add comment. + + * mutex.c (pthread_mutex_init): Initialise new elements in + pthread_mutex_t. + (pthread_mutex_unlock): Invert "pthread_equal()" test. + 2000-12-28 Ross Johnson + * semaphore.c (mode_t): Use ifndef HAVE_MODE_T to include definition. + + * config.h.in (HAVE_MODE_T): Added. + (_UWIN): Start adding defines for the UWIN package. + * private.c (ptw32_threadStart): Unhandled exceptions are now passed through to the system to deal with. This is consistent with normal Windows behaviour. C++ applications may use diff --git a/GNUmakefile b/GNUmakefile index 9ad6119..a7d13b3 100644 --- a/GNUmakefile +++ b/GNUmakefile @@ -52,12 +52,6 @@ LIBS = libpthreadw32.a all: $(LIBS) -fake.a: - @ $(CP) pthreadVCE.dll $(DLL) - dlltool --def pthread.def --output-lib $@ --dllname $(DLL) - @-$(RM) $(LIBS) - $(MV) fake.a $(LIBS) - $(LIBS): $(DLL) dlltool --def pthread.def --output-lib $@ --dllname $(DLL) @@ -78,10 +72,11 @@ $(DLL): $(OBJS) clean: -$(RM) *~ - -$(RM) $(LIBS) -$(RM) *.o -$(RM) *.exe - -$(RM) $(DLL) -$(RM) $(DLL:.dll=.base) -$(RM) $(DLL:.dll=.exp) - -$(RM) fake.a + +realclean: + -$(RM) $(LIBS) + -$(RM) $(DLL) diff --git a/Makefile b/Makefile index 1f63c31..ac8a718 100644 --- a/Makefile +++ b/Makefile @@ -53,21 +53,15 @@ VSE: @ nmake /nologo EHFLAGS="$(VSEFLAGS)" pthreadVSE.dll realclean: clean - @ for %%ext in (dll lib) do \ - if exist *.%%ext del *.%%ext - -# del *.dll -# del *.lib + if exist *.dll del *.dll + if exit *.lib del *.lib clean: - @ for %%ext in (obj ilk pdb exp o) do \ - if exist *.%%ext del *.%%ext - -# if exist *.obj del *.obj -# if exist *.ilk del *.ilk -# if exist *.pdb del *.pdb -# if exist *.exp del *.exp -# if exist *.o del *.o + if exist *.obj del *.obj + if exist *.ilk del *.ilk + if exist *.pdb del *.pdb + if exist *.exp del *.exp + if exist *.o del *.o install: $(DLLS) diff --git a/config.h b/config.h index acea758..75e0338 100644 --- a/config.h +++ b/config.h @@ -1,5 +1,8 @@ /* config.h.in. Generated automatically from configure.in by autoheader. */ +#ifndef PTW32_CONFIG_H +#define PTW32_CONFIG_H + /* Do we know about the C type sigset_t? */ #undef HAVE_SIGSET_T @@ -27,6 +30,12 @@ /* Define if you need to convert string parameters to unicode. (eg. WinCE) */ #undef NEED_UNICODE_CONSTS +/* Do we know about type mode_t? */ +#undef HAVE_MODE_T + +/* Define if you have the timespec struct */ +#undef HAVE_STRUCT_TIMESPEC + /* * Target specific groups */ @@ -39,3 +48,14 @@ #define NEED_SEM #define NEED_UNICODE_CONSTS #endif + +#ifdef _UWIN +#define HAVE_MODE_T +#define HAVE_STRUCT_TIMESPEC +#endif + +#ifdef __MINGW32__ +#define HAVE_MODE_T +#endif + +#endif \ No newline at end of file diff --git a/config.h.in b/config.h.in index acea758..75e0338 100644 --- a/config.h.in +++ b/config.h.in @@ -1,5 +1,8 @@ /* config.h.in. Generated automatically from configure.in by autoheader. */ +#ifndef PTW32_CONFIG_H +#define PTW32_CONFIG_H + /* Do we know about the C type sigset_t? */ #undef HAVE_SIGSET_T @@ -27,6 +30,12 @@ /* Define if you need to convert string parameters to unicode. (eg. WinCE) */ #undef NEED_UNICODE_CONSTS +/* Do we know about type mode_t? */ +#undef HAVE_MODE_T + +/* Define if you have the timespec struct */ +#undef HAVE_STRUCT_TIMESPEC + /* * Target specific groups */ @@ -39,3 +48,14 @@ #define NEED_SEM #define NEED_UNICODE_CONSTS #endif + +#ifdef _UWIN +#define HAVE_MODE_T +#define HAVE_STRUCT_TIMESPEC +#endif + +#ifdef __MINGW32__ +#define HAVE_MODE_T +#endif + +#endif \ No newline at end of file diff --git a/implement.h b/implement.h index 9c74bb4..c6f535e 100644 --- a/implement.h +++ b/implement.h @@ -124,6 +124,7 @@ struct pthread_attr_t_ { struct pthread_mutex_t_ { HANDLE mutex; CRITICAL_SECTION cs; + int lockCount; pthread_t ownerThread; }; diff --git a/mutex.c b/mutex.c index d31b3b2..3e4722e 100644 --- a/mutex.c +++ b/mutex.c @@ -105,6 +105,8 @@ pthread_mutex_init(pthread_mutex_t *mutex, const pthread_mutexattr_t *attr) } mx->mutex = 0; + mx->lockCount = 0; + mx->ownerThread = NULL; if (attr != NULL && *attr != NULL @@ -586,7 +588,7 @@ pthread_mutex_unlock(pthread_mutex_t *mutex) { pthread_t self = pthread_self(); - if (pthread_equal(mx->ownerThread, self) == 0) + if (pthread_equal(mx->ownerThread, self)) { int oldCount = mx->lockCount; pthread_t oldOwner = mx->ownerThread; diff --git a/private.c b/private.c index 977eb67..ee3ab52 100644 --- a/private.c +++ b/private.c @@ -240,7 +240,7 @@ ptw32_threadStart (ThreadParms * threadParms) void *(*start) (void *); void *arg; -#ifdef _MSC_VER +#if defined(_MSC_VER) && !defined(__cplusplus) DWORD ei[] = {0,0,0}; #endif @@ -354,6 +354,7 @@ ptw32_threadStart (ThreadParms * threadParms) */ status = self->exitStatus = PTHREAD_CANCELED; (void) pthread_mutex_destroy(&self->cancelLock); + (void) set_terminate(ptw32_oldTerminate); ptw32_callUserDestroyRoutines(self); throw; @@ -362,6 +363,8 @@ ptw32_threadStart (ThreadParms * threadParms) */ } + (void) set_terminate(ptw32_oldTerminate); + #else /* __cplusplus */ /* @@ -375,7 +378,32 @@ ptw32_threadStart (ThreadParms * threadParms) #endif /* _MSC_VER */ (void) pthread_mutex_destroy(&self->cancelLock); - ptw32_callUserDestroyRoutines(self); + +#if 1 + if (self->detachState == PTHREAD_CREATE_DETACHED) + { + /* + * We need to cleanup the pthread now in case we have + * been statically linked, in which case the cleanup + * in dllMain won't get done. Joinable threads will + * be cleaned up by pthread_join(). + * + * Note that implicitly created pthreads (those created + * for Win32 threads which have called pthreads routines) + * must be cleaned up explicitly by the application + * (by calling pthread_win32_thread_detach_np()) if + * this library has been statically linked. For the dll, + * dllMain will do the cleanup automatically. + */ + (void) pthread_win32_thread_detach_np (); + } + else + { + ptw32_callUserDestroyRoutines (self); + } +#else + ptw32_callUserDestroyRoutines (self); +#endif #if ! defined (__MINGW32__) || defined (__MSVCRT__) _endthreadex ((unsigned) status); @@ -611,21 +639,15 @@ ptw32_callUserDestroyRoutines (pthread_t thread) #if defined(_MSC_VER) && !defined(__cplusplus) - __try - { /* * Run the caller's cleanup routine. + * + * If an exception occurs we let the system handle it + * as an unhandled exception. Since we are leaving the + * thread we should not get any internal pthreads + * exceptions. */ (*(k->destructor)) (value); - } - __except (EXCEPTION_EXECUTE_HANDLER) - { - /* - * A system unexpected exception had occurred - * running the user's destructor. - * We get control back within this block. - */ - } #else /* _MSC_VER && !__cplusplus */ #ifdef __cplusplus @@ -640,10 +662,15 @@ ptw32_callUserDestroyRoutines (pthread_t thread) catch (...) { /* - * A system unexpected exception had occurred + * A system unexpected exception has occurred * running the user's destructor. - * We get control back within this block. + * We get control back within this block in case + * the application has set up it's own terminate + * handler. Since we are leaving the thread we + * should not get any internal pthreads + * exceptions. */ + terminate(); } #else /* __cplusplus */ diff --git a/pthread.h b/pthread.h index 21f4be9..cada83c 100644 --- a/pthread.h +++ b/pthread.h @@ -941,6 +941,11 @@ int pthreadCancelableTimedWait (HANDLE waitHandle, DWORD timeout); /* * Thread-Safe C Runtime Library Mappings. */ +#if 1 +#if (! defined(NEED_ERRNO)) || (! defined( _REENTRANT ) && (! defined( _MT ) || ! defined( _MD ))) +int * _errno( void ); +#endif +#else #if (! defined(NEED_ERRNO)) || (! defined( _REENTRANT ) && (! defined( _MT ) || ! defined( _MD ))) #if defined(PTW32_BUILD) __declspec( dllexport ) int * _errno( void ); @@ -948,6 +953,7 @@ __declspec( dllexport ) int * _errno( void ); int * _errno( void ); #endif #endif +#endif /* * WIN32 C runtime library had been made thread-safe diff --git a/semaphore.h b/semaphore.h index 8af8be8..bc66e35 100644 --- a/semaphore.h +++ b/semaphore.h @@ -28,7 +28,26 @@ #if !defined( SEMAPHORE_H ) #define SEMAPHORE_H -#ifdef NEED_ERRNO +/* + * This is a duplicate of what is in the autoconf config.h, + * which is only used when building the pthread-win32 libraries. + */ + +#ifndef PTW32_CONFIG_H +# if defined(WINCE) +# define NEED_ERRNO +# define NEED_SEM +# endif +# if defined(_UWIN) || defined(__MINGW32__) +# define HAVE_MODE_T +# endif +#endif + +/* + * + */ + +#ifdef NEED_SEM #include "need_errno.h" #else #include @@ -41,7 +60,7 @@ extern "C" { #endif /* __cplusplus */ -#if defined(_MSC_VER) +#ifndef HAVE_MODE_T typedef unsigned int mode_t; #endif diff --git a/sync.c b/sync.c index 83f421c..4fb168c 100644 --- a/sync.c +++ b/sync.c @@ -131,10 +131,10 @@ pthread_join (pthread_t thread, void **value_ptr) { /* * Pthread_join is a cancelation point. - * If we are cancelled then our target thread must not be + * If we are canceled then our target thread must not be * detached (destroyed). This is guarranteed because * pthreadCancelableWait will not return if we - * are cancelled. + * are canceled. */ result = pthreadCancelableWait(thread->threadH); diff --git a/tests/ChangeLog b/tests/ChangeLog index db66602..92fca1b 100644 --- a/tests/ChangeLog +++ b/tests/ChangeLog @@ -1,3 +1,20 @@ +2000-12-29 Ross Johnson + + * GNUmakefile: Add mutex4 test; ensure libpthreadw32.a is + removed for "clean" target. + * Makefile: Add mutex4 test. + + * exception3.c: Remove SEH code; automatically pass the test + under SEH (which is an N/A environment). + + * mutex4.c: New test. + + * eyal1.c (do_work_unit): Add a dummy "if" to force the + optimiser to retain code; reduce thread work loads. + + * condvar8.c (main): Add an additional "assert" for debugging; + increase pthread_cond_signal timeout. + 2000-12-28 Ross Johnson * eyal1.c: Increase thread work loads. diff --git a/tests/GNUmakefile b/tests/GNUmakefile index 7b4fd64..33de3ac 100644 --- a/tests/GNUmakefile +++ b/tests/GNUmakefile @@ -32,7 +32,7 @@ COPYFILES = $(HDR) $(LIB) $(DLL) TESTS = loadfree \ mutex1 condvar1 condvar2 exit1 create1 equal1 \ exit2 exit3 \ - join0 join1 join2 mutex2 mutex3 \ + join0 join1 join2 mutex2 mutex3 mutex4 \ count1 once1 tsd1 self1 self2 cancel1 cancel2 eyal1 \ condvar3 condvar4 condvar5 condvar6 condvar7 condvar8 condvar9 \ errno1 \ @@ -57,6 +57,7 @@ create1.pass: mutex2.pass cancel1.pass: create1.pass cancel2.pass: cancel1.pass mutex3.pass: create1.pass +mutex4.pass: mutex3.pass equal1.pass: create1.pass exit2.pass: create1.pass exit3.pass: create1.pass @@ -122,7 +123,7 @@ clean: - $(RM) pthread.h - $(RM) semaphore.h - $(RM) sched.h - - $(RM) *.a + - $(RM) libpthreadw32.a - $(RM) *.e - $(RM) *.obj - $(RM) *.pdb diff --git a/tests/Makefile b/tests/Makefile index 208b438..281cd50 100644 --- a/tests/Makefile +++ b/tests/Makefile @@ -33,7 +33,7 @@ EHFLAGS = # stop. PASSES= loadfree.pass \ - mutex1.pass mutex2.pass mutex3.pass \ + mutex1.pass mutex2.pass mutex3.pass mutex4.pass \ condvar1.pass condvar2.pass \ exit1.pass create1.pass equal1.pass \ exit2.pass exit3.pass \ @@ -112,6 +112,7 @@ create1.pass: mutex2.pass cancel1.pass: create1.pass cancel2.pass: cancel1.pass mutex3.pass: create1.pass +mutex4.pass: mutex3.pass equal1.pass: create1.pass exit2.pass: create1.pass exit3.pass: create1.pass diff --git a/tests/condvar8.c b/tests/condvar8.c index 9e63b79..ff893c7 100644 --- a/tests/condvar8.c +++ b/tests/condvar8.c @@ -93,10 +93,6 @@ mythread(void * arg) 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)) @@ -135,7 +131,7 @@ main() abstime.tv_sec = currSysTime.time; abstime.tv_nsec = NANOSEC_PER_MILLISEC * currSysTime.millitm; - abstime.tv_sec += 5; + abstime.tv_sec += 10; assert((t[0] = pthread_self()) != NULL); @@ -179,6 +175,8 @@ main() * Give threads time to complete. */ Sleep(1000); + + assert(awoken == (i - 1)); } diff --git a/tests/exception3.c b/tests/exception3.c index b820b4b..66f4173 100644 --- a/tests/exception3.c +++ b/tests/exception3.c @@ -39,17 +39,16 @@ * - Process returns non-zero exit status. */ +#include "test.h" + +#if defined(__cplusplus) -#if defined(_MSC_VER) && defined(__cplusplus) +#if defined(_MSC_VER) #include #else #include #endif -#if defined(_MSC_VER) || defined(__cplusplus) - -#include "test.h" - /* * Create NUMTHREADS threads in addition to the Main thread. */ @@ -60,28 +59,12 @@ enum { int caught = 0; pthread_mutex_t caughtLock = PTHREAD_MUTEX_INITIALIZER; -#if defined(_MSC_VER) && !defined(__cplusplus) - -LONG unhandledExceptionFilter (EXCEPTION_POINTERS *ep) -{ - if (ep->ExceptionRecord->ExceptionCode == 0x1) - { - pthread_mutex_lock(&caughtLock); - caught++; - pthread_mutex_unlock(&caughtLock); - } - - return EXCEPTION_CONTINUE_EXECUTION; -} - -#elif defined(__cplusplus) - void terminateFunction () { pthread_mutex_lock(&caughtLock); caught++; -#if 1 +#if 0 { FILE * fp = fopen("pthread.log", "a"); fprintf(fp, "Caught = %d\n", caught); @@ -92,28 +75,15 @@ terminateFunction () pthread_exit((void *) 0); } -#endif - void * exceptionedThread(void * arg) { int dummy = 0x1; - { -#if defined(_MSC_VER) && !defined(__cplusplus) + (void) set_terminate(&terminateFunction); + throw dummy; - RaiseException(dummy, 0, 0, NULL); - -#elif defined(__cplusplus) - - (void) set_terminate(&terminateFunction); - - throw dummy; - -#endif - } - - return (void *) 100; + return (void *) 0; } int @@ -125,25 +95,13 @@ main() assert((mt = pthread_self()) != NULL); - { -#if defined(_MSC_VER) && !defined(__cplusplus) - LPTOP_LEVEL_EXCEPTION_FILTER oldHandler; - oldHandler = SetUnhandledExceptionFilter((LPTOP_LEVEL_EXCEPTION_FILTER) unhandledExceptionFilter); -#endif - - for (i = 0; i < NUMTHREADS; i++) - { - assert(pthread_create(&et[i], NULL, exceptionedThread, NULL) == 0); - } - -#if defined(_MSC_VER) && !defined(__cplusplus) - (void) SetUnhandledExceptionFilter(oldHandler); -#endif + for (i = 0; i < NUMTHREADS; i++) + { + assert(pthread_create(&et[i], NULL, exceptionedThread, NULL) == 0); + } - Sleep(30000); - } + Sleep(10000); - printf("Caught = %d\n", caught); assert(caught == NUMTHREADS); /* @@ -152,7 +110,7 @@ main() return 0; } -#else /* defined(_MSC_VER) || defined(__cplusplus) */ +#else /* defined(__cplusplus) */ int main() @@ -161,4 +119,4 @@ main() return 0; } -#endif /* defined(_MSC_VER) || defined(__cplusplus) */ +#endif /* defined(__cplusplus) */ diff --git a/tests/eyal1.c b/tests/eyal1.c index a9ba909..6954c3b 100644 --- a/tests/eyal1.c +++ b/tests/eyal1.c @@ -63,7 +63,7 @@ typedef struct thread_control TC; static TC *tcs = NULL; static int nthreads = 10; -static int nwork = 1000; +static int nwork = 100; static int quiet = 0; static int todo = -1; @@ -86,7 +86,7 @@ die (int ret) } -static void +static double waste_time (int n) { int i; @@ -98,6 +98,7 @@ waste_time (int n) { f = 2 * f * f / (f * f); } + return f; } static int @@ -105,6 +106,7 @@ do_work_unit (int who, int n) { int i; static int nchars = 0; + double f = 0.0; if (quiet) i = 0; @@ -131,7 +133,11 @@ do_work_unit (int who, int n) } n = rand () % 10000; /* ignore incoming 'n' */ - waste_time (n); + f = waste_time (n); + + /* This prevents the statement above from being optimised out */ + if (f > 0.0) + return(n); return (n); } diff --git a/tests/mutex4.c b/tests/mutex4.c index 5290f2a..7b989d0 100644 --- a/tests/mutex4.c +++ b/tests/mutex4.c @@ -10,35 +10,32 @@ */ #include "test.h" - -pthread_mutex_t mutex1 = PTHREAD_MUTEX_INITIALIZER; -pthread_mutex_t locker_done = PTHREAD_MUTEX_INITIALIZER; -pthread_mutex_t unlocker_done = PTHREAD_MUTEX_INITIALIZER; -static int washere = 0; +static int wasHere = 0; + +static pthread_mutex_t mutex1 = PTHREAD_MUTEX_INITIALIZER; void * locker(void * arg) { - assert(pthread_mutex_lock(&locker_start) == 0); + wasHere++; assert(pthread_mutex_lock(&mutex1) == 0); - assert(pthread_mutex_unlock(&locker_start) == 0); - - /* Wait for unlocker to finish */ - assert(pthread_mutex_lock(&unlocker_end) == 0); + Sleep(1000); assert(pthread_mutex_unlock(&mutex1) == 0); + wasHere++; return 0; } void * unlocker(void * arg) { + wasHere++; + /* Wait for locker to lock mutex1 */ - assert(pthread_mutex_lock(&unlocker_start) == 0); + Sleep(500); assert(pthread_mutex_unlock(&mutex1) == EPERM); - assert(pthread_mutex_unlock(&unlocker_start) == 0); - + wasHere++; return 0; } @@ -47,16 +44,11 @@ main() { pthread_t t; - assert(pthread_mutex_lock(&locker_start) == 0); - assert(pthread_mutex_lock(&unlocker_start) == 0); - assert(pthread_create(&t, NULL, locker, NULL) == 0); - assert(pthread_mutex_unlock(&locker_start) == 0); - Sleep(0); - assert(pthread_create(&t, NULL, unlocker, NULL) == 0); - assert(pthread_mutex_unlock(&unlocker_start) == 0); - Sleep(0); + Sleep(2000); + + assert(wasHere == 4); return 0; } diff --git a/tsd.c b/tsd.c index cd80ff9..4e95210 100644 --- a/tsd.c +++ b/tsd.c @@ -317,7 +317,7 @@ pthread_getspecific (pthread_key_t key) * the thread, NULL is returned. * * RESULTS - * key value + * key value or NULL on failure * * ------------------------------------------------------ */ -- cgit v1.2.3