]> git.saurik.com Git - wxWidgets.git/blobdiff - include/wx/thrimpl.cpp
Better fix
[wxWidgets.git] / include / wx / thrimpl.cpp
index b01436a220a8a847e1b348be26475bff8903ed9e..63b837fec65bd81f447a778eeb3e32a113d4364e 100644 (file)
@@ -44,6 +44,14 @@ wxMutexError wxMutex::Lock()
     return m_internal->Lock();
 }
 
+wxMutexError wxMutex::LockTimeout(unsigned long ms)
+{
+    wxCHECK_MSG( m_internal, wxMUTEX_INVALID,
+                 _T("wxMutex::Lock(): not initialized") );
+
+    return m_internal->Lock(ms);
+}
+
 wxMutexError wxMutex::TryLock()
 {
     wxCHECK_MSG( m_internal, wxMUTEX_INVALID,
@@ -60,6 +68,161 @@ wxMutexError wxMutex::Unlock()
     return m_internal->Unlock();
 }
 
+// --------------------------------------------------------------------------
+// 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;
+
+    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
+    {
+        wxCriticalSectionLocker lock(m_csWaiters);
+        m_numWaiters++;
+    }
+
+    m_mutex.Unlock();
+
+    // a potential race condition can occur here
+    //
+    // after a thread increments m_numWaiters, 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 m_numWaiters 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();
+
+    if ( err == wxSEMA_NO_ERROR )
+        return wxCOND_NO_ERROR;
+    else if ( err == wxSEMA_TIMEOUT )
+        return wxCOND_TIMEOUT;
+    else
+        return wxCOND_MISC_ERROR;
+}
+
+wxCondError wxConditionInternal::WaitTimeout(unsigned long milliseconds)
+{
+    {
+        wxCriticalSectionLocker lock(m_csWaiters);
+        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_TIMEOUT )
+    {
+        // another potential race condition exists here it is caused when a
+        // 'waiting' thread times out, and returns from WaitForSingleObject,
+        // but has not yet decremented m_numWaiters
+        //
+        // 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
+        // m_csWaiters. 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);
+
+        wxSemaError err2 = m_semaphore.WaitTimeout(0);
+
+        if ( err2 != wxSEMA_NO_ERROR )
+        {
+            m_numWaiters--;
+        }
+    }
+
+    m_mutex.Lock();
+
+    return err == wxSEMA_NO_ERROR ? wxCOND_NO_ERROR
+                                  : err == wxSEMA_TIMEOUT ? wxCOND_TIMEOUT
+                                                          : 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
+
 // ----------------------------------------------------------------------------
 // wxCondition
 // ----------------------------------------------------------------------------