X-Git-Url: https://git.saurik.com/wxWidgets.git/blobdiff_plain/d1bab566475b6573c1c0c982eec395c6d8eef13a..3d104ec3570c714e770784003d6eafe9f9226d9a:/src/msw/thread.cpp?ds=inline diff --git a/src/msw/thread.cpp b/src/msw/thread.cpp index 3f273a4600..24aa2a43fd 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 ) + const bool hasExited = thread->m_internal->GetState() == STATE_EXITED; + + if ( hasExited ) { rc = (THREAD_RETVAL)-1; - wasCancelled = TRUE; } else // do run thread { @@ -460,24 +501,24 @@ 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 ( !hasExited ) { - // 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; } @@ -486,7 +527,7 @@ void wxThreadInternal::SetPriority(unsigned int priority) { m_priority = priority; - // translate wxWindows priority to the Windows one + // translate wxWidgets priority to the Windows one int win_priority; if (m_priority <= 20) win_priority = THREAD_PRIORITY_LOWEST; @@ -552,7 +593,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 +601,7 @@ bool wxThreadInternal::Create(wxThread *thread, unsigned int stackSize) SetPriority(m_priority); } - return TRUE; + return true; } wxThreadError wxThreadInternal::Kill() @@ -578,42 +619,49 @@ wxThreadError wxThreadInternal::Kill() } wxThreadError -wxThreadInternal::WaitForTerminate(bool shouldCancel, - wxCriticalSection& cs, - wxThread::ExitCode *pRc) +wxThreadInternal::WaitForTerminate(wxCriticalSection& cs, + wxThread::ExitCode *pRc, + wxThread *threadToDelete) { - wxThread::ExitCode rc = 0; + // 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; - // Delete() is always safe to call, so consider all possible states + wxThread::ExitCode rc = 0; - // 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; + // we might need to resume the thread if it's currently stopped + bool shouldResume = false; - // check if the thread already started to run + // as Delete() (which calls us) is always safe to call we need to consider + // all possible states { wxCriticalSectionLocker lock(cs); 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; + // we must call Resume() as the thread hasn't been initially + // resumed yet (and as Resume() it knows about STATE_EXITED + // special case, it won't touch it and WinThreadStart() will + // just exit immediately) + shouldResume = true; + shouldDelete = false; } - - isRunning = TRUE; - - // shouldResume is correctly set to FALSE here + //else: shouldResume is correctly set to false here, wait until + // someone else runs the thread and it finishes } - else + else // running, paused, cancelled or even exited { shouldResume = m_state == STATE_PAUSED; } @@ -623,125 +671,131 @@ wxThreadInternal::WaitForTerminate(bool shouldCancel, if ( shouldResume ) Resume(); - // is it still running? - if ( isRunning || m_state == STATE_RUNNING ) + // ask the thread to terminate + if ( shouldDelete ) + { + wxCriticalSectionLocker lock(cs); + + Cancel(); + } + + + // now wait for thread to finish + if ( wxThread::IsMain() ) + { + // set flag for wxIsWaitingForThread() + gs_waitingForThread = true; + } + + // we can't just wait for the thread to terminate because it might be + // calling some GUI functions and so it will never terminate before we + // 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 wxDUMMY_INITIALIZE(0); + do { if ( wxThread::IsMain() ) { - // set flag for wxIsWaitingForThread() - gs_waitingForThread = TRUE; + // give the thread we're waiting for chance to do the GUI call + // it might be in + if ( (gs_nWaitingForGui > 0) && wxGuiOwnedByMainThread() ) + { + wxMutexGuiLeave(); + } } - // ask the thread to terminate - if ( shouldCancel ) + result = ::MsgWaitForMultipleObjects + ( + 1, // number of objects to wait for + &m_hThread, // the objects + false, // don't wait for all objects + INFINITE, // no timeout + QS_ALLINPUT | // return as soon as there are any events + QS_ALLPOSTMESSAGE + ); + + switch ( result ) { - wxCriticalSectionLocker lock(cs); - - Cancel(); - } + case 0xFFFFFFFF: + // error + wxLogSysError(_("Can not wait for thread termination")); + Kill(); + return wxTHREAD_KILLED; + + case WAIT_OBJECT_0: + // thread we're waiting for terminated + break; - // we can't just wait for the thread to terminate because it might be - // calling some GUI functions and so it will never terminate before we - // 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 - do - { - if ( wxThread::IsMain() ) - { - // give the thread we're waiting for chance to do the GUI call - // it might be in - if ( (gs_nWaitingForGui > 0) && wxGuiOwnedByMainThread() ) + case WAIT_OBJECT_0 + 1: + // new message arrived, process it -- but only if we're the + // main thread as we don't support processing messages in + // the other ones + // + // NB: we still must include QS_ALLINPUT even when waiting + // in a secondary thread because if it had created some + // window somehow (possible not even using wxWidgets) + // the system might dead lock then + if ( wxThread::IsMain() ) { - wxMutexGuiLeave(); - } - } + // it looks that sometimes WAIT_OBJECT_0 + 1 is + // returned but there are no messages in the thread + // queue -- prevent DoMessageFromThreadWait() from + // blocking inside ::GetMessage() forever in this case + ::PostMessage(NULL, WM_NULL, 0, 0); - result = ::MsgWaitForMultipleObjects - ( - 1, // number of objects to wait for - &m_hThread, // the objects - FALSE, // don't wait for all objects - INFINITE, // no timeout - QS_ALLINPUT | // return as soon as there are any events - QS_ALLPOSTMESSAGE - ); - - switch ( result ) - { - case 0xFFFFFFFF: - // error - wxLogSysError(_("Can not wait for thread termination")); - Kill(); - return wxTHREAD_KILLED; - - case WAIT_OBJECT_0: - // thread we're waiting for terminated - break; - - case WAIT_OBJECT_0 + 1: - // new message arrived, process it -- but only if we're the - // main thread as we don't support processing messages in - // the other ones - // - // NB: we still must include QS_ALLINPUT even when waiting - // in a secondary thread because if it had created some - // window somehow (possible not even using wxWindows) - // the system might dead lock then - if ( wxThread::IsMain() ) + wxAppTraits *traits = wxTheApp ? wxTheApp->GetTraits() + : NULL; + + if ( traits && !traits->DoMessageFromThreadWait() ) { - // it looks that sometimes WAIT_OBJECT_0 + 1 is - // returned but there are no messages in the thread - // queue -- prevent DoMessageFromThreadWait() from - // blocking inside ::GetMessage() forever in this case - ::PostMessage(NULL, WM_NULL, 0, 0); - - wxAppTraits *traits = wxTheApp ? wxTheApp->GetTraits() - : NULL; - - if ( traits && !traits->DoMessageFromThreadWait() ) - { - // WM_QUIT received: kill the thread - Kill(); - - return wxTHREAD_KILLED; - } - } - break; + // WM_QUIT received: kill the thread + Kill(); - default: - wxFAIL_MSG(wxT("unexpected result of MsgWaitForMultipleObject")); - } - } while ( result != WAIT_OBJECT_0 ); + return wxTHREAD_KILLED; + } + } + break; - if ( wxThread::IsMain() ) - { - gs_waitingForThread = FALSE; + default: + wxFAIL_MSG(wxT("unexpected result of MsgWaitForMultipleObject")); } + } while ( result != WAIT_OBJECT_0 ); + + if ( wxThread::IsMain() ) + { + 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 +808,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,18 +823,18 @@ 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 // we are going to terminate without running any user code - if we did it, - // the codei n Delete() wouldn't work + // the code in WaitForTerminate() wouldn't work if ( m_state != STATE_EXITED ) { m_state = STATE_RUNNING; } - return TRUE; + return true; } // static functions @@ -803,7 +857,7 @@ wxThread *wxThread::This() bool wxThread::IsMain() { - return ::GetCurrentThreadId() == gs_idMainThread; + return ::GetCurrentThreadId() == gs_idMainThread || gs_idMainThread == 0; } void wxThread::Yield() @@ -833,7 +887,10 @@ unsigned long wxThread::GetCurrentId() bool wxThread::SetConcurrency(size_t level) { -#ifndef __WXWINCE__ +#ifdef __WXWINCE__ + wxUnusedVar(level); + return false; +#else wxASSERT_MSG( IsMain(), _T("should only be called from the main thread") ); // ok only for the default one @@ -842,12 +899,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 +943,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 +971,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; + + return true; +#endif // __WXWINCE__/!__WXWINCE__ } // ctor and dtor @@ -932,7 +990,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 +1056,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 +1191,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 +1203,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 +1211,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 +1226,9 @@ void wxThreadModule::OnExit() wxLogLastError(wxT("TlsFree failed.")); } + delete gs_critsectThreadDelete; + gs_critsectThreadDelete = NULL; + if ( gs_critsectGui ) { gs_critsectGui->Leave(); @@ -1210,7 +1273,7 @@ void WXDLLIMPEXP_BASE wxMutexGuiLeave() if ( wxThread::IsMain() ) { - gs_bGuiOwnedByMainThread = FALSE; + gs_bGuiOwnedByMainThread = false; } else { @@ -1241,7 +1304,7 @@ void WXDLLIMPEXP_BASE wxMutexGuiLeaveOrEnter() { gs_critsectGui->Enter(); - gs_bGuiOwnedByMainThread = TRUE; + gs_bGuiOwnedByMainThread = true; } //else: already have it, nothing to do }