From 68d4172048cfc0f3e658ef90ff5af926eedfdd57 Mon Sep 17 00:00:00 2001 From: Vadim Zeitlin Date: Thu, 10 Jul 2003 17:10:16 +0000 Subject: [PATCH] Wait() doesn't cancel the thread any longer git-svn-id: https://svn.wxwidgets.org/svn/wx/wxWidgets/trunk@21872 c3d73ce0-8a6f-49c7-b76d-6d57e0e08775 --- docs/changes.txt | 1 + src/msw/thread.cpp | 355 +++++++++++++++++++++++++-------------------- 2 files changed, 195 insertions(+), 161 deletions(-) diff --git a/docs/changes.txt b/docs/changes.txt index aa62b91f4d..a60e724bbf 100644 --- a/docs/changes.txt +++ b/docs/changes.txt @@ -163,6 +163,7 @@ wxMSW: - support for accelerator keys in the owner drawn menus (Derry Bryson) - wxCaret::SetSize() doesn't hide the caret any longer as it used to - wxCheckListBox::Check() doesn't send CHECKLISTBOX_TOGGLE event any more +- fixed bugs in wxThread::Wait() and IsAlive() - fixed bug with wxTR_EDIT_LABELS not working with wxTR_MULTIPLE - fixes for compilation with OpenWatcom and DigitalMars compilers - fixed wxStaticText best size calculation (was wrong by '&' width) diff --git a/src/msw/thread.cpp b/src/msw/thread.cpp index 26a0cbd3fa..d59a960162 100644 --- a/src/msw/thread.cpp +++ b/src/msw/thread.cpp @@ -527,6 +527,15 @@ public: // create a new (suspended) thread (for the given thread object) bool Create(wxThread *thread, unsigned int stackSize); + // 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); + + // kill the thread unconditionally + wxThreadError Kill(); + // suspend/resume/terminate bool Suspend(); bool Resume(); @@ -683,6 +692,176 @@ bool wxThreadInternal::Create(wxThread *thread, unsigned int stackSize) return TRUE; } +wxThreadError wxThreadInternal::Kill() +{ + if ( !::TerminateThread(m_hThread, (DWORD)-1) ) + { + wxLogSysError(_("Couldn't terminate thread")); + + return wxTHREAD_MISC_ERROR; + } + + Free(); + + return wxTHREAD_NO_ERROR; +} + +wxThreadError +wxThreadInternal::WaitForTerminate(bool shouldCancel, + wxCriticalSection& cs, + wxThread::ExitCode *pRc) +{ + 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; + + // check if the thread already started to run + { + wxCriticalSectionLocker lock(cs); + + if ( m_state == STATE_NEW ) + { + if ( shouldCancel ) + { + // 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; + } + + isRunning = TRUE; + + // shouldResume is correctly set to FALSE here + } + else + { + shouldResume = m_state == STATE_PAUSED; + } + } + + // resume the thread if it is paused + if ( shouldResume ) + Resume(); + + // does is still run? + if ( isRunning || m_state == STATE_RUNNING ) + { + if ( wxThread::IsMain() ) + { + // set flag for wxIsWaitingForThread() + gs_waitingForThread = TRUE; + } + + // ask the thread to terminate + if ( shouldCancel ) + { + wxCriticalSectionLocker lock(cs); + + Cancel(); + } + + // 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() ) + { + wxMutexGuiLeave(); + } + } + + 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 + { + // 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; + + 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 + { + if ( !::GetExitCodeThread(m_hThread, (LPDWORD)&rc) ) + { + wxLogLastError(wxT("GetExitCodeThread")); + + rc = (wxThread::ExitCode)-1; + } + } while ( (DWORD)rc == STILL_ACTIVE ); + + if ( pRc ) + *pRc = rc; + + return rc == (wxThread::ExitCode)-1 ? wxTHREAD_MISC_ERROR + : wxTHREAD_NO_ERROR; +} + bool wxThreadInternal::Suspend() { DWORD nSuspendCount = ::SuspendThread(m_hThread); @@ -933,175 +1112,36 @@ wxThread::ExitCode wxThread::Wait() // although under Windows we can wait for any thread, it's an error to // wait for a detached one in wxWin API wxCHECK_MSG( !IsDetached(), (ExitCode)-1, - _T("can't wait for detached thread") ); + _T("wxThread::Wait(): can't wait for detached thread") ); ExitCode rc = (ExitCode)-1; - (void)Delete(&rc); + (void)m_internal->WaitForTerminate(false, m_critsect, &rc); m_internal->Free(); + wxCriticalSectionLocker lock(m_critsect); + m_internal->SetState(STATE_EXITED); + return rc; } wxThreadError wxThread::Delete(ExitCode *pRc) { - 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, - shouldCancel = TRUE, - isRunning = FALSE; - - // check if the thread already started to run - { - wxCriticalSectionLocker lock(m_critsect); - - if ( m_internal->GetState() == STATE_NEW ) - { - // 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_internal->SetState(STATE_EXITED); - - Resume(); // it knows about STATE_EXITED special case - - shouldCancel = FALSE; - isRunning = TRUE; - - // shouldResume is correctly set to FALSE here - } - else - { - shouldResume = IsPaused(); - } - } - - // resume the thread if it is paused - if ( shouldResume ) - Resume(); - - HANDLE hThread = m_internal->GetHandle(); - - // does is still run? - if ( isRunning || IsRunning() ) - { - if ( IsMain() ) - { - // set flag for wxIsWaitingForThread() - gs_waitingForThread = TRUE; - } - - // ask the thread to terminate - if ( shouldCancel ) - { - wxCriticalSectionLocker lock(m_critsect); - - m_internal->Cancel(); - } - - // 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 ( IsMain() ) - { - // give the thread we're waiting for chance to do the GUI call - // it might be in - if ( (gs_nWaitingForGui > 0) && wxGuiOwnedByMainThread() ) - { - wxMutexGuiLeave(); - } - } - - result = ::MsgWaitForMultipleObjects - ( - 1, // number of objects to wait for - &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 - { - // 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; - - default: - wxFAIL_MSG(wxT("unexpected result of MsgWaitForMultipleObject")); - } - } while ( result != WAIT_OBJECT_0 ); - - if ( 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 - { - if ( !::GetExitCodeThread(hThread, (LPDWORD)&rc) ) - { - wxLogLastError(wxT("GetExitCodeThread")); - - rc = (ExitCode)-1; - } - } while ( (DWORD)rc == STILL_ACTIVE ); + wxThreadError rc = m_internal->WaitForTerminate(true, m_critsect, pRc); if ( IsDetached() ) { - // if the thread exits normally, this is done in WinThreadStart, but in - // this case it would have been too early because - // MsgWaitForMultipleObject() would fail if the thread handle was - // closed while we were waiting on it, so we must do it here delete this; } + else // joinable + { + // update the status of the joinable thread + wxCriticalSectionLocker lock(m_critsect); + m_internal->SetState(STATE_EXITED); + } - if ( pRc ) - *pRc = rc; - - return rc == (ExitCode)-1 ? wxTHREAD_MISC_ERROR : wxTHREAD_NO_ERROR; + return rc; } wxThreadError wxThread::Kill() @@ -1109,14 +1149,7 @@ wxThreadError wxThread::Kill() if ( !IsRunning() ) return wxTHREAD_NOT_RUNNING; - if ( !::TerminateThread(m_internal->GetHandle(), (DWORD)-1) ) - { - wxLogSysError(_("Couldn't terminate thread")); - - return wxTHREAD_MISC_ERROR; - } - - m_internal->Free(); + wxThreadError rc = m_internal->Kill(); if ( IsDetached() ) { @@ -1129,7 +1162,7 @@ wxThreadError wxThread::Kill() m_internal->SetState(STATE_EXITED); } - return wxTHREAD_NO_ERROR; + return rc; } void wxThread::Exit(ExitCode status) -- 2.45.2