void MiscThreadTestCase::TestThreadSuspend() { MyDetachedThread *thread = new MyDetachedThread(15, 'X'); CPPUNIT_ASSERT_EQUAL( wxTHREAD_NO_ERROR, thread->Run() ); // this is for this demo only, in a real life program we'd use another // condition variable which would be signaled from wxThread::Entry() to // tell us that the thread really started running - but here just wait a // bit and hope that it will be enough (the problem is, of course, that // the thread might still not run when we call Pause() which will result // in an error) wxMilliSleep(300); for ( size_t n = 0; n < 3; n++ ) { thread->Pause(); if ( n > 0 ) { // don't sleep but resume immediately the first time wxMilliSleep(300); } CPPUNIT_ASSERT_EQUAL( wxTHREAD_NO_ERROR, thread->Resume() ); } // wait until the thread terminates CPPUNIT_ASSERT_EQUAL( wxSEMA_NO_ERROR, gs_cond.Wait() ); }
void wxThreadInternal::Resume() { wxCHECK_RET( m_state == STATE_PAUSED, wxT("can't resume thread which is not suspended.") ); // the thread might be not actually paused yet - if there were no call to // TestDestroy() since the last call to Pause() for example if ( IsReallyPaused() ) { wxLogTrace(TRACE_THREADS, _T("Waking up thread %ld"), THR_ID(this)); // wake up Pause() m_semSuspend.Post(); // reset the flag SetReallyPaused(false); } else { wxLogTrace(TRACE_THREADS, _T("Thread %ld is not yet really paused"), THR_ID(this)); } SetState(STATE_RUNNING); }
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; }
void MyDetachedThread::OnExit() { //wxLogTrace(wxT("thread"), wxT("Thread %ld is in OnExit"), GetId()); wxCriticalSectionLocker lock(gs_critsect); if ( !--gs_counter && !m_cancelled ) gs_cond.Post(); }
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; }
void wxThreadInternal::Pause() { // the state is set from the thread which pauses us first, this function // is called later so the state should have been already set wxCHECK_RET( m_state == STATE_PAUSED, wxT("thread must first be paused with wxThread::Pause().") ); // wait until the semaphore is Post()ed from Resume() m_semSuspend.Wait(); }
wxCondError wxConditionInternal::WaitTimeout( unsigned long milliseconds ) { IncrementAtomic( &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 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; }
void MiscThreadTestCase::TestThreadConditions() { wxMutex mutex; wxCondition condition(mutex); // otherwise its difficult to understand which log messages pertain to // which condition //wxLogTrace(wxT("thread"), wxT("Local condition var is %08x, gs_cond = %08x"), // condition.GetId(), gs_cond.GetId()); // create and launch threads MyWaitingThread *threads[10]; size_t n; for ( n = 0; n < WXSIZEOF(threads); n++ ) { threads[n] = new MyWaitingThread( &mutex, &condition ); } for ( n = 0; n < WXSIZEOF(threads); n++ ) { CPPUNIT_ASSERT_EQUAL( wxTHREAD_NO_ERROR, threads[n]->Run() ); } // wait until all threads run // NOTE: main thread is waiting for the other threads to start size_t nRunning = 0; while ( nRunning < WXSIZEOF(threads) ) { CPPUNIT_ASSERT_EQUAL( wxSEMA_NO_ERROR, gs_cond.Wait() ); nRunning++; // note that main thread is already running } wxMilliSleep(500); #if 1 // now wake one of them up CPPUNIT_ASSERT_EQUAL( wxCOND_NO_ERROR, condition.Signal() ); #endif wxMilliSleep(200); // wake all the (remaining) threads up, so that they can exit CPPUNIT_ASSERT_EQUAL( wxCOND_NO_ERROR, condition.Broadcast() ); // give them time to terminate (dirty!) wxMilliSleep(500); }
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; }
virtual ExitCode Entry() { //wxPrintf(wxT("Thread %lu has started running.\n"), GetId()); gs_cond.Post(); //wxPrintf(wxT("Thread %lu starts to wait...\n"), GetId()); m_mutex->Lock(); m_condition->Wait(); m_mutex->Unlock(); //wxPrintf(wxT("Thread %lu finished to wait, exiting.\n"), GetId()); return 0; }
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; }
void wxThreadInternal::Resume() { wxCHECK_RET( m_state == STATE_PAUSED, wxT("can't resume thread which is not suspended.") ); // the thread might be not actually paused yet - if there were no call to // TestDestroy() since the last call to Pause() for example if ( IsReallyPaused() ) { // wake up Pause() m_semSuspend.Post(); // reset the flag SetReallyPaused( false ); } SetState( STATE_RUNNING ); }
void MiscThreadTestCase::TestDetached() { static const size_t nThreads = 3; MyDetachedThread *threads[nThreads]; size_t n; for ( n = 0; n < nThreads; n++ ) { threads[n] = new MyDetachedThread(10, 'A' + n); } threads[0]->SetPriority(WXTHREAD_MIN_PRIORITY); threads[1]->SetPriority(WXTHREAD_MAX_PRIORITY); for ( n = 0; n < nThreads; n++ ) { CPPUNIT_ASSERT_EQUAL( wxTHREAD_NO_ERROR, threads[n]->Run() ); } // wait until all threads terminate CPPUNIT_ASSERT_EQUAL( wxSEMA_NO_ERROR, gs_cond.Wait() ); }
wxCondError wxConditionInternal::Wait() { // increment the number of waiters IncrementAtomic( &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::Broadcast() { wxCriticalSectionLocker lock(m_csWaiters); #if defined(__INTEL_COMPILER) && 1 /* VDM auto patch */ # pragma ivdep # pragma swp # pragma unroll # pragma prefetch # if 0 # pragma simd noassert # endif #endif /* VDM auto patch */ while ( m_numWaiters > 0 ) { if ( m_semaphore.Post() != wxSEMA_NO_ERROR ) return wxCOND_MISC_ERROR; m_numWaiters--; } return wxCOND_NO_ERROR; }