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