+// --------------------------------------------------------------------------
+// wxConditionInternal
+// --------------------------------------------------------------------------
+
+// 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(__WXMSW__) || defined(__OS2__) || defined(__EMX__)
+
+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;
+
+ wxDECLARE_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
+ {
+ wxCriticalSectionLocker lock(m_csWaiters);
+ m_numWaiters++;
+ }
+
+ m_mutex.Unlock();
+
+ // after unlocking the mutex other threads may Signal() us, but it is ok
+ // now as we had already incremented m_numWaiters so Signal() will post the
+ // semaphore and decrement m_numWaiters back even if it is called before we
+ // start to Wait()
+ const wxSemaError err = m_semaphore.Wait();
+
+ m_mutex.Lock();
+
+ if ( err == wxSEMA_NO_ERROR )
+ {
+ // m_numWaiters was decremented by Signal()
+ return wxCOND_NO_ERROR;
+ }
+
+ // but in case of an error we need to do it manually
+ {
+ wxCriticalSectionLocker lock(m_csWaiters);
+ m_numWaiters--;
+ }
+
+ return err == wxSEMA_TIMEOUT ? wxCOND_TIMEOUT : wxCOND_MISC_ERROR;
+}
+
+wxCondError wxConditionInternal::WaitTimeout(unsigned long milliseconds)
+{
+ {
+ wxCriticalSectionLocker lock(m_csWaiters);
+ m_numWaiters++;
+ }
+
+ m_mutex.Unlock();
+
+ wxSemaError err = m_semaphore.WaitTimeout(milliseconds);
+
+ m_mutex.Lock();
+
+ if ( err == wxSEMA_NO_ERROR )
+ return wxCOND_NO_ERROR;
+
+ if ( err == wxSEMA_TIMEOUT )
+ {
+ // a potential race condition exists here: it happens when a waiting
+ // thread times out but doesn't have time to decrement m_numWaiters yet
+ // before Signal() is called in another thread
+ //
+ // to handle this particular case, check the semaphore again after
+ // acquiring m_csWaiters lock -- this will catch the signals missed
+ // during this window
+ wxCriticalSectionLocker lock(m_csWaiters);
+
+ err = m_semaphore.WaitTimeout(0);
+ if ( err == wxSEMA_NO_ERROR )
+ return wxCOND_NO_ERROR;
+
+ // we need to decrement m_numWaiters ourselves as it wasn't done by
+ // Signal()
+ m_numWaiters--;
+
+ return err == wxSEMA_TIMEOUT ? wxCOND_TIMEOUT : wxCOND_MISC_ERROR;
+ }
+
+ // undo m_numWaiters++ above in case of an error
+ {
+ wxCriticalSectionLocker lock(m_csWaiters);
+ m_numWaiters--;
+ }
+
+ return 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 // MSW or OS2
+