Allow wxThread::Wait() and Delete() to block, even under wxMSW.

Add "wait mode" parameter to these methods which can be used to make them
block even under wxMSW where they currently dispatch messages when called
which can be totally unexpected.

Do keep the old behaviour for compatibility however, although it will change i
3.2.

Closes #12998.

git-svn-id: https://svn.wxwidgets.org/svn/wx/wxWidgets/trunk@67185 c3d73ce0-8a6f-49c7-b76d-6d57e0e08775
This commit is contained in:
Vadim Zeitlin 2011-03-14 11:54:32 +00:00
parent 31dcbd12ef
commit b95a7c3144
12 changed files with 111 additions and 24 deletions

View File

@ -188,6 +188,13 @@ Changes in behaviour not resulting in compilation errors, please read this!
StartDrag() virtual methods changed. You will need to change them in your
derived renderer class too if you override them.
- wxThread::Wait() and wxThread::Delete() used to dispatch the events while
waiting for the thread to exit in wxMSW. They still do it in default build
with WXWIN_COMPATIBILITY_2_8 defined but won't dispatch any events, i.e. the
default wait mode will become wxTHREAD_WAIT_BLOCK in the next wxWidgets
release so you are strongly encouraged to upgrade your code to stop relying
on this behaviour.
Changes in behaviour which may result in compilation errors
-----------------------------------------------------------
@ -439,6 +446,7 @@ All:
- Added wxIMAGE_OPTION_GIF_COMMENT to read and write GIF comments (troelsk).
- Added wxStack<> template class.
- Added precision parameter to wxString::From[C]Double().
- Added wxThread::Wait() and Delete() "wait mode" parameter (Catalin Raceanu).
Unix:

View File

@ -40,7 +40,7 @@ public:
// wait for the handle to be signaled, return WAIT_OBJECT_0 if it is or, in
// the GUI code, WAIT_OBJECT_0 + 1 if a Windows message arrived
virtual WXDWORD WaitForThread(WXHANDLE hThread) = 0;
virtual WXDWORD WaitForThread(WXHANDLE hThread, int flags) = 0;
#ifndef __WXWINCE__

View File

@ -26,7 +26,7 @@ public:
virtual wxTimerImpl *CreateTimerImpl(wxTimer *timer);
#endif
virtual bool DoMessageFromThreadWait();
virtual WXDWORD WaitForThread(WXHANDLE hThread);
virtual WXDWORD WaitForThread(WXHANDLE hThread, int flags);
#ifndef __WXWINCE__
virtual bool CanUseStderr() { return true; }
virtual bool WriteToStderr(const wxString& text);
@ -46,7 +46,7 @@ public:
#endif
virtual bool DoMessageFromThreadWait();
virtual wxPortId GetToolkitVersion(int *majVer = NULL, int *minVer = NULL) const;
virtual WXDWORD WaitForThread(WXHANDLE hThread);
virtual WXDWORD WaitForThread(WXHANDLE hThread, int flags);
#ifndef __WXWINCE__
virtual bool CanUseStderr();

View File

