diff options
author | Thomas Pfaff <tpfaff@gmx.net> | 2003-03-18 22:49:38 +0300 |
---|---|---|
committer | Thomas Pfaff <tpfaff@gmx.net> | 2003-03-18 22:49:38 +0300 |
commit | f592b05df1c4aece7a0a2dbe91163b95ca0de80e (patch) | |
tree | 487099b8c3da29d6e01bf022701718dd09e089ff /winsup/cygwin/thread.cc | |
parent | 0bad7c2e26b90c872f9ed1dabfd0b28054c63dee (diff) |
* thread.h (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::waiting): Change type to unsigned long.
(pthread_cond::pending): New member.
(pthread_cond::semWait): Ditto.
(pthread_cond::mtxIn): 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::fixup_after_fork): Remove DETECT_BAD_APP
conditional.
(__pthread_cond_broadcast): Just return 0 if the condition is
not initialized. Call pthread_cond::UnBlock to release blocked
threads.
(__pthread_cond_signal): Ditto.
(__pthread_cond__dowait): Rewrite.
(pthread_cond_timedwait): Add pthread_testcancel call. Fix
waitlength calculation.
(pthread_cond_wait): Add pthread_testcancel call.
Diffstat (limited to 'winsup/cygwin/thread.cc')
-rw-r--r-- | winsup/cygwin/thread.cc | 352 |
1 files changed, 164 insertions, 188 deletions
diff --git a/winsup/cygwin/thread.cc b/winsup/cygwin/thread.cc index f20356439..125e7d38f 100644 --- a/winsup/cygwin/thread.cc +++ b/winsup/cygwin/thread.cc @@ -462,8 +462,8 @@ open () *pause () poll () pread () -pthread_cond_timedwait () -pthread_cond_wait () +*pthread_cond_timedwait () +*pthread_cond_wait () *pthread_join () *pthread_testcancel () putmsg () @@ -812,36 +812,57 @@ 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), + mtxCond(NULL), next (NULL) { - int temperr; - this->shared = attr ? attr->shared : PTHREAD_PROCESS_PRIVATE; - this->mutex = NULL; - this->waiting = 0; + pthread_mutex *verifyable_mutex_obj; - 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))) + if (attr) + if (attr->shared != PTHREAD_PROCESS_PRIVATE) + { + magic = 0; + return; + } + + verifyable_mutex_obj = &mtxIn; + 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; + } + /* Change the mutex type to NORMAL to speed up mutex operations */ + mtxIn.type = PTHREAD_MUTEX_NORMAL; + + verifyable_mutex_obj = &mtxOut; + if (!pthread_mutex::isGoodObject (&verifyable_mutex_obj)) + { + thread_printf ("Internal cond mutex is not valid. this %p", this); + magic = 0; + return; + } + /* Change the mutex type to NORMAL to speed up mutex operations */ + mtxOut.type = PTHREAD_MUTEX_NORMAL; + + semWait = ::CreateSemaphore (&sec_none_nih, 0, LONG_MAX, NULL); + if (!semWait) + { + debug_printf ("CreateSemaphore failed. %E"); + 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); + /* 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); @@ -856,132 +877,125 @@ pthread_cond::~pthread_cond () } void -pthread_cond::BroadCast () -{ - /* 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); -} - -void -pthread_cond::Signal () +pthread_cond::UnBlock (const bool all) { - 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. - */ - int spins = 10; - /* When ExitingWait is nonzero after a decrement, the leaving thread has - * done it's thing + unsigned long releaseable; + + /* + * Block outgoing threads (and avoid simultanous unblocks) */ - 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. + */ + mtxIn.Lock (); + + /* + * 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. + mtxIn.Lock (); + if (1 == InterlockedIncrement ((long *)&waiting)) + mtxCond = mutex; + else if (mtxCond != mutex) + { + InterlockedDecrement ((long *)&waiting); + mtxIn.UnLock (); + return EINVAL; + } + mtxIn.UnLock (); + + /* + * Release the mutex and wait on semaphore */ + ++mutex->condwaits; + mutex->UnLock (); - rv = SignalObjectAndWait (mutex->win32_obj_id, win32_obj_id, dwMilliseconds, - false); -#endif - switch (rv) + rv = pthread::cancelable_wait (semWait, dwMilliseconds, false); + + mtxOut.Lock (); + + if (rv != WAIT_OBJECT_0) { - 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; + /* + * 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 + */ + mtxIn.UnLock (); + + 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 cygwin@cygwin.com"); -#else - waiting = 0; - mutex = NULL; -#endif + waiting = pending = 0; + mtxCond = NULL; + + /* Unlock eventually locked mutexes */ + mtxIn.UnLock (); + mtxOut.UnLock (); + + semWait = ::CreateSemaphore (&sec_none_nih, 0, LONG_MAX, NULL); + if (!semWait) + api_fatal ("pthread_cond::fixup_after_fork () failed to recreate win32 semaphore"); } /* pthread_key */ @@ -1325,12 +1339,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 cygwin@cygwin.com"); -#else condwaits = 0; -#endif } bool @@ -2168,11 +2177,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; } @@ -2181,82 +2190,47 @@ 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 pthread_cond_timedwait (pthread_cond_t *cond, pthread_mutex_t *mutex, const struct timespec *abstime) { + struct timeval tv; + long waitlength; + + pthread_testcancel (); + if (check_valid_pointer (abstime)) return EINVAL; - struct timeb currSysTime; - long waitlength; - ftime (&currSysTime); - waitlength = (abstime->tv_sec - currSysTime.time) * 1000; + + gettimeofday (&tv, NULL); + 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); @@ -2265,6 +2239,8 @@ pthread_cond_timedwait (pthread_cond_t *cond, pthread_mutex_t *mutex, extern "C" int pthread_cond_wait (pthread_cond_t *cond, pthread_mutex_t *mutex) { + pthread_testcancel (); + return __pthread_cond_dowait (cond, mutex, INFINITE); } |