-// --------------------------------------------------------------------------
-// wxCondition
-// --------------------------------------------------------------------------
-
-// Win32 doesn't have explicit support for the POSIX condition variables and
-// the Win32 events have quite different semantics, so we reimplement the
-// conditions from scratch using the mutexes and semaphores
-class wxConditionInternal
-{
-public:
- wxConditionInternal(wxMutex& mutex);
-
- bool IsOk() const { return m_mutex.IsOk() && m_semaphore.IsOk(); }
-
- wxCondError Wait();
- wxCondError WaitTimeout(unsigned long milliseconds);
-
- wxCondError Signal();
- wxCondError Broadcast();
-
-private:
- // the number of threads currently waiting for this condition
- LONG m_numWaiters;
-
- // the critical section protecting m_numWaiters
- wxCriticalSection m_csWaiters;
-
- wxMutex& m_mutex;
- wxSemaphore m_semaphore;
-};
-
-wxConditionInternal::wxConditionInternal(wxMutex& mutex)
- : m_mutex(mutex)
-{
- // another thread can't access it until we return from ctor, so no need to
- // protect access to m_numWaiters here
- m_numWaiters = 0;
-}
-
-wxCondError wxConditionInternal::Wait()
-{
- // increment the number of waiters
- ::InterlockedIncrement(&m_numWaiters);
-
- m_mutex.Unlock();
-
- // a potential race condition can occur here
- //
- // after a thread increments nwaiters, and unlocks the mutex and before the
- // semaphore.Wait() is called, if another thread can cause a signal to be
- // generated
- //
- // this race condition is handled by using a semaphore and incrementing the
- // semaphore only if 'nwaiters' is greater that zero since the semaphore,
- // can 'remember' signals the race condition will not occur
-
- // wait ( if necessary ) and decrement semaphore
- wxSemaError err = m_semaphore.Wait();
- m_mutex.Lock();
-
- return err == wxSEMA_NO_ERROR ? wxCOND_NO_ERROR : wxCOND_MISC_ERROR;
-}
-
-wxCondError wxConditionInternal::WaitTimeout(unsigned long milliseconds)
-{
- ::InterlockedIncrement(&m_numWaiters);
-
- m_mutex.Unlock();
-
- // a race condition can occur at this point in the code
- //
- // please see the comments in Wait(), for details
-
- wxSemaError err = m_semaphore.WaitTimeout(milliseconds);
-
- if ( err == wxSEMA_BUSY )
- {
- // another potential race condition exists here it is caused when a
- // 'waiting' thread timesout, and returns from WaitForSingleObject, but
- // has not yet decremented 'nwaiters'.
- //
- // at this point if another thread calls signal() then the semaphore
- // will be incremented, but the waiting thread will miss it.
- //
- // to handle this particular case, the waiting thread calls
- // WaitForSingleObject again with a timeout of 0, after locking
- // 'nwaiters_mutex'. this call does not block because of the zero
- // timeout, but will allow the waiting thread to catch the missed
- // signals.
- wxCriticalSectionLocker lock(m_csWaiters);
-
- err = m_semaphore.WaitTimeout(0);
-
- if ( err != wxSEMA_NO_ERROR )
- {
- m_numWaiters--;
- }
- }
-
- m_mutex.Lock();
-
- return err == wxSEMA_NO_ERROR ? wxCOND_NO_ERROR : wxCOND_MISC_ERROR;
-}
-
-wxCondError wxConditionInternal::Signal()
-{
- wxCriticalSectionLocker lock(m_csWaiters);
-
- if ( m_numWaiters > 0 )
- {
- // increment the semaphore by 1
- if ( m_semaphore.Post() != wxSEMA_NO_ERROR )
- return wxCOND_MISC_ERROR;
-
- m_numWaiters--;
- }
-
- return wxCOND_NO_ERROR;
-}
-
-wxCondError wxConditionInternal::Broadcast()
-{
- wxCriticalSectionLocker lock(m_csWaiters);
-
- while ( m_numWaiters > 0 )
- {
- if ( m_semaphore.Post() != wxSEMA_NO_ERROR )
- return wxCOND_MISC_ERROR;
-
- m_numWaiters--;
- }
-
- return wxCOND_NO_ERROR;
-}
-