Attached is a new pthread_cond implementation. The current implementation uses WIN32 events to block threads. To work around the missing SignalObjectAndWait on 9x and to protect against the "lost wakeup" problem the code is rather complex and uses a busy loop to wait for released threads. (see http://www.cs.wustl.edu/~schmidt/win32-cv-1.html for a strategies about implementing pthread cond variables on WIN32 and a description of the lost wakeup problem).
Actually the implementation suffers from following problems: 1. pthread_cond_broadcast does not protect against the lost wakeup problem. 2. pthread_cond_{wait|timedwait} is not a cancellation point. 3. pthread_cond_timedwait ignores the nsec part of abstime. 4. The spec requires that the mutex which is used with the condition shall be locked by the calling thread. The current code does not check this and will additionally create the mutex if it initialized with PTHREAD_MUTEX_INITIALIZER. The opengroup spec suggests EPERM under that condition. The attached code has passed all pthreads_win32 tests so far and a stress test of one the pthreads_win32 contributors. And i was able to build a threaded perl 5.8.0-1 that passed all pthread related tests. The pthread_cond test in perl is really good and it failed with the current code erratically. (FYI: The perl test result was: ... t/op/alarm...........................Unable to create sub named "" at op/alarm.t line 20. # Looks like you planned 4 tests but ran 0. FAILED at test 1 ... t/op/magic...........................FAILED at test 24 ... lib/ExtUtils/t/Embed.................FAILED at test 2 Failed 3 test scripts out of 681, 99.56% okay. The Embed test failed with an segmentation fault.) Attached are also two additional test cases from pthreads_win32. As a side effect thread.cc will shrink significantly. 2003-01-23 Thomas Pfaff <[EMAIL PROTECTED]> * thread.h (pthread_mutex::canBeUnlocked): New static method. (pthread_cond::ExitingWait): Remove. (pthread_cond::mutex): Ditto. (pthread_cond::cond_access): Ditto. (pthread_cond::win32_obj_id): Ditto. (pthread_cond::TimedWait): Ditto. (pthread_cond::BroadCast): Ditto. (pthread_cond::Signal): Ditto. (pthread_cond::pending): New member. (pthread_cond::semWait): Ditto. (pthread_cond::semIn): Ditto. (pthread_cond::mtxOut): Ditto. (pthread_cond::mtxCond): Ditto. (pthread_cond::UnBlock): New method. (pthread_cond::Wait): Ditto. * thread.cc: Update list of cancellation points. (pthread_cond::pthread_cond): Rewrite. (pthread_cond::~pthread_cond): Ditto. (pthread_cond::TimedWait): Remove. (pthread_cond::BroadCast): Ditto. (pthread_cond::Signal): Ditto. (pthread_cond::UnBlock): Implement. (pthread_cond::Wait): Ditto. (pthread_cond::fixup_after_fork): Rewrite. (pthread_mutex::canBeUnlocked): Implement. (pthread_mutex::fixup_after_fork): Remove DETECT_BAD_APP conditional. (__pthread_cond_broadcast): Just return 0 if the condition is ot initialized. Call pthread_cond::UnBlock to release blocked threads. (__pthread_cond_signal): Ditto. (__pthread_cond__dowait): Rewrite. (pthread_cond_timedwait): Fix waitlength calculation.
/* * 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 <sys/timeb.h> /* * 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. */ #ifdef _MSC_VER #pragma inline_depth(0) #endif 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); #ifdef _MSC_VER #pragma inline_depth() #endif assert(cvthing.shared > 0); awoken++; assert(pthread_mutex_unlock(&cvthing.lock) == 0); return (void *) 0; } int main() { int failed = 0; int i; int first, last; int canceledThreads = 0; pthread_t t[NUMTHREADS + 1]; struct timeb currSysTime; const DWORD NANOSEC_PER_MILLISEC = 1000000; assert((t[0] = pthread_self()) != NULL); assert(cvthing.notbusy == PTHREAD_COND_INITIALIZER); assert(cvthing.lock == PTHREAD_MUTEX_INITIALIZER); /* get current system time */ 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; }
/* * 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 <sys/timeb.h> /* * 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); #ifdef _MSC_VER #pragma inline_depth(0) #endif 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); #ifdef _MSC_VER #pragma inline_depth() #endif assert(cvthing.shared > 0); awoken++; assert(pthread_mutex_unlock(&cvthing.lock) == 0); return (void *) 0; } int main() { int failed = 0; int i; pthread_t t[NUMTHREADS + 1]; struct timeb currSysTime; 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); /* get current system time */ ftime(&currSysTime); abstime.tv_sec = currSysTime.time; abstime.tv_nsec = NANOSEC_PER_MILLISEC * currSysTime.millitm; abstime.tv_sec += 10; 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(1000); 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 -urp src.old/winsup/cygwin/thread.cc src/winsup/cygwin/thread.cc --- src.old/winsup/cygwin/thread.cc 2003-01-20 15:53:36.000000000 +0100 +++ src/winsup/cygwin/thread.cc 2003-01-23 09:34:54.000000000 +0100 @@ -461,8 +461,8 @@ open () *pause () poll () pread () -pthread_cond_timedwait () -pthread_cond_wait () +*pthread_cond_timedwait () +*pthread_cond_wait () *pthread_join () *pthread_testcancel () putmsg () @@ -811,36 +811,55 @@ pthread_cond::initMutex () api_fatal ("Could not create win32 Mutex for pthread cond static initializer support."); } -pthread_cond::pthread_cond (pthread_condattr *attr):verifyable_object (PTHREAD_COND_MAGIC) +pthread_cond::pthread_cond (pthread_condattr *attr) : + verifyable_object (PTHREAD_COND_MAGIC), + shared (0), waiting (0), pending (0), semWait (NULL), + semIn (NULL), mtxCond(NULL), next (NULL) { - int temperr; - this->shared = attr ? attr->shared : PTHREAD_PROCESS_PRIVATE; - this->mutex = NULL; - this->waiting = 0; - - this->win32_obj_id = ::CreateEvent (&sec_none_nih, false, /* auto signal reset - which I think is pthreads like ? */ - false, /* start non signaled */ - NULL /* no name */); - /* TODO: make a shared mem mutex if out attributes request shared mem cond */ - cond_access = NULL; - if ((temperr = pthread_mutex_init (&this->cond_access, NULL))) + pthread_mutex *verifyable_mutex_obj = &mtxOut; + + if (attr) + if (attr->shared != PTHREAD_PROCESS_PRIVATE) + { + magic = 0; + return; + } + + if (!pthread_mutex::isGoodObject (&verifyable_mutex_obj)) { - system_printf ("couldn't init mutex, this %p errno %d", this, temperr); - /* we need the mutex for correct behaviour */ + thread_printf ("Internal cond mutex is not valid. this %p", this); magic = 0; + return; + } + + semWait = ::CreateSemaphore (&sec_none_nih, 0, LONG_MAX, NULL); + if (!semWait) + { + debug_printf ("CreateSemaphore failed. %E"); + magic = 0; + return; + } + + semIn = ::CreateSemaphore (&sec_none_nih, 1, 1, NULL); + if (!semIn) + { + debug_printf ("CreateSemaphore failed. %E"); + CloseHandle (semWait); + magic = 0; + return; } - if (!this->win32_obj_id) - magic = 0; /* threadsafe addition is easy */ next = (pthread_cond *) InterlockedExchangePointer (&MT_INTERFACE->conds, this); } pthread_cond::~pthread_cond () { - if (win32_obj_id) - CloseHandle (win32_obj_id); - pthread_mutex_destroy (&cond_access); + if (semWait) + CloseHandle (semWait); + if (semIn) + CloseHandle (semIn); + /* I'm not 100% sure the next bit is threadsafe. I think it is... */ if (MT_INTERFACE->conds == this) InterlockedExchangePointer (&MT_INTERFACE->conds, this->next); @@ -855,132 +874,125 @@ pthread_cond::~pthread_cond () } void -pthread_cond::BroadCast () +pthread_cond::UnBlock (const bool all) { - /* TODO: implement the same race fix as Signal has */ - if (pthread_mutex_lock (&cond_access)) - system_printf ("Failed to lock condition variable access mutex, this %p", this); - int count = waiting; - if (!pthread_mutex::isGoodObject (&mutex)) - { - if (pthread_mutex_unlock (&cond_access)) - system_printf ("Failed to unlock condition variable access mutex, this %p", this); - /* This isn't and API error - users are allowed to call this when no threads - are waiting - system_printf ("Broadcast called with invalid mutex"); - */ - return; - } - while (count--) - PulseEvent (win32_obj_id); - if (pthread_mutex_unlock (&cond_access)) - system_printf ("Failed to unlock condition variable access mutex, this %p", this); -} + unsigned long releaseable; -void -pthread_cond::Signal () -{ - if (pthread_mutex_lock (&cond_access)) - system_printf ("Failed to lock condition variable access mutex, this %p", this); - if (!pthread_mutex::isGoodObject (&mutex)) - { - if (pthread_mutex_unlock (&cond_access)) - system_printf ("Failed to unlock condition variable access mutex, this %p", - this); - return; - } - int temp = waiting; - if (!temp) - /* nothing to signal */ - { - if (pthread_mutex_unlock (&cond_access)) - system_printf ("Failed to unlock condition variable access mutex, this %p", this); - return; - } - /* Prime the detection flag */ - ExitingWait = 1; - /* Signal any waiting thread */ - PulseEvent (win32_obj_id); - /* No one can start waiting until we release the condition access mutex */ - /* The released thread will decrement waiting when it gets a time slice... - without waiting for the access mutex - * InterLockedIncrement on 98 +, NT4 + returns the incremented value. - * On 95, nt 3.51 < it returns a sign correct number - 0=0, + for greater than 0, - - * for less than 0. - * Because of this we cannot spin on the waiting count, but rather we need a - * dedicated flag for a thread exiting the Wait function. - * Also not that Interlocked* sync CPU caches with memory. + /* + * Block outgoing threads (and avoid simultanous unblocks) */ - int spins = 10; - /* When ExitingWait is nonzero after a decrement, the leaving thread has - * done it's thing - */ - while (InterlockedDecrement (&ExitingWait) == 0 && spins) + mtxOut.Lock (); + + releaseable = waiting - pending; + if (releaseable) { - InterlockedIncrement (&ExitingWait); - /* give up the cpu to force a context switch. */ - low_priority_sleep (0); - if (spins == 5) - /* we've had 5 timeslices, and the woken thread still hasn't done it's - * thing - maybe we raced it with the event? */ - PulseEvent (win32_obj_id); - spins--; + unsigned long released; + + if (!pending) + { + /* + * Block incoming threads until all waiting threads are released. + */ + WaitForSingleObject (semIn, INFINITE); + + /* + * Calculate releaseable again because threads can enter until + * the semaphore has been taken, but they can not leave, therefore pending + * is unchanged and releaseable can only get higher + */ + releaseable = waiting - pending; + } + + released = all ? releaseable : 1; + pending += released; + /* + * Signal threads + */ + ::ReleaseSemaphore (semWait, released, NULL); } - if (waiting + 1 != temp) - system_printf ("Released too many threads - %d now %d originally", waiting, temp); - if (pthread_mutex_unlock (&cond_access)) - system_printf ("Failed to unlock condition variable access mutex, this %p", this); + + /* + * And let the threads release. + */ + mtxOut.UnLock (); } int -pthread_cond::TimedWait (DWORD dwMilliseconds) +pthread_cond::Wait (pthread_mutex_t mutex, DWORD dwMilliseconds) { DWORD rv; - // FIXME: race condition (potentially drop events - // Possible solution (single process only) - place this in a critical section. - mutex->UnLock (); - rv = WaitForSingleObject (win32_obj_id, dwMilliseconds); -#if 0 - /* we need to use native win32 mutex's here, because the cygwin ones now use - * critical sections, which are faster, but introduce a race _here_. Until then - * The NT variant of the code is redundant. - */ - - rv = SignalObjectAndWait (mutex->win32_obj_id, win32_obj_id, dwMilliseconds, - false); -#endif - switch (rv) + WaitForSingleObject (semIn, INFINITE); + if (1 == InterlockedIncrement ((long *)&waiting)) + mtxCond = mutex; + else if (mtxCond != mutex) { - case WAIT_FAILED: - return 0; /* POSIX doesn't allow errors after we modify the mutex state */ - case WAIT_ABANDONED: - case WAIT_TIMEOUT: - return ETIMEDOUT; - case WAIT_OBJECT_0: - return 0; /* we have been signaled */ - default: - return 0; + InterlockedDecrement ((long *)&waiting); + ::ReleaseSemaphore (semIn, 1, NULL); + return EINVAL; } + ::ReleaseSemaphore (semIn, 1, NULL); + + /* + * Release the mutex and wait for semaphore + */ + ++mutex->condwaits; + mutex->UnLock (); + + rv = pthread::cancelable_wait (semWait, dwMilliseconds, false); + + mtxOut.Lock (); + + if (rv != WAIT_OBJECT_0) + { + /* + * It might happen that a signal is sent while the thread got canceled + * or timed out. Try to take one. + * If the thread gets one than a signal|broadcast is in progress. + */ + if (WAIT_OBJECT_0 == WaitForSingleObject (semWait, 0)) + /* + * thread got cancelled ot timed out while a signalling is in progress. + * Set wait result back to signaled + */ + rv = WAIT_OBJECT_0; + } + + InterlockedDecrement ((long *)&waiting); + + if (rv == WAIT_OBJECT_0 && 0 == --pending) + /* + * All signaled threads are released, + * new threads can enter Wait + */ + ::ReleaseSemaphore (semIn, 1, NULL); + + mtxOut.UnLock (); + + mutex->Lock (); + --mutex->condwaits; + + if (rv == WAIT_CANCELED) + pthread::static_cancel_self (); + else if (rv == WAIT_TIMEOUT) + return ETIMEDOUT; + + return 0; } void pthread_cond::fixup_after_fork () { - debug_printf ("cond %x in fixup_after_fork", this); - if (shared != PTHREAD_PROCESS_PRIVATE) - api_fatal ("doesn't understand PROCESS_SHARED condition variables"); - /* FIXME: duplicate code here and in the constructor. */ - this->win32_obj_id = ::CreateEvent (&sec_none_nih, false, false, NULL); - if (!win32_obj_id) - api_fatal ("failed to create new win32 mutex"); -#if DETECT_BAD_APPS - if (waiting) - api_fatal ("Forked () while a condition variable has waiting threads.\nReport to [EMAIL PROTECTED]"); -#else - waiting = 0; - mutex = NULL; -#endif + waiting = pending = 0; + mtxCond = NULL; + + semWait = ::CreateSemaphore (&sec_none_nih, 0, LONG_MAX, NULL); + if (!semWait) + api_fatal ("pthread_cond::fixup_after_fork () failed to recreate win32 +semaphore"); + + semIn = ::CreateSemaphore (&sec_none_nih, 1, 1, NULL); + if (!semIn) + api_fatal ("pthread_cond::fixup_after_fork () failed to recreate win32 +semaphore"); } /* pthread_key */ @@ -1157,6 +1169,20 @@ pthread_mutex::isGoodInitializerOrBadObj return true; } +bool +pthread_mutex::canBeUnlocked (pthread_mutex_t const *mutex) +{ + pthread_t self = pthread::self (); + + if (!isGoodObject (mutex)) + return false; + /* + * Check if the mutex is owned by the current thread and can be unlocked + */ + return __pthread_equal (&(*mutex)->owner, &self) && + 1 == (*mutex)->recursion_counter; +} + /* This is used for mutex creation protection within a single process only */ nativeMutex NO_COPY pthread_mutex::mutexInitializationLock; @@ -1333,12 +1359,7 @@ pthread_mutex::fixup_after_fork () if (!win32_obj_id) api_fatal ("pthread_mutex::fixup_after_fork () failed to recreate win32 semaphore for mutex"); -#if DETECT_BAD_APPS - if (condwaits) - api_fatal ("Forked () while a mutex has condition variables waiting on it.\nReport to [EMAIL PROTECTED]"); -#else condwaits = 0; -#endif } bool @@ -2176,11 +2197,11 @@ int __pthread_cond_broadcast (pthread_cond_t *cond) { if (pthread_cond::isGoodInitializer (cond)) - pthread_cond::init (cond, NULL); + return 0; if (!pthread_cond::isGoodObject (cond)) return EINVAL; - (*cond)->BroadCast (); + (*cond)->UnBlock (true); return 0; } @@ -2189,70 +2210,30 @@ int __pthread_cond_signal (pthread_cond_t *cond) { if (pthread_cond::isGoodInitializer (cond)) - pthread_cond::init (cond, NULL); + return 0; if (!pthread_cond::isGoodObject (cond)) return EINVAL; - (*cond)->Signal (); + (*cond)->UnBlock (false); return 0; } -int +static int __pthread_cond_dowait (pthread_cond_t *cond, pthread_mutex_t *mutex, - long waitlength) + DWORD waitlength) { -// and yes cond_access here is still open to a race. (we increment, context swap, -// broadcast occurs - we miss the broadcast. the functions aren't split properly. - int rv; - pthread_mutex **themutex = NULL; - if (pthread_mutex::isGoodInitializer (mutex)) - pthread_mutex::init (mutex, NULL); - themutex = mutex; + if (!pthread_mutex::isGoodObject (mutex)) + return EINVAL; + if (!pthread_mutex::canBeUnlocked (mutex)) + return EPERM; + if (pthread_cond::isGoodInitializer (cond)) pthread_cond::init (cond, NULL); - - if (!pthread_mutex::isGoodObject (themutex)) - return EINVAL; if (!pthread_cond::isGoodObject (cond)) return EINVAL; - /* if the cond variable is blocked, then the above timer test maybe wrong. *shrug**/ - if (pthread_mutex_lock (&(*cond)->cond_access)) - system_printf ("Failed to lock condition variable access mutex, this %p", *cond); - - if ((*cond)->waiting) - if ((*cond)->mutex && ((*cond)->mutex != (*themutex))) - { - if (pthread_mutex_unlock (&(*cond)->cond_access)) - system_printf ("Failed to unlock condition variable access mutex, this %p", *cond); - return EINVAL; - } - InterlockedIncrement (&((*cond)->waiting)); - - (*cond)->mutex = (*themutex); - InterlockedIncrement (&((*themutex)->condwaits)); - if (pthread_mutex_unlock (&(*cond)->cond_access)) - system_printf ("Failed to unlock condition variable access mutex, this %p", *cond); - /* At this point calls to Signal will progress evebn if we aren' yet waiting - However, the loop there should allow us to get scheduled and call wait, - and have them call PulseEvent again if we dont' respond. */ - rv = (*cond)->TimedWait (waitlength); - /* this may allow a race on the mutex acquisition and waits. - But doing this within the cond access mutex creates a different race */ - InterlockedDecrement (&((*cond)->waiting)); - /* Tell Signal that we have been released */ - InterlockedDecrement (&((*cond)->ExitingWait)); - (*themutex)->Lock (); - if (pthread_mutex_lock (&(*cond)->cond_access)) - system_printf ("Failed to lock condition variable access mutex, this %p", *cond); - if ((*cond)->waiting == 0) - (*cond)->mutex = NULL; - InterlockedDecrement (&((*themutex)->condwaits)); - if (pthread_mutex_unlock (&(*cond)->cond_access)) - system_printf ("Failed to unlock condition variable access mutex, this %p", *cond); - - return rv; + return (*cond)->Wait (*mutex, waitlength); } extern "C" int @@ -2261,10 +2242,12 @@ pthread_cond_timedwait (pthread_cond_t * { if (check_valid_pointer (abstime)) return EINVAL; - struct timeb currSysTime; + struct timeval tv; + struct timezone tz; long waitlength; - ftime (&currSysTime); - waitlength = (abstime->tv_sec - currSysTime.time) * 1000; + gettimeofday (&tv, &tz); + waitlength = abstime->tv_sec * 1000 + abstime->tv_nsec / (1000 * 1000); + waitlength -= tv.tv_sec * 1000 + tv.tv_usec / 1000; if (waitlength < 0) return ETIMEDOUT; return __pthread_cond_dowait (cond, mutex, waitlength); diff -urp src.old/winsup/cygwin/thread.h src/winsup/cygwin/thread.h --- src.old/winsup/cygwin/thread.h 2003-01-20 15:53:18.000000000 +0100 +++ src/winsup/cygwin/thread.h 2003-01-22 16:23:43.000000000 +0100 @@ -304,6 +304,7 @@ public: static bool isGoodInitializer (pthread_mutex_t const *); static bool isGoodInitializerOrObject (pthread_mutex_t const *); static bool isGoodInitializerOrBadObject (pthread_mutex_t const *mutex); + static bool canBeUnlocked (pthread_mutex_t const *mutex); static void initMutex (); static int init (pthread_mutex_t *, const pthread_mutexattr_t *); @@ -455,16 +456,18 @@ public: static int init (pthread_cond_t *, const pthread_condattr_t *); int shared; - LONG waiting; - LONG ExitingWait; - pthread_mutex *mutex; - /* to allow atomic behaviour for cond_broadcast */ - pthread_mutex_t cond_access; - HANDLE win32_obj_id; + + unsigned long waiting; + unsigned long pending; + HANDLE semWait; + HANDLE semIn; + pthread_mutex mtxOut; + pthread_mutex_t mtxCond; + class pthread_cond * next; - int TimedWait (DWORD dwMilliseconds); - void BroadCast (); - void Signal (); + + void UnBlock (const bool all); + int Wait (pthread_mutex_t mutex, DWORD dwMilliseconds); void fixup_after_fork (); pthread_cond (pthread_condattr *);