trying to fix race conditions. double deletions and memory leaks on thread termination (closes bug 775774 and replaces patch 781918)

git-svn-id: https://svn.wxwidgets.org/svn/wx/wxWidgets/trunk@23788 c3d73ce0-8a6f-49c7-b76d-6d57e0e08775
This commit is contained in:
Vadim Zeitlin
2003-09-21 23:57:09 +00:00
parent ff3985971d
commit 3ef22a5b02

View File

@@ -112,8 +112,8 @@ static DWORD gs_tlsThisThread = 0xFFFFFFFF;
// calling wxMutexGuiEnter() // calling wxMutexGuiEnter()
static DWORD gs_idMainThread = 0; static DWORD gs_idMainThread = 0;
// if it's FALSE, some secondary thread is holding the GUI lock // if it's false, some secondary thread is holding the GUI lock
static bool gs_bGuiOwnedByMainThread = TRUE; static bool gs_bGuiOwnedByMainThread = true;
// critical section which controls access to all GUI functions: any secondary // 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 // 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 // critical section which protects gs_nWaitingForGui variable
static wxCriticalSection *gs_critsectWaitingForGui = NULL; 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() // number of threads waiting for GUI in wxMutexGuiEnter()
static size_t gs_nWaitingForGui = 0; static size_t gs_nWaitingForGui = 0;
// are we waiting for a thread termination? // are we waiting for a thread termination?
static bool gs_waitingForThread = FALSE; static bool gs_waitingForThread = false;
// ============================================================================ // ============================================================================
// Windows implementation of thread and related classes // Windows implementation of thread and related classes
@@ -191,7 +196,7 @@ wxMutexInternal::wxMutexInternal(wxMutexType WXUNUSED(mutexType))
m_mutex = ::CreateMutex m_mutex = ::CreateMutex
( (
NULL, // default secutiry attributes NULL, // default secutiry attributes
FALSE, // not initially locked false, // not initially locked
NULL // no name NULL // no name
); );
@@ -370,11 +375,13 @@ wxSemaError wxSemaphoreInternal::Post()
class wxThreadInternal class wxThreadInternal
{ {
public: public:
wxThreadInternal() wxThreadInternal(wxThread *thread)
{ {
m_thread = thread;
m_hThread = 0; m_hThread = 0;
m_state = STATE_NEW; m_state = STATE_NEW;
m_priority = WXTHREAD_DEFAULT_PRIORITY; m_priority = WXTHREAD_DEFAULT_PRIORITY;
m_nRef = 1;
} }
~wxThreadInternal() ~wxThreadInternal()
@@ -400,9 +407,9 @@ public:
// wait for the thread to terminate, either by itself, or by asking it // wait for the thread to terminate, either by itself, or by asking it
// (politely, this is not Kill()!) to do it // (politely, this is not Kill()!) to do it
wxThreadError WaitForTerminate(bool shouldCancel, wxThreadError WaitForTerminate(wxCriticalSection& cs,
wxCriticalSection& cs, wxThread::ExitCode *pRc,
wxThread::ExitCode *pRc); wxThread *threadToDelete = NULL);
// kill the thread unconditionally // kill the thread unconditionally
wxThreadError Kill(); wxThreadError Kill();
@@ -427,27 +434,59 @@ public:
// thread function // thread function
static THREAD_RETVAL THREAD_CALLCONV WinThreadStart(void *thread); 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: private:
// the thread we're associated with
wxThread *m_thread;
HANDLE m_hThread; // handle of the thread HANDLE m_hThread; // handle of the thread
wxThreadState m_state; // state, see wxThreadState enum wxThreadState m_state; // state, see wxThreadState enum
unsigned int m_priority; // thread priority in "wx" units unsigned int m_priority; // thread priority in "wx" units
DWORD m_tid; // thread id 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) 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 THREAD_CALLCONV wxThreadInternal::WinThreadStart(void *param)
{ {
THREAD_RETVAL rc; THREAD_RETVAL rc;
bool wasCancelled;
wxThread * const thread = (wxThread *)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
wxThread *thread = (wxThread *)param;
if ( thread->m_internal->GetState() == STATE_EXITED ) if ( thread->m_internal->GetState() == STATE_EXITED )
{ {
rc = (THREAD_RETVAL)-1; rc = (THREAD_RETVAL)-1;
wasCancelled = TRUE;
} }
else // do run thread else // do run thread
{ {
@@ -462,22 +501,15 @@ THREAD_RETVAL THREAD_CALLCONV wxThreadInternal::WinThreadStart(void *param)
rc = (THREAD_RETVAL)thread->Entry(); rc = (THREAD_RETVAL)thread->Entry();
// enter m_critsect before changing the thread state // enter m_critsect before changing the thread state
thread->m_critsect.Enter(); wxCriticalSectionLocker lock(thread->m_critsect);
wasCancelled = thread->m_internal->GetState() == STATE_CANCELED;
thread->m_internal->SetState(STATE_EXITED); thread->m_internal->SetState(STATE_EXITED);
thread->m_critsect.Leave();
} }
thread->OnExit(); thread->OnExit();
// if the thread was cancelled (from Delete()), then its handle is still // the thread may delete itself now if it wants, we don't need it any more
// needed there thread->m_internal->LetDie();
if ( thread->IsDetached() && !wasCancelled )
{
// auto delete
delete thread;
}
//else: the joinable threads handle will be closed when Wait() is done
return rc; return rc;
} }
@@ -552,7 +584,7 @@ bool wxThreadInternal::Create(wxThread *thread, unsigned int stackSize)
{ {
wxLogSysError(_("Can't create thread")); wxLogSysError(_("Can't create thread"));
return FALSE; return false;
} }
if ( m_priority != WXTHREAD_DEFAULT_PRIORITY ) if ( m_priority != WXTHREAD_DEFAULT_PRIORITY )
@@ -560,7 +592,7 @@ bool wxThreadInternal::Create(wxThread *thread, unsigned int stackSize)
SetPriority(m_priority); SetPriority(m_priority);
} }
return TRUE; return true;
} }
wxThreadError wxThreadInternal::Kill() wxThreadError wxThreadInternal::Kill()
@@ -578,18 +610,27 @@ wxThreadError wxThreadInternal::Kill()
} }
wxThreadError wxThreadError
wxThreadInternal::WaitForTerminate(bool shouldCancel, wxThreadInternal::WaitForTerminate(wxCriticalSection& cs,
wxCriticalSection& cs, wxThread::ExitCode *pRc,
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; wxThread::ExitCode rc = 0;
// Delete() is always safe to call, so consider all possible states // 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 // we might need to resume the thread, but we might also not need to cancel
// it if it doesn't run yet // it if it doesn't run yet
bool shouldResume = FALSE, bool shouldResume = false,
isRunning = FALSE; isRunning = false;
// check if the thread already started to run // check if the thread already started to run
{ {
@@ -597,7 +638,7 @@ wxThreadInternal::WaitForTerminate(bool shouldCancel,
if ( m_state == STATE_NEW ) if ( m_state == STATE_NEW )
{ {
if ( shouldCancel ) if ( shouldDelete )
{ {
// WinThreadStart() will see it and terminate immediately, no // WinThreadStart() will see it and terminate immediately, no
// need to cancel the thread -- but we still need to resume it // need to cancel the thread -- but we still need to resume it
@@ -606,12 +647,12 @@ wxThreadInternal::WaitForTerminate(bool shouldCancel,
Resume(); // it knows about STATE_EXITED special case 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 ) else if ( m_state == STATE_EXITED )
{ {
@@ -633,11 +674,11 @@ wxThreadInternal::WaitForTerminate(bool shouldCancel,
if ( wxThread::IsMain() ) if ( wxThread::IsMain() )
{ {
// set flag for wxIsWaitingForThread() // set flag for wxIsWaitingForThread()
gs_waitingForThread = TRUE; gs_waitingForThread = true;
} }
// ask the thread to terminate // ask the thread to terminate
if ( shouldCancel ) if ( shouldDelete )
{ {
wxCriticalSectionLocker lock(cs); wxCriticalSectionLocker lock(cs);
@@ -666,7 +707,7 @@ wxThreadInternal::WaitForTerminate(bool shouldCancel,
( (
1, // number of objects to wait for 1, // number of objects to wait for
&m_hThread, // the objects &m_hThread, // the objects
FALSE, // don't wait for all objects false, // don't wait for all objects
INFINITE, // no timeout INFINITE, // no timeout
QS_ALLINPUT | // return as soon as there are any events QS_ALLINPUT | // return as soon as there are any events
QS_ALLPOSTMESSAGE QS_ALLPOSTMESSAGE
@@ -721,7 +762,7 @@ wxThreadInternal::WaitForTerminate(bool shouldCancel,
if ( wxThread::IsMain() ) if ( wxThread::IsMain() )
{ {
gs_waitingForThread = FALSE; gs_waitingForThread = false;
} }
} }
@@ -741,11 +782,9 @@ wxThreadInternal::WaitForTerminate(bool shouldCancel,
if ( pRc ) if ( pRc )
*pRc = rc; *pRc = rc;
// we don't need the thread handle any more // we don't need the thread handle any more in any case
Free(); Free();
wxCriticalSectionLocker lock(cs);
SetState(STATE_EXITED);
return rc == (wxThread::ExitCode)-1 ? wxTHREAD_MISC_ERROR return rc == (wxThread::ExitCode)-1 ? wxTHREAD_MISC_ERROR
: wxTHREAD_NO_ERROR; : wxTHREAD_NO_ERROR;
@@ -758,12 +797,12 @@ bool wxThreadInternal::Suspend()
{ {
wxLogSysError(_("Can not suspend thread %x"), m_hThread); wxLogSysError(_("Can not suspend thread %x"), m_hThread);
return FALSE; return false;
} }
m_state = STATE_PAUSED; m_state = STATE_PAUSED;
return TRUE; return true;
} }
bool wxThreadInternal::Resume() bool wxThreadInternal::Resume()
@@ -773,7 +812,7 @@ bool wxThreadInternal::Resume()
{ {
wxLogSysError(_("Can not resume thread %x"), m_hThread); 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 // don't change the state from STATE_EXITED because it's special and means
@@ -784,7 +823,7 @@ bool wxThreadInternal::Resume()
m_state = STATE_RUNNING; m_state = STATE_RUNNING;
} }
return TRUE; return true;
} }
// static functions // static functions
@@ -851,7 +890,7 @@ bool wxThread::SetConcurrency(size_t level)
{ {
wxLogLastError(_T("GetProcessAffinityMask")); wxLogLastError(_T("GetProcessAffinityMask"));
return FALSE; return false;
} }
// how many CPUs have we got? // how many CPUs have we got?
@@ -890,7 +929,7 @@ bool wxThread::SetConcurrency(size_t level)
{ {
wxLogDebug(_T("bad level %u in wxThread::SetConcurrency()"), 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 // set it: we can't link to SetProcessAffinityMask() because it doesn't
@@ -918,17 +957,17 @@ bool wxThread::SetConcurrency(size_t level)
if ( !pfnSetProcessAffinityMask ) if ( !pfnSetProcessAffinityMask )
{ {
// msg given above - do it only once // msg given above - do it only once
return FALSE; return false;
} }
if ( pfnSetProcessAffinityMask(hProcess, dwProcMask) == 0 ) if ( pfnSetProcessAffinityMask(hProcess, dwProcMask) == 0 )
{ {
wxLogLastError(_T("SetProcessAffinityMask")); wxLogLastError(_T("SetProcessAffinityMask"));
return FALSE; return false;
} }
#endif #endif
return TRUE; return true;
} }
// ctor and dtor // ctor and dtor
@@ -936,7 +975,7 @@ bool wxThread::SetConcurrency(size_t level)
wxThread::wxThread(wxThreadKind kind) wxThread::wxThread(wxThreadKind kind)
{ {
m_internal = new wxThreadInternal(); m_internal = new wxThreadInternal(this);
m_isDetached = kind == wxTHREAD_DETACHED; m_isDetached = kind == wxTHREAD_DETACHED;
} }
@@ -1002,14 +1041,14 @@ wxThread::ExitCode wxThread::Wait()
ExitCode rc = (ExitCode)-1; ExitCode rc = (ExitCode)-1;
(void)m_internal->WaitForTerminate(false, m_critsect, &rc); (void)m_internal->WaitForTerminate(m_critsect, &rc);
return rc; return rc;
} }
wxThreadError wxThread::Delete(ExitCode *pRc) wxThreadError wxThread::Delete(ExitCode *pRc)
{ {
return m_internal->WaitForTerminate(true, m_critsect, pRc); return m_internal->WaitForTerminate(m_critsect, pRc, this);
} }
wxThreadError wxThread::Kill() wxThreadError wxThread::Kill()
@@ -1137,7 +1176,7 @@ bool wxThreadModule::OnInit()
// words, this should never happen // words, this should never happen
wxLogSysError(_("Thread module initialization failed: impossible to allocate index in thread local storage")); 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 // main thread doesn't have associated wxThread object, so store 0 in the
@@ -1149,7 +1188,7 @@ bool wxThreadModule::OnInit()
wxLogSysError(_("Thread module initialization failed: can not store value in thread local storage")); wxLogSysError(_("Thread module initialization failed: can not store value in thread local storage"));
return FALSE; return false;
} }
gs_critsectWaitingForGui = new wxCriticalSection(); gs_critsectWaitingForGui = new wxCriticalSection();
@@ -1157,10 +1196,12 @@ bool wxThreadModule::OnInit()
gs_critsectGui = new wxCriticalSection(); gs_critsectGui = new wxCriticalSection();
gs_critsectGui->Enter(); gs_critsectGui->Enter();
gs_critsectThreadDelete = new wxCriticalSection;
// no error return for GetCurrentThreadId() // no error return for GetCurrentThreadId()
gs_idMainThread = ::GetCurrentThreadId(); gs_idMainThread = ::GetCurrentThreadId();
return TRUE; return true;
} }
void wxThreadModule::OnExit() void wxThreadModule::OnExit()
@@ -1170,6 +1211,9 @@ void wxThreadModule::OnExit()
wxLogLastError(wxT("TlsFree failed.")); wxLogLastError(wxT("TlsFree failed."));
} }
delete gs_critsectThreadDelete;
gs_critsectThreadDelete = NULL;
if ( gs_critsectGui ) if ( gs_critsectGui )
{ {
gs_critsectGui->Leave(); gs_critsectGui->Leave();
@@ -1214,7 +1258,7 @@ void WXDLLIMPEXP_BASE wxMutexGuiLeave()
if ( wxThread::IsMain() ) if ( wxThread::IsMain() )
{ {
gs_bGuiOwnedByMainThread = FALSE; gs_bGuiOwnedByMainThread = false;
} }
else else
{ {
@@ -1245,7 +1289,7 @@ void WXDLLIMPEXP_BASE wxMutexGuiLeaveOrEnter()
{ {
gs_critsectGui->Enter(); gs_critsectGui->Enter();
gs_bGuiOwnedByMainThread = TRUE; gs_bGuiOwnedByMainThread = true;
} }
//else: already have it, nothing to do //else: already have it, nothing to do
} }