fix handling of errors in wxConditionInternal::Wait() and WaitTimeout() (#10111)
git-svn-id: https://svn.wxwidgets.org/svn/wx/wxWidgets/trunk@56526 c3d73ce0-8a6f-49c7-b76d-6d57e0e08775
This commit is contained in:
@@ -122,26 +122,27 @@ wxCondError wxConditionInternal::Wait()
|
|||||||
|
|
||||||
m_mutex.Unlock();
|
m_mutex.Unlock();
|
||||||
|
|
||||||
// a potential race condition can occur here
|
// 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
|
||||||
// after a thread increments m_numWaiters, and unlocks the mutex and before
|
// semaphore and decrement m_numWaiters back even if it is called before we
|
||||||
// the semaphore.Wait() is called, if another thread can cause a signal to
|
// start to Wait()
|
||||||
// be generated
|
const wxSemaError err = m_semaphore.Wait();
|
||||||
//
|
|
||||||
// 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();
|
m_mutex.Lock();
|
||||||
|
|
||||||
if ( err == wxSEMA_NO_ERROR )
|
if ( err == wxSEMA_NO_ERROR )
|
||||||
|
{
|
||||||
|
// m_numWaiters was decremented by Signal()
|
||||||
return wxCOND_NO_ERROR;
|
return wxCOND_NO_ERROR;
|
||||||
else if ( err == wxSEMA_TIMEOUT )
|
}
|
||||||
return wxCOND_TIMEOUT;
|
|
||||||
else
|
// but in case of an error we need to do it manually
|
||||||
return wxCOND_MISC_ERROR;
|
{
|
||||||
|
wxCriticalSectionLocker lock(m_csWaiters);
|
||||||
|
m_numWaiters--;
|
||||||
|
}
|
||||||
|
|
||||||
|
return err == wxSEMA_TIMEOUT ? wxCOND_TIMEOUT : wxCOND_MISC_ERROR;
|
||||||
}
|
}
|
||||||
|
|
||||||
wxCondError wxConditionInternal::WaitTimeout(unsigned long milliseconds)
|
wxCondError wxConditionInternal::WaitTimeout(unsigned long milliseconds)
|
||||||
@@ -153,41 +154,42 @@ wxCondError wxConditionInternal::WaitTimeout(unsigned long milliseconds)
|
|||||||
|
|
||||||
m_mutex.Unlock();
|
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);
|
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();
|
m_mutex.Lock();
|
||||||
|
|
||||||
return err == wxSEMA_NO_ERROR ? wxCOND_NO_ERROR
|
if ( err == wxSEMA_NO_ERROR )
|
||||||
: err == wxSEMA_TIMEOUT ? wxCOND_TIMEOUT
|
return wxCOND_NO_ERROR;
|
||||||
: wxCOND_MISC_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()
|
wxCondError wxConditionInternal::Signal()
|
||||||
|
Reference in New Issue
Block a user