Wait() should now return correct exit code even if thread state was EXITED (replaces patch 1166425)

git-svn-id: https://svn.wxwidgets.org/svn/wx/wxWidgets/trunk@33007 c3d73ce0-8a6f-49c7-b76d-6d57e0e08775
This commit is contained in:
Vadim Zeitlin
2005-03-23 21:29:04 +00:00
parent 7dccdf8120
commit 0f00be4b33

View File

@@ -484,9 +484,9 @@ THREAD_RETVAL THREAD_CALLCONV wxThreadInternal::WinThreadStart(void *param)
// first of all, check whether we hadn't been cancelled already and don't // first of all, check whether we hadn't been cancelled already and don't
// start the user code at all then // start the user code at all then
bool isExited = (thread->m_internal->GetState() == STATE_EXITED); const bool hasExited = thread->m_internal->GetState() == STATE_EXITED;
if ( isExited ) if ( hasExited )
{ {
rc = (THREAD_RETVAL)-1; rc = (THREAD_RETVAL)-1;
} }
@@ -509,7 +509,7 @@ THREAD_RETVAL THREAD_CALLCONV wxThreadInternal::WinThreadStart(void *param)
// threads after state is changed to STATE_EXITED. // threads after state is changed to STATE_EXITED.
bool isDetached = thread->IsDetached(); bool isDetached = thread->IsDetached();
if (!isExited) if ( !hasExited )
{ {
// enter m_critsect before changing the thread state // enter m_critsect before changing the thread state
wxCriticalSectionLocker lock(thread->m_critsect); wxCriticalSectionLocker lock(thread->m_critsect);
@@ -517,7 +517,8 @@ THREAD_RETVAL THREAD_CALLCONV wxThreadInternal::WinThreadStart(void *param)
} }
// the thread may delete itself now if it wants, we don't need it any more // the thread may delete itself now if it wants, we don't need it any more
if (isDetached) thread->m_internal->LetDie(); if ( isDetached )
thread->m_internal->LetDie();
return rc; return rc;
} }
@@ -633,14 +634,11 @@ wxThreadInternal::WaitForTerminate(wxCriticalSection& cs,
wxThread::ExitCode rc = 0; wxThread::ExitCode rc = 0;
// Delete() is always safe to call, so consider all possible states // we might need to resume the thread if it's currently stopped
bool shouldResume = false;
// we might need to resume the thread, but we might also not need to cancel // as Delete() (which calls us) is always safe to call we need to consider
// it if it doesn't run yet // all possible states
bool shouldResume = false,
isRunning = false;
// check if the thread already started to run
{ {
wxCriticalSectionLocker lock(cs); wxCriticalSectionLocker lock(cs);
@@ -653,20 +651,17 @@ wxThreadInternal::WaitForTerminate(wxCriticalSection& cs,
// to let it run // to let it run
m_state = STATE_EXITED; m_state = STATE_EXITED;
Resume(); // it knows about STATE_EXITED special case // 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; shouldDelete = false;
} }
//else: shouldResume is correctly set to false here, wait until
isRunning = true; // someone else runs the thread and it finishes
// shouldResume is correctly set to false here
} }
else if ( m_state == STATE_EXITED ) else // running, paused, cancelled or even exited
{
return wxTHREAD_NOT_RUNNING;
}
else // running (but maybe paused or cancelled)
{ {
shouldResume = m_state == STATE_PAUSED; shouldResume = m_state == STATE_PAUSED;
} }
@@ -676,104 +671,103 @@ wxThreadInternal::WaitForTerminate(wxCriticalSection& cs,
if ( shouldResume ) if ( shouldResume )
Resume(); Resume();
// is it still running? // ask the thread to terminate
if ( isRunning || m_state == STATE_RUNNING ) 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() ) if ( wxThread::IsMain() )
{ {
// set flag for wxIsWaitingForThread() // give the thread we're waiting for chance to do the GUI call
gs_waitingForThread = true; // it might be in
} if ( (gs_nWaitingForGui > 0) && wxGuiOwnedByMainThread() )
// ask the thread to terminate
if ( shouldDelete )
{
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 wxDUMMY_INITIALIZE(0);
do
{
if ( wxThread::IsMain() )
{ {
// give the thread we're waiting for chance to do the GUI call wxMutexGuiLeave();
// it might be in }
if ( (gs_nWaitingForGui > 0) && wxGuiOwnedByMainThread() ) }
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 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 wxAppTraits *traits = wxTheApp ? wxTheApp->GetTraits()
( : NULL;
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 ) if ( traits && !traits->DoMessageFromThreadWait() )
{
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 wxWidgets)
// the system might dead lock then
if ( wxThread::IsMain() )
{ {
// it looks that sometimes WAIT_OBJECT_0 + 1 is // WM_QUIT received: kill the thread
// returned but there are no messages in the thread Kill();
// queue -- prevent DoMessageFromThreadWait() from
// blocking inside ::GetMessage() forever in this case
::PostMessage(NULL, WM_NULL, 0, 0);
wxAppTraits *traits = wxTheApp ? wxTheApp->GetTraits() return wxTHREAD_KILLED;
: NULL;
if ( traits && !traits->DoMessageFromThreadWait() )
{
// WM_QUIT received: kill the thread
Kill();
return wxTHREAD_KILLED;
}
} }
break; }
break;
default: default:
wxFAIL_MSG(wxT("unexpected result of MsgWaitForMultipleObject")); wxFAIL_MSG(wxT("unexpected result of MsgWaitForMultipleObject"));
}
} while ( result != WAIT_OBJECT_0 );
if ( wxThread::IsMain() )
{
gs_waitingForThread = false;
} }
} while ( result != WAIT_OBJECT_0 );
if ( wxThread::IsMain() )
{
gs_waitingForThread = false;
} }
// although the thread might be already in the EXITED state it might not // 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 // have terminated yet and so we are not sure that it has actually
// terminated if the "if" above hadn't been taken // terminated if the "if" above hadn't been taken
@@ -834,7 +828,7 @@ bool wxThreadInternal::Resume()
// don't change the state from STATE_EXITED because it's special and means // 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, // 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 ) if ( m_state != STATE_EXITED )
{ {
m_state = STATE_RUNNING; m_state = STATE_RUNNING;