+// --------------------------------------------------------------------------
+// wxConditionInternal
+// --------------------------------------------------------------------------
+
+#if defined(__WXMSW__) || defined(__WXPM__) || defined(__EMX__)
+// Win32 and OS/2 don't have explicit support for the POSIX condition
+// variables and their events/event semaphores have quite different semantics,
+// so we reimplement the conditions from scratch using the mutexes and
+// semaphores
+#if defined(__WXPM__) || defined(__EMX__)
+void InterlockedIncrement(LONG *num)
+{
+ ::DosEnterCritSec();
+ (*num)++;
+ ::DosExitCritSec();
+}
+#endif
+
+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;
+
+ DECLARE_NO_COPY_CLASS(wxConditionInternal)
+};
+
+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;
+}
+#endif
+