X-Git-Url: https://git.saurik.com/wxWidgets.git/blobdiff_plain/d1bab566475b6573c1c0c982eec395c6d8eef13a..fbfe58cb95e3e0586f52371964a44d345db99f2b:/src/msw/thread.cpp diff --git a/src/msw/thread.cpp b/src/msw/thread.cpp index 3f273a4600..469ced786b 100644 --- a/src/msw/thread.cpp +++ b/src/msw/thread.cpp @@ -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 { @@ -370,11 +375,13 @@ wxSemaError wxSemaphoreInternal::Post() 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() @@ -400,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(); @@ -427,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 { @@ -460,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; } @@ -552,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 ) @@ -560,7 +600,7 @@ bool wxThreadInternal::Create(wxThread *thread, unsigned int stackSize) SetPriority(m_priority); } - return TRUE; + return true; } wxThreadError wxThreadInternal::Kill() @@ -578,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 { @@ -597,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 if ( m_state == STATE_EXITED ) + { + return wxTHREAD_NOT_RUNNING; } - else + else // running (but maybe paused or cancelled) { shouldResume = m_state == STATE_PAUSED; } @@ -629,11 +682,11 @@ wxThreadInternal::WaitForTerminate(bool shouldCancel, 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); @@ -645,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() ) @@ -662,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 @@ -717,31 +770,38 @@ 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 + // we don't need the thread handle any more in any case Free(); - wxCriticalSectionLocker lock(cs); - SetState(STATE_EXITED); return rc == (wxThread::ExitCode)-1 ? wxTHREAD_MISC_ERROR : wxTHREAD_NO_ERROR; @@ -754,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() @@ -769,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 @@ -780,7 +840,7 @@ bool wxThreadInternal::Resume() m_state = STATE_RUNNING; } - return TRUE; + return true; } // static functions @@ -842,12 +902,12 @@ bool wxThread::SetConcurrency(size_t level) // get system affinity mask first HANDLE hProcess = ::GetCurrentProcess(); - DWORD dwProcMask, dwSysMask; + DWORD_PTR dwProcMask, dwSysMask; if ( ::GetProcessAffinityMask(hProcess, &dwProcMask, &dwSysMask) == 0 ) { wxLogLastError(_T("GetProcessAffinityMask")); - return FALSE; + return false; } // how many CPUs have we got? @@ -886,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 @@ -914,17 +974,18 @@ 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; +#endif // !__WXWINCE__ + + return true; } // ctor and dtor @@ -932,7 +993,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; } @@ -998,14 +1059,14 @@ wxThread::ExitCode wxThread::Wait() ExitCode rc = (ExitCode)-1; - (void)m_internal->WaitForTerminate(false, m_critsect, &rc); + (void)m_internal->WaitForTerminate(m_critsect, &rc); return rc; } wxThreadError wxThread::Delete(ExitCode *pRc) { - return m_internal->WaitForTerminate(true, m_critsect, pRc); + return m_internal->WaitForTerminate(m_critsect, pRc, this); } wxThreadError wxThread::Kill() @@ -1133,7 +1194,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 @@ -1145,7 +1206,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(); @@ -1153,10 +1214,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() @@ -1166,6 +1229,9 @@ void wxThreadModule::OnExit() wxLogLastError(wxT("TlsFree failed.")); } + delete gs_critsectThreadDelete; + gs_critsectThreadDelete = NULL; + if ( gs_critsectGui ) { gs_critsectGui->Leave(); @@ -1210,7 +1276,7 @@ void WXDLLIMPEXP_BASE wxMutexGuiLeave() if ( wxThread::IsMain() ) { - gs_bGuiOwnedByMainThread = FALSE; + gs_bGuiOwnedByMainThread = false; } else { @@ -1241,7 +1307,7 @@ void WXDLLIMPEXP_BASE wxMutexGuiLeaveOrEnter() { gs_critsectGui->Enter(); - gs_bGuiOwnedByMainThread = TRUE; + gs_bGuiOwnedByMainThread = true; } //else: already have it, nothing to do }