X-Git-Url: https://git.saurik.com/wxWidgets.git/blobdiff_plain/4a08283ef0ef65f44d64493255c0c77a885c25e3..2d1715aa46b210e69ddf2dd1cd1c7d2f23c4298a:/src/msw/thread.cpp diff --git a/src/msw/thread.cpp b/src/msw/thread.cpp index 46719dc9c2..e179d3683f 100644 --- a/src/msw/thread.cpp +++ b/src/msw/thread.cpp @@ -10,7 +10,7 @@ // Licence: wxWindows licence ///////////////////////////////////////////////////////////////////////////// -#ifdef __GNUG__ +#if defined(__GNUG__) && !defined(NO_GCC_PRAGMA) #pragma implementation "thread.h" #endif @@ -112,8 +112,8 @@ static DWORD gs_tlsThisThread = 0xFFFFFFFF; // calling wxMutexGuiEnter() static DWORD gs_idMainThread = 0; -// if it's FALSE, some secondary thread is holding the GUI lock -static bool gs_bGuiOwnedByMainThread = TRUE; +// if it's false, some secondary thread is holding the GUI lock +static bool gs_bGuiOwnedByMainThread = true; // critical section which controls access to all GUI functions: any secondary // thread (i.e. except the main one) must enter this crit section before doing @@ -123,11 +123,16 @@ static wxCriticalSection *gs_critsectGui = NULL; // critical section which protects gs_nWaitingForGui variable static wxCriticalSection *gs_critsectWaitingForGui = NULL; +// critical section which serializes WinThreadStart() and WaitForTerminate() +// (this is a potential bottleneck, we use a single crit sect for all threads +// in the system, but normally time spent inside it should be quite short) +static wxCriticalSection *gs_critsectThreadDelete = NULL; + // number of threads waiting for GUI in wxMutexGuiEnter() static size_t gs_nWaitingForGui = 0; // are we waiting for a thread termination? -static bool gs_waitingForThread = FALSE; +static bool gs_waitingForThread = false; // ============================================================================ // Windows implementation of thread and related classes @@ -191,7 +196,7 @@ wxMutexInternal::wxMutexInternal(wxMutexType WXUNUSED(mutexType)) m_mutex = ::CreateMutex ( NULL, // default secutiry attributes - FALSE, // not initially locked + false, // not initially locked NULL // no name ); @@ -295,7 +300,7 @@ private: wxSemaphoreInternal::wxSemaphoreInternal(int initialcount, int maxcount) { -#ifndef __WXWINCE__ +#if !defined(_WIN32_WCE) || (_WIN32_WCE >= 300) if ( maxcount == 0 ) { // make it practically infinite @@ -348,7 +353,7 @@ wxSemaError wxSemaphoreInternal::WaitTimeout(unsigned long milliseconds) wxSemaError wxSemaphoreInternal::Post() { -#ifndef __WXWINCE__ +#if !defined(_WIN32_WCE) || (_WIN32_WCE >= 300) if ( !::ReleaseSemaphore(m_semaphore, 1, NULL /* ptr to previous count */) ) #endif { @@ -360,143 +365,6 @@ wxSemaError wxSemaphoreInternal::Post() return wxSEMA_NO_ERROR; } -// -------------------------------------------------------------------------- -// wxCondition -// -------------------------------------------------------------------------- - -// Win32 doesn't have explicit support for the POSIX condition variables and -// the Win32 events have quite different semantics, so we reimplement the -// conditions from scratch using the mutexes and semaphores -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 - ::InterlockedIncrement(&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::WaitTimeout(unsigned long milliseconds) -{ - ::InterlockedIncrement(&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_BUSY ) - { - // 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; -} - -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; -} - // ---------------------------------------------------------------------------- // wxThread implementation // ---------------------------------------------------------------------------- @@ -507,11 +375,13 @@ wxCondError wxConditionInternal::Broadcast() class wxThreadInternal { public: - wxThreadInternal() + wxThreadInternal(wxThread *thread) { + m_thread = thread; m_hThread = 0; m_state = STATE_NEW; m_priority = WXTHREAD_DEFAULT_PRIORITY; + m_nRef = 1; } ~wxThreadInternal() @@ -537,9 +407,9 @@ public: // wait for the thread to terminate, either by itself, or by asking it // (politely, this is not Kill()!) to do it - wxThreadError WaitForTerminate(bool shouldCancel, - wxCriticalSection& cs, - wxThread::ExitCode *pRc); + wxThreadError WaitForTerminate(wxCriticalSection& cs, + wxThread::ExitCode *pRc, + wxThread *threadToDelete = NULL); // kill the thread unconditionally wxThreadError Kill(); @@ -564,27 +434,61 @@ public: // thread function static THREAD_RETVAL THREAD_CALLCONV WinThreadStart(void *thread); + void KeepAlive() + { + if ( m_thread->IsDetached() ) + ::InterlockedIncrement(&m_nRef); + } + + void LetDie() + { + if ( m_thread->IsDetached() && !::InterlockedDecrement(&m_nRef) ) + delete m_thread; + } + private: + // the thread we're associated with + wxThread *m_thread; + HANDLE m_hThread; // handle of the thread wxThreadState m_state; // state, see wxThreadState enum unsigned int m_priority; // thread priority in "wx" units DWORD m_tid; // thread id + // number of threads which need this thread to remain alive, when the count + // reaches 0 we kill the owning wxThread -- and die ourselves with it + LONG m_nRef; + DECLARE_NO_COPY_CLASS(wxThreadInternal) }; +// small class which keeps a thread alive during its lifetime +class wxThreadKeepAlive +{ +public: + wxThreadKeepAlive(wxThreadInternal& thrImpl) : m_thrImpl(thrImpl) + { m_thrImpl.KeepAlive(); } + ~wxThreadKeepAlive() + { m_thrImpl.LetDie(); } + +private: + wxThreadInternal& m_thrImpl; +}; + + THREAD_RETVAL THREAD_CALLCONV wxThreadInternal::WinThreadStart(void *param) { THREAD_RETVAL rc; - bool wasCancelled; + + wxThread * const thread = (wxThread *)param; // first of all, check whether we hadn't been cancelled already and don't // start the user code at all then - wxThread *thread = (wxThread *)param; - if ( thread->m_internal->GetState() == STATE_EXITED ) + bool isExited = (thread->m_internal->GetState() == STATE_EXITED); + + if ( isExited ) { rc = (THREAD_RETVAL)-1; - wasCancelled = TRUE; } else // do run thread { @@ -597,24 +501,23 @@ THREAD_RETVAL THREAD_CALLCONV wxThreadInternal::WinThreadStart(void *param) } rc = (THREAD_RETVAL)thread->Entry(); - - // enter m_critsect before changing the thread state - thread->m_critsect.Enter(); - wasCancelled = thread->m_internal->GetState() == STATE_CANCELED; - thread->m_internal->SetState(STATE_EXITED); - thread->m_critsect.Leave(); } thread->OnExit(); - // if the thread was cancelled (from Delete()), then its handle is still - // needed there - if ( thread->IsDetached() && !wasCancelled ) + // save IsDetached because thread object can be deleted by joinable + // threads after state is changed to STATE_EXITED. + bool isDetached = thread->IsDetached(); + + if (!isExited) { - // auto delete - delete thread; + // enter m_critsect before changing the thread state + wxCriticalSectionLocker lock(thread->m_critsect); + thread->m_internal->SetState(STATE_EXITED); } - //else: the joinable threads handle will be closed when Wait() is done + + // the thread may delete itself now if it wants, we don't need it any more + if (isDetached) thread->m_internal->LetDie(); return rc; } @@ -689,7 +592,7 @@ bool wxThreadInternal::Create(wxThread *thread, unsigned int stackSize) { wxLogSysError(_("Can't create thread")); - return FALSE; + return false; } if ( m_priority != WXTHREAD_DEFAULT_PRIORITY ) @@ -697,7 +600,7 @@ bool wxThreadInternal::Create(wxThread *thread, unsigned int stackSize) SetPriority(m_priority); } - return TRUE; + return true; } wxThreadError wxThreadInternal::Kill() @@ -715,18 +618,27 @@ wxThreadError wxThreadInternal::Kill() } wxThreadError -wxThreadInternal::WaitForTerminate(bool shouldCancel, - wxCriticalSection& cs, - wxThread::ExitCode *pRc) +wxThreadInternal::WaitForTerminate(wxCriticalSection& cs, + wxThread::ExitCode *pRc, + wxThread *threadToDelete) { + // prevent the thread C++ object from disappearing as long as we are using + // it here + wxThreadKeepAlive keepAlive(*this); + + + // we may either wait passively for the thread to terminate (when called + // from Wait()) or ask it to terminate (when called from Delete()) + bool shouldDelete = threadToDelete != NULL; + wxThread::ExitCode rc = 0; // Delete() is always safe to call, so consider all possible states // we might need to resume the thread, but we might also not need to cancel // it if it doesn't run yet - bool shouldResume = FALSE, - isRunning = FALSE; + bool shouldResume = false, + isRunning = false; // check if the thread already started to run { @@ -734,23 +646,27 @@ wxThreadInternal::WaitForTerminate(bool shouldCancel, if ( m_state == STATE_NEW ) { - if ( shouldCancel ) + if ( shouldDelete ) { - // WinThreadStart() will see it and terminate immediately, no need - // to cancel the thread - but we still need to resume it to let it - // run + // WinThreadStart() will see it and terminate immediately, no + // need to cancel the thread -- but we still need to resume it + // to let it run m_state = STATE_EXITED; Resume(); // it knows about STATE_EXITED special case - shouldCancel = FALSE; + shouldDelete = false; } - isRunning = TRUE; + isRunning = true; - // shouldResume is correctly set to FALSE here + // shouldResume is correctly set to false here } - else + else if ( m_state == STATE_EXITED ) + { + return wxTHREAD_NOT_RUNNING; + } + else // running (but maybe paused or cancelled) { shouldResume = m_state == STATE_PAUSED; } @@ -760,17 +676,17 @@ wxThreadInternal::WaitForTerminate(bool shouldCancel, if ( shouldResume ) Resume(); - // does is still run? + // is it still running? if ( isRunning || m_state == STATE_RUNNING ) { if ( wxThread::IsMain() ) { // set flag for wxIsWaitingForThread() - gs_waitingForThread = TRUE; + gs_waitingForThread = true; } // ask the thread to terminate - if ( shouldCancel ) + if ( shouldDelete ) { wxCriticalSectionLocker lock(cs); @@ -782,7 +698,7 @@ wxThreadInternal::WaitForTerminate(bool shouldCancel, // process the Windows messages that result from these functions // (note that even in console applications we might have to process // messages if we use wxExecute() or timers or ...) - DWORD result = 0; // suppress warnings from broken compilers + DWORD result wxDUMMY_INITIALIZE(0); do { if ( wxThread::IsMain() ) @@ -799,7 +715,7 @@ wxThreadInternal::WaitForTerminate(bool shouldCancel, ( 1, // number of objects to wait for &m_hThread, // the objects - FALSE, // don't wait for all objects + false, // don't wait for all objects INFINITE, // no timeout QS_ALLINPUT | // return as soon as there are any events QS_ALLPOSTMESSAGE @@ -854,26 +770,39 @@ wxThreadInternal::WaitForTerminate(bool shouldCancel, if ( wxThread::IsMain() ) { - gs_waitingForThread = FALSE; + gs_waitingForThread = false; } } // although the thread might be already in the EXITED state it might not // have terminated yet and so we are not sure that it has actually // terminated if the "if" above hadn't been taken - do + for ( ;; ) { if ( !::GetExitCodeThread(m_hThread, (LPDWORD)&rc) ) { wxLogLastError(wxT("GetExitCodeThread")); rc = (wxThread::ExitCode)-1; + + break; } - } while ( (DWORD)rc == STILL_ACTIVE ); + + if ( (DWORD)rc != STILL_ACTIVE ) + break; + + // give the other thread some time to terminate, otherwise we may be + // starving it + ::Sleep(1); + } if ( pRc ) *pRc = rc; + // we don't need the thread handle any more in any case + Free(); + + return rc == (wxThread::ExitCode)-1 ? wxTHREAD_MISC_ERROR : wxTHREAD_NO_ERROR; } @@ -885,12 +814,12 @@ bool wxThreadInternal::Suspend() { wxLogSysError(_("Can not suspend thread %x"), m_hThread); - return FALSE; + return false; } m_state = STATE_PAUSED; - return TRUE; + return true; } bool wxThreadInternal::Resume() @@ -900,7 +829,7 @@ bool wxThreadInternal::Resume() { wxLogSysError(_("Can not resume thread %x"), m_hThread); - return FALSE; + return false; } // don't change the state from STATE_EXITED because it's special and means @@ -911,7 +840,7 @@ bool wxThreadInternal::Resume() m_state = STATE_RUNNING; } - return TRUE; + return true; } // static functions @@ -937,10 +866,6 @@ bool wxThread::IsMain() return ::GetCurrentThreadId() == gs_idMainThread; } -#ifdef Yield -#undef Yield -#endif - void wxThread::Yield() { // 0 argument to Sleep() is special and means to just give away the rest of @@ -982,7 +907,7 @@ bool wxThread::SetConcurrency(size_t level) { wxLogLastError(_T("GetProcessAffinityMask")); - return FALSE; + return false; } // how many CPUs have we got? @@ -1021,7 +946,7 @@ bool wxThread::SetConcurrency(size_t level) { wxLogDebug(_T("bad level %u in wxThread::SetConcurrency()"), level); - return FALSE; + return false; } // set it: we can't link to SetProcessAffinityMask() because it doesn't @@ -1049,17 +974,17 @@ bool wxThread::SetConcurrency(size_t level) if ( !pfnSetProcessAffinityMask ) { // msg given above - do it only once - return FALSE; + return false; } if ( pfnSetProcessAffinityMask(hProcess, dwProcMask) == 0 ) { wxLogLastError(_T("SetProcessAffinityMask")); - return FALSE; + return false; } #endif - return TRUE; + return true; } // ctor and dtor @@ -1067,7 +992,7 @@ bool wxThread::SetConcurrency(size_t level) wxThread::wxThread(wxThreadKind kind) { - m_internal = new wxThreadInternal(); + m_internal = new wxThreadInternal(this); m_isDetached = kind == wxTHREAD_DETACHED; } @@ -1133,32 +1058,14 @@ wxThread::ExitCode wxThread::Wait() ExitCode rc = (ExitCode)-1; - (void)m_internal->WaitForTerminate(false, m_critsect, &rc); - - m_internal->Free(); - - wxCriticalSectionLocker lock(m_critsect); - m_internal->SetState(STATE_EXITED); + (void)m_internal->WaitForTerminate(m_critsect, &rc); return rc; } wxThreadError wxThread::Delete(ExitCode *pRc) { - wxThreadError rc = m_internal->WaitForTerminate(true, m_critsect, pRc); - - if ( IsDetached() ) - { - delete this; - } - else // joinable - { - // update the status of the joinable thread - wxCriticalSectionLocker lock(m_critsect); - m_internal->SetState(STATE_EXITED); - } - - return rc; + return m_internal->WaitForTerminate(m_critsect, pRc, this); } wxThreadError wxThread::Kill() @@ -1286,7 +1193,7 @@ bool wxThreadModule::OnInit() // words, this should never happen wxLogSysError(_("Thread module initialization failed: impossible to allocate index in thread local storage")); - return FALSE; + return false; } // main thread doesn't have associated wxThread object, so store 0 in the @@ -1298,7 +1205,7 @@ bool wxThreadModule::OnInit() wxLogSysError(_("Thread module initialization failed: can not store value in thread local storage")); - return FALSE; + return false; } gs_critsectWaitingForGui = new wxCriticalSection(); @@ -1306,10 +1213,12 @@ bool wxThreadModule::OnInit() gs_critsectGui = new wxCriticalSection(); gs_critsectGui->Enter(); + gs_critsectThreadDelete = new wxCriticalSection; + // no error return for GetCurrentThreadId() gs_idMainThread = ::GetCurrentThreadId(); - return TRUE; + return true; } void wxThreadModule::OnExit() @@ -1319,6 +1228,9 @@ void wxThreadModule::OnExit() wxLogLastError(wxT("TlsFree failed.")); } + delete gs_critsectThreadDelete; + gs_critsectThreadDelete = NULL; + if ( gs_critsectGui ) { gs_critsectGui->Leave(); @@ -1363,7 +1275,7 @@ void WXDLLIMPEXP_BASE wxMutexGuiLeave() if ( wxThread::IsMain() ) { - gs_bGuiOwnedByMainThread = FALSE; + gs_bGuiOwnedByMainThread = false; } else { @@ -1394,7 +1306,7 @@ void WXDLLIMPEXP_BASE wxMutexGuiLeaveOrEnter() { gs_critsectGui->Enter(); - gs_bGuiOwnedByMainThread = TRUE; + gs_bGuiOwnedByMainThread = true; } //else: already have it, nothing to do }