@ -71,6 +71,21 @@ enum wxThreadKind
wxTHREAD_JOINABLE
};
enum wxThreadWait
{
wxTHREAD_WAIT_BLOCK,
wxTHREAD_WAIT_YIELD, // process events while waiting; MSW only
// For compatibility reasons we use wxTHREAD_WAIT_YIELD by default as this
// was the default behaviour of wxMSW 2.8 but it should be avoided as it's
// dangerous and not portable.
#if WXWIN_COMPATIBILITY_2_8
wxTHREAD_WAIT_DEFAULT = wxTHREAD_WAIT_YIELD
#else
wxTHREAD_WAIT_DEFAULT = wxTHREAD_WAIT_BLOCK
#endif
};
// defines the interval of priority
enum
{
@ -516,13 +531,14 @@ public:
// does it!
//
// will fill the rc pointer with the thread exit code if it's !NULL
wxThreadError Delete(ExitCode *rc = NULL);
wxThreadError Delete(ExitCode *rc = NULL,
wxThreadWait waitMode = wxTHREAD_WAIT_DEFAULT);
// waits for a joinable thread to finish and returns its exit code
//
// Returns (ExitCode)-1 on error (for example, if the thread is not
// joinable)
ExitCode Wait();
ExitCode Wait(wxThreadWait waitMode = wxTHREAD_WAIT_DEFAULT);
// kills the thread without giving it any chance to clean up - should
// not be used under normal circumstances, use Delete() instead.

View File

@ -591,6 +591,46 @@ public:
void Leave();
};
/**
The possible thread wait types.
@since 2.9.2
*/
enum wxThreadWait
{
/**
No events are processed while waiting.
This is the default under all platforms except for wxMSW.
*/
wxTHREAD_WAIT_BLOCK,
/**
Yield for event dispatching while waiting.
This flag is dangerous as it exposes the program using it to unexpected
reentrancies in the same way as calling wxYield() function does so you
are strongly advised to avoid its use and not wait for the thread
termination from the main (GUI) thread at all to avoid making your
application unresponsive.
Also notice that this flag is not portable as it is only implemented in
wxMSW and simply ignored under the other platforms.
*/
wxTHREAD_WAIT_YIELD,
/**
Default wait mode for wxThread::Wait() and wxThread::Delete().
For compatibility reasons, the default wait mode is currently
wxTHREAD_WAIT_YIELD if WXWIN_COMPATIBILITY_2_8 is defined (and it is
by default). However, as mentioned above, you're strongly encouraged to
not use wxTHREAD_WAIT_YIELD and pass wxTHREAD_WAIT_BLOCK to wxThread
method explicitly.
*/
wxTHREAD_WAIT_DEFAULT = wxTHREAD_WAIT_YIELD
};
/**
The possible thread kinds.
*/
@ -1001,6 +1041,15 @@ public:
Calling Delete() gracefully terminates a @b detached thread, either when
the thread calls TestDestroy() or when it finishes processing.
@param rc
The thread exit code, if rc is not NULL.
@param waitMode
As described in wxThreadWait documentation, wxTHREAD_WAIT_BLOCK
should be used as the wait mode even although currently
wxTHREAD_WAIT_YIELD is for compatibility reasons. This parameter is
new in wxWidgets 2.9.2.
@note
This function works on a joinable thread but in that case makes
the TestDestroy() function of the thread return @true and then
@ -1009,7 +1058,8 @@ public:
See @ref thread_deletion for a broader explanation of this routine.
*/
wxThreadError Delete(void** rc = NULL);
wxThreadError Delete(ExitCode *rc = NULL,
wxThreadWait waitMode = wxTHREAD_WAIT_BLOCK);
/**
Returns the number of system CPUs or -1 if the value is unknown.
@ -1224,9 +1274,15 @@ public:
This function can only be called from another thread context.
@param waitMode
As described in wxThreadWait documentation, wxTHREAD_WAIT_BLOCK
should be used as the wait mode even although currently
wxTHREAD_WAIT_YIELD is for compatibility reasons. This parameter is
new in wxWidgets 2.9.2.
See @ref thread_deletion for a broader explanation of this routine.
*/
ExitCode Wait();
ExitCode Wait(wxThreadWait flags = wxTHREAD_WAIT_BLOCK);
/**
Give the rest of the thread's time-slice to the system allowing the other

View File

@ -233,15 +233,20 @@ bool wxGUIAppTraits::DoMessageFromThreadWait()
return evtLoop->Dispatch();
}
DWORD wxGUIAppTraits::WaitForThread(WXHANDLE hThread)
DWORD wxGUIAppTraits::WaitForThread(WXHANDLE hThread, int flags)
{
// We only ever dispatch messages from the main thread and, additionally,
// even from the main thread we shouldn't wait for the message if we don't
// have a running event loop as we would never remove them from the message
// queue then and so we would enter an infinite loop as
// MsgWaitForMultipleObjects() keeps returning WAIT_OBJECT_0 + 1.
if ( !wxIsMainThread() || !wxEventLoop::GetActive() )
if ( flags == wxTHREAD_WAIT_BLOCK ||
!wxIsMainThread() ||
!wxEventLoop::GetActive() )
{
// Simple blocking wait.
return DoSimpleWaitForThread(hThread);
}
return ::MsgWaitForMultipleObjects
(

View File

@ -88,7 +88,7 @@ wxEventLoopBase *wxConsoleAppTraits::CreateEventLoop()
}
WXDWORD wxConsoleAppTraits::WaitForThread(WXHANDLE hThread)
WXDWORD wxConsoleAppTraits::WaitForThread(WXHANDLE hThread, int WXUNUSED(flags))
{
return DoSimpleWaitForThread(hThread);
}

View File

@ -450,6 +450,7 @@ public:
// (politely, this is not Kill()!) to do it
wxThreadError WaitForTerminate(wxCriticalSection& cs,
wxThread::ExitCode *pRc,
wxThreadWait waitMode,
wxThread *threadToDelete = NULL);
// kill the thread unconditionally
@ -703,6 +704,7 @@ wxThreadError wxThreadInternal::Kill()
wxThreadError
wxThreadInternal::WaitForTerminate(wxCriticalSection& cs,
wxThread::ExitCode *pRc,
wxThreadWait waitMode,
wxThread *threadToDelete)
{
// prevent the thread C++ object from disappearing as long as we are using
@ -792,7 +794,7 @@ wxThreadInternal::WaitForTerminate(wxCriticalSection& cs,
wxAppTraits *traits = wxTheApp ? wxTheApp->GetTraits() : NULL;
if ( traits )
{
result = traits->WaitForThread(m_hThread);
result = traits->WaitForThread(m_hThread, waitMode);
}
else // can't wait for the thread
{
@ -1108,7 +1110,7 @@ wxThreadError wxThread::Resume()
// stopping thread
// ---------------
wxThread::ExitCode wxThread::Wait()
wxThread::ExitCode wxThread::Wait(wxThreadWait waitMode)
{
ExitCode rc = wxUIntToPtr(THREAD_ERROR_EXIT);
@ -1117,14 +1119,14 @@ wxThread::ExitCode wxThread::Wait()
wxCHECK_MSG( !IsDetached(), rc,
wxT("wxThread::Wait(): can't wait for detached thread") );
(void)m_internal->WaitForTerminate(m_critsect, &rc);
(void)m_internal->WaitForTerminate(m_critsect, &rc, waitMode);
return rc;
}
wxThreadError wxThread::Delete(ExitCode *pRc)
wxThreadError wxThread::Delete(ExitCode *pRc, wxThreadWait waitMode)
{
return m_internal->WaitForTerminate(m_critsect, pRc, this);
return m_internal->WaitForTerminate(m_critsect, pRc, waitMode, this);
}
wxThreadError wxThread::Kill()

View File

@ -661,18 +661,18 @@ wxThreadError wxThread::Resume()
// stopping thread
// ---------------
wxThread::ExitCode wxThread::Wait()
wxThread::ExitCode wxThread::Wait(wxThreadWait waitMode)
{
// 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,
wxT("can't wait for detached thread") );
ExitCode rc = (ExitCode)-1;
(void)Delete(&rc);
(void)Delete(&rc, waitMode);
return(rc);
}
wxThreadError wxThread::Delete(ExitCode *pRc)
wxThreadError wxThread::Delete(ExitCode *pRc, wxThreadWait WXUNUSED(waitMode))
{
ExitCode rc = 0;

View File

@ -940,7 +940,7 @@ wxThreadError wxThread::Resume()
// exiting thread
// -----------------------------------------------------------------------------
wxThread::ExitCode wxThread::Wait()
wxThread::ExitCode wxThread::Wait(wxThreadWait WXUNUSED(waitMode))
{
wxCHECK_MSG( This() != this, (ExitCode)-1,
wxT("a thread can't wait for itself") );
@ -953,7 +953,7 @@ wxThread::ExitCode wxThread::Wait()
return m_internal->GetExitCode();
}
wxThreadError wxThread::Delete(ExitCode *rc)
wxThreadError wxThread::Delete(ExitCode *rc, wxThreadWait WXUNUSED(waitMode))
{
wxCHECK_MSG( This() != this, wxTHREAD_MISC_ERROR,
wxT("a thread can't delete itself") );

View File

@ -467,12 +467,12 @@ wxThreadError wxThread::Resume()
// stopping thread
// ---------------
wxThread::ExitCode wxThread::Wait()
wxThread::ExitCode wxThread::Wait(wxThreadWait WXUNUSED(waitMode))
{
return 0;
}
wxThreadError wxThread::Delete(ExitCode *pRc)
wxThreadError wxThread::Delete(ExitCode *pRc, wxThreadWait WXUNUSED(waitMode))
{
return wxTHREAD_NO_ERROR;
}

View File

@ -1425,7 +1425,7 @@ wxThreadError wxThread::Resume()
// exiting thread
// -----------------------------------------------------------------------------
wxThread::ExitCode wxThread::Wait()
wxThread::ExitCode wxThread::Wait(wxThreadWait WXUNUSED(waitMode))
{
wxCHECK_MSG( This() != this, (ExitCode)-1,
wxT("a thread can't wait for itself") );
@ -1438,7 +1438,7 @@ wxThread::ExitCode wxThread::Wait()
return m_internal->GetExitCode();
}
wxThreadError wxThread::Delete(ExitCode *rc)
wxThreadError wxThread::Delete(ExitCode *rc, wxThreadWait WXUNUSED(waitMode))
{
wxCHECK_MSG( This() != this, wxTHREAD_MISC_ERROR,
wxT("a thread can't delete itself") );