more threds fixes, more threads tests - seems to work ok for non GUI case
git-svn-id: https://svn.wxwidgets.org/svn/wx/wxWidgets/trunk@4798 c3d73ce0-8a6f-49c7-b76d-6d57e0e08775
This commit is contained in:
@@ -32,8 +32,8 @@
|
|||||||
//#define TEST_ARRAYS
|
//#define TEST_ARRAYS
|
||||||
//#define TEST_LOG
|
//#define TEST_LOG
|
||||||
//#define TEST_STRINGS
|
//#define TEST_STRINGS
|
||||||
//#define TEST_THREADS
|
#define TEST_THREADS
|
||||||
#define TEST_TIME
|
//#define TEST_TIME
|
||||||
//#define TEST_LONGLONG
|
//#define TEST_LONGLONG
|
||||||
|
|
||||||
// ============================================================================
|
// ============================================================================
|
||||||
@@ -392,6 +392,8 @@ void TestThreadSuspend()
|
|||||||
thread->Resume();
|
thread->Resume();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
puts("Waiting until it terminates now");
|
||||||
|
|
||||||
// wait until the thread terminates
|
// wait until the thread terminates
|
||||||
gs_cond.Wait();
|
gs_cond.Wait();
|
||||||
|
|
||||||
@@ -407,6 +409,12 @@ void TestThreadDelete()
|
|||||||
|
|
||||||
puts("\n*** Testing thread delete function ***");
|
puts("\n*** Testing thread delete function ***");
|
||||||
|
|
||||||
|
MyDetachedThread *thread0 = new MyDetachedThread(30, 'W');
|
||||||
|
|
||||||
|
thread0->Delete();
|
||||||
|
|
||||||
|
puts("\nDeleted a thread which didn't start to run yet.");
|
||||||
|
|
||||||
MyDetachedThread *thread1 = new MyDetachedThread(30, 'Y');
|
MyDetachedThread *thread1 = new MyDetachedThread(30, 'Y');
|
||||||
|
|
||||||
thread1->Run();
|
thread1->Run();
|
||||||
@@ -429,19 +437,19 @@ void TestThreadDelete()
|
|||||||
|
|
||||||
puts("\nDeleted a sleeping thread.");
|
puts("\nDeleted a sleeping thread.");
|
||||||
|
|
||||||
MyJoinableThread *thread3 = new MyJoinableThread(20);
|
MyJoinableThread thread3(20);
|
||||||
thread3->Run();
|
thread3.Run();
|
||||||
|
|
||||||
thread3->Delete();
|
thread3.Delete();
|
||||||
|
|
||||||
puts("\nDeleted a joinable thread.");
|
puts("\nDeleted a joinable thread.");
|
||||||
|
|
||||||
MyJoinableThread *thread4 = new MyJoinableThread(2);
|
MyJoinableThread thread4(2);
|
||||||
thread4->Run();
|
thread4.Run();
|
||||||
|
|
||||||
wxThread::Sleep(300);
|
wxThread::Sleep(300);
|
||||||
|
|
||||||
thread4->Delete();
|
thread4.Delete();
|
||||||
|
|
||||||
puts("\nDeleted a joinable thread which already terminated.");
|
puts("\nDeleted a joinable thread which already terminated.");
|
||||||
|
|
||||||
@@ -598,11 +606,11 @@ int main(int argc, char **argv)
|
|||||||
if ( argc > 1 && argv[1][0] == 't' )
|
if ( argc > 1 && argv[1][0] == 't' )
|
||||||
wxLog::AddTraceMask("thread");
|
wxLog::AddTraceMask("thread");
|
||||||
|
|
||||||
if ( 0 )
|
if ( 1 )
|
||||||
TestDetachedThreads();
|
TestDetachedThreads();
|
||||||
if ( 0 )
|
if ( 1 )
|
||||||
TestJoinableThreads();
|
TestJoinableThreads();
|
||||||
if ( 0 )
|
if ( 1 )
|
||||||
TestThreadSuspend();
|
TestThreadSuspend();
|
||||||
if ( 1 )
|
if ( 1 )
|
||||||
TestThreadDelete();
|
TestThreadDelete();
|
||||||
|
@@ -224,9 +224,12 @@ wxMutexError wxMutex::Unlock()
|
|||||||
// wxCondition (Posix implementation)
|
// wxCondition (Posix implementation)
|
||||||
//--------------------------------------------------------------------
|
//--------------------------------------------------------------------
|
||||||
|
|
||||||
// notice that we must use a mutex with POSIX condition variables to ensure
|
// The native POSIX condition variables are dumb: if the condition is signaled
|
||||||
// that the worker thread doesn't signal condition before the waiting thread
|
// before another thread starts to wait on it, the signal is lost and so this
|
||||||
// starts to wait for it
|
// other thread will be never woken up. It's much more convenient to us to
|
||||||
|
// remember that the condition was signaled and to return from Wait()
|
||||||
|
// immediately in this case (this is more like Win32 automatic event objects)
|
||||||
|
|
||||||
class wxConditionInternal
|
class wxConditionInternal
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
@@ -239,20 +242,34 @@ public:
|
|||||||
void Signal();
|
void Signal();
|
||||||
void Broadcast();
|
void Broadcast();
|
||||||
|
|
||||||
|
void WaitDone();
|
||||||
|
bool ShouldWait();
|
||||||
|
bool HasWaiters();
|
||||||
|
|
||||||
private:
|
private:
|
||||||
pthread_mutex_t m_mutex;
|
bool m_wasSignaled; // TRUE if condition was signaled while
|
||||||
pthread_cond_t m_condition;
|
// nobody waited for it
|
||||||
|
size_t m_nWaiters; // TRUE if someone already waits for us
|
||||||
|
|
||||||
|
pthread_mutex_t m_mutexProtect; // protects access to vars above
|
||||||
|
|
||||||
|
pthread_mutex_t m_mutex; // the mutex used with the condition
|
||||||
|
pthread_cond_t m_condition; // the condition itself
|
||||||
};
|
};
|
||||||
|
|
||||||
wxConditionInternal::wxConditionInternal()
|
wxConditionInternal::wxConditionInternal()
|
||||||
{
|
{
|
||||||
|
m_wasSignaled = FALSE;
|
||||||
|
m_nWaiters = 0;
|
||||||
|
|
||||||
if ( pthread_cond_init(&m_condition, (pthread_condattr_t *)NULL) != 0 )
|
if ( pthread_cond_init(&m_condition, (pthread_condattr_t *)NULL) != 0 )
|
||||||
{
|
{
|
||||||
// this is supposed to never happen
|
// this is supposed to never happen
|
||||||
wxFAIL_MSG( _T("pthread_cond_init() failed") );
|
wxFAIL_MSG( _T("pthread_cond_init() failed") );
|
||||||
}
|
}
|
||||||
|
|
||||||
if ( pthread_mutex_init(&m_mutex, (pthread_mutexattr_t*)NULL) != 0 )
|
if ( pthread_mutex_init(&m_mutex, (pthread_mutexattr_t *)NULL) != 0 ||
|
||||||
|
pthread_mutex_init(&m_mutexProtect, NULL) != 0 )
|
||||||
{
|
{
|
||||||
// neither this
|
// neither this
|
||||||
wxFAIL_MSG( _T("wxCondition: pthread_mutex_init() failed") );
|
wxFAIL_MSG( _T("wxCondition: pthread_mutex_init() failed") );
|
||||||
@@ -279,60 +296,131 @@ wxConditionInternal::~wxConditionInternal()
|
|||||||
wxLogDebug(_T("wxCondition: failed to unlock the mutex"));
|
wxLogDebug(_T("wxCondition: failed to unlock the mutex"));
|
||||||
}
|
}
|
||||||
|
|
||||||
if ( pthread_mutex_destroy( &m_mutex ) != 0 )
|
if ( pthread_mutex_destroy( &m_mutex ) != 0 ||
|
||||||
|
pthread_mutex_destroy( &m_mutexProtect ) != 0 )
|
||||||
{
|
{
|
||||||
wxLogDebug(_T("Failed to destroy mutex (it is probably locked)"));
|
wxLogDebug(_T("Failed to destroy mutex (it is probably locked)"));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void wxConditionInternal::WaitDone()
|
||||||
|
{
|
||||||
|
MutexLock lock(m_mutexProtect);
|
||||||
|
|
||||||
|
m_wasSignaled = FALSE;
|
||||||
|
m_nWaiters--;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool wxConditionInternal::ShouldWait()
|
||||||
|
{
|
||||||
|
MutexLock lock(m_mutexProtect);
|
||||||
|
|
||||||
|
if ( m_wasSignaled )
|
||||||
|
{
|
||||||
|
// the condition was signaled before we started to wait, reset the
|
||||||
|
// flag and return
|
||||||
|
m_wasSignaled = FALSE;
|
||||||
|
|
||||||
|
return FALSE;
|
||||||
|
}
|
||||||
|
|
||||||
|
// we start to wait for it
|
||||||
|
m_nWaiters++;
|
||||||
|
|
||||||
|
return TRUE;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool wxConditionInternal::HasWaiters()
|
||||||
|
{
|
||||||
|
MutexLock lock(m_mutexProtect);
|
||||||
|
|
||||||
|
if ( m_nWaiters )
|
||||||
|
{
|
||||||
|
// someone waits for us, signal the condition normally
|
||||||
|
return TRUE;
|
||||||
|
}
|
||||||
|
|
||||||
|
// nobody waits for us and may be never will - so just remember that the
|
||||||
|
// condition was signaled and don't do anything else
|
||||||
|
m_wasSignaled = TRUE;
|
||||||
|
|
||||||
|
return FALSE;
|
||||||
|
}
|
||||||
|
|
||||||
void wxConditionInternal::Wait()
|
void wxConditionInternal::Wait()
|
||||||
{
|
{
|
||||||
if ( pthread_cond_wait( &m_condition, &m_mutex ) != 0 )
|
if ( ShouldWait() )
|
||||||
{
|
{
|
||||||
// not supposed to ever happen
|
if ( pthread_cond_wait( &m_condition, &m_mutex ) != 0 )
|
||||||
wxFAIL_MSG( _T("pthread_cond_wait() failed") );
|
{
|
||||||
|
// not supposed to ever happen
|
||||||
|
wxFAIL_MSG( _T("pthread_cond_wait() failed") );
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
WaitDone();
|
||||||
}
|
}
|
||||||
|
|
||||||
bool wxConditionInternal::WaitWithTimeout(const timespec* ts)
|
bool wxConditionInternal::WaitWithTimeout(const timespec* ts)
|
||||||
{
|
{
|
||||||
switch ( pthread_cond_timedwait( &m_condition, &m_mutex, ts ) )
|
bool ok;
|
||||||
|
|
||||||
|
if ( ShouldWait() )
|
||||||
{
|
{
|
||||||
case 0:
|
switch ( pthread_cond_timedwait( &m_condition, &m_mutex, ts ) )
|
||||||
// condition signaled
|
{
|
||||||
return TRUE;
|
case 0:
|
||||||
|
// condition signaled
|
||||||
|
ok = TRUE;
|
||||||
|
break;
|
||||||
|
|
||||||
default:
|
default:
|
||||||
wxLogDebug(_T("pthread_cond_timedwait() failed"));
|
wxLogDebug(_T("pthread_cond_timedwait() failed"));
|
||||||
|
|
||||||
// fall through
|
// fall through
|
||||||
|
|
||||||
case ETIMEDOUT:
|
case ETIMEDOUT:
|
||||||
case EINTR:
|
case EINTR:
|
||||||
// wait interrupted or timeout elapsed
|
// wait interrupted or timeout elapsed
|
||||||
return FALSE;
|
ok = FALSE;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
// the condition had already been signaled before
|
||||||
|
ok = TRUE;
|
||||||
|
}
|
||||||
|
|
||||||
|
WaitDone();
|
||||||
|
|
||||||
|
return ok;
|
||||||
}
|
}
|
||||||
|
|
||||||
void wxConditionInternal::Signal()
|
void wxConditionInternal::Signal()
|
||||||
{
|
{
|
||||||
MutexLock lock(m_mutex);
|
if ( HasWaiters() )
|
||||||
|
|
||||||
if ( pthread_cond_signal( &m_condition ) != 0 )
|
|
||||||
{
|
{
|
||||||
// shouldn't ever happen
|
MutexLock lock(m_mutex);
|
||||||
wxFAIL_MSG(_T("pthread_cond_signal() failed"));
|
|
||||||
|
if ( pthread_cond_signal( &m_condition ) != 0 )
|
||||||
|
{
|
||||||
|
// shouldn't ever happen
|
||||||
|
wxFAIL_MSG(_T("pthread_cond_signal() failed"));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void wxConditionInternal::Broadcast()
|
void wxConditionInternal::Broadcast()
|
||||||
{
|
{
|
||||||
MutexLock lock(m_mutex);
|
if ( HasWaiters() )
|
||||||
|
|
||||||
if ( pthread_cond_broadcast( &m_condition ) != 0 )
|
|
||||||
{
|
{
|
||||||
// shouldn't ever happen
|
MutexLock lock(m_mutex);
|
||||||
wxFAIL_MSG(_T("pthread_cond_broadcast() failed"));
|
|
||||||
|
if ( pthread_cond_broadcast( &m_condition ) != 0 )
|
||||||
|
{
|
||||||
|
// shouldn't ever happen
|
||||||
|
wxFAIL_MSG(_T("pthread_cond_broadcast() failed"));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -396,6 +484,8 @@ public:
|
|||||||
void Wait();
|
void Wait();
|
||||||
// wake up threads waiting for our termination
|
// wake up threads waiting for our termination
|
||||||
void SignalExit();
|
void SignalExit();
|
||||||
|
// wake up threads waiting for our start
|
||||||
|
void SignalRun() { m_condRun.Signal(); }
|
||||||
// go to sleep until Resume() is called
|
// go to sleep until Resume() is called
|
||||||
void Pause();
|
void Pause();
|
||||||
// resume the thread
|
// resume the thread
|
||||||
@@ -418,8 +508,16 @@ public:
|
|||||||
void SetExitCode(wxThread::ExitCode exitcode) { m_exitcode = exitcode; }
|
void SetExitCode(wxThread::ExitCode exitcode) { m_exitcode = exitcode; }
|
||||||
wxThread::ExitCode GetExitCode() const { return m_exitcode; }
|
wxThread::ExitCode GetExitCode() const { return m_exitcode; }
|
||||||
|
|
||||||
|
// the pause flag
|
||||||
|
void SetReallyPaused(bool paused) { m_isPaused = paused; }
|
||||||
|
bool IsReallyPaused() const { return m_isPaused; }
|
||||||
|
|
||||||
// tell the thread that it is a detached one
|
// tell the thread that it is a detached one
|
||||||
void Detach() { m_shouldBeJoined = m_shouldBroadcast = FALSE; }
|
void Detach()
|
||||||
|
{
|
||||||
|
m_shouldBeJoined = m_shouldBroadcast = FALSE;
|
||||||
|
m_isDetached = TRUE;
|
||||||
|
}
|
||||||
// but even detached threads need to notifyus about their termination
|
// but even detached threads need to notifyus about their termination
|
||||||
// sometimes - tell the thread that it should do it
|
// sometimes - tell the thread that it should do it
|
||||||
void Notify() { m_shouldBroadcast = TRUE; }
|
void Notify() { m_shouldBroadcast = TRUE; }
|
||||||
@@ -432,6 +530,9 @@ private:
|
|||||||
// this flag is set when the thread should terminate
|
// this flag is set when the thread should terminate
|
||||||
bool m_cancelled;
|
bool m_cancelled;
|
||||||
|
|
||||||
|
// this flag is set when the thread is blocking on m_condSuspend
|
||||||
|
bool m_isPaused;
|
||||||
|
|
||||||
// the thread exit code - only used for joinable (!detached) threads and
|
// the thread exit code - only used for joinable (!detached) threads and
|
||||||
// is only valid after the thread termination
|
// is only valid after the thread termination
|
||||||
wxThread::ExitCode m_exitcode;
|
wxThread::ExitCode m_exitcode;
|
||||||
@@ -441,6 +542,7 @@ private:
|
|||||||
wxCriticalSection m_csJoinFlag;
|
wxCriticalSection m_csJoinFlag;
|
||||||
bool m_shouldBeJoined;
|
bool m_shouldBeJoined;
|
||||||
bool m_shouldBroadcast;
|
bool m_shouldBroadcast;
|
||||||
|
bool m_isDetached;
|
||||||
|
|
||||||
// VZ: it's possible that we might do with less than three different
|
// VZ: it's possible that we might do with less than three different
|
||||||
// condition objects - for example, m_condRun and m_condEnd a priori
|
// condition objects - for example, m_condRun and m_condEnd a priori
|
||||||
@@ -479,6 +581,10 @@ void *wxThreadInternal::PthreadStart(void *ptr)
|
|||||||
return (void *)-1;
|
return (void *)-1;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// have to declare this before pthread_cleanup_push() which defines a
|
||||||
|
// block!
|
||||||
|
bool dontRunAtAll;
|
||||||
|
|
||||||
#if HAVE_THREAD_CLEANUP_FUNCTIONS
|
#if HAVE_THREAD_CLEANUP_FUNCTIONS
|
||||||
// install the cleanup handler which will be called if the thread is
|
// install the cleanup handler which will be called if the thread is
|
||||||
// cancelled
|
// cancelled
|
||||||
@@ -488,22 +594,34 @@ void *wxThreadInternal::PthreadStart(void *ptr)
|
|||||||
// wait for the condition to be signaled from Run()
|
// wait for the condition to be signaled from Run()
|
||||||
pthread->m_condRun.Wait();
|
pthread->m_condRun.Wait();
|
||||||
|
|
||||||
// call the main entry
|
// test whether we should run the run at all - may be it was deleted
|
||||||
pthread->m_exitcode = thread->Entry();
|
// before it started to Run()?
|
||||||
|
|
||||||
wxLogTrace(TRACE_THREADS, _T("Thread %ld left its Entry()."),
|
|
||||||
pthread->GetId());
|
|
||||||
|
|
||||||
{
|
{
|
||||||
wxCriticalSectionLocker lock(thread->m_critsect);
|
wxCriticalSectionLocker lock(thread->m_critsect);
|
||||||
|
|
||||||
wxLogTrace(TRACE_THREADS, _T("Thread %ld changes state to EXITED."),
|
dontRunAtAll = pthread->GetState() == STATE_NEW &&
|
||||||
|
pthread->WasCancelled();
|
||||||
|
}
|
||||||
|
|
||||||
|
if ( !dontRunAtAll )
|
||||||
|
{
|
||||||
|
// call the main entry
|
||||||
|
pthread->m_exitcode = thread->Entry();
|
||||||
|
|
||||||
|
wxLogTrace(TRACE_THREADS, _T("Thread %ld left its Entry()."),
|
||||||
pthread->GetId());
|
pthread->GetId());
|
||||||
|
|
||||||
// change the state of the thread to "exited" so that PthreadCleanup
|
{
|
||||||
// handler won't do anything from now (if it's called before we do
|
wxCriticalSectionLocker lock(thread->m_critsect);
|
||||||
// pthread_cleanup_pop below)
|
|
||||||
pthread->SetState(STATE_EXITED);
|
wxLogTrace(TRACE_THREADS, _T("Thread %ld changes state to EXITED."),
|
||||||
|
pthread->GetId());
|
||||||
|
|
||||||
|
// change the state of the thread to "exited" so that
|
||||||
|
// PthreadCleanup handler won't do anything from now (if it's
|
||||||
|
// called before we do pthread_cleanup_pop below)
|
||||||
|
pthread->SetState(STATE_EXITED);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// NB: at least under Linux, pthread_cleanup_push/pop are macros and pop
|
// NB: at least under Linux, pthread_cleanup_push/pop are macros and pop
|
||||||
@@ -514,12 +632,21 @@ void *wxThreadInternal::PthreadStart(void *ptr)
|
|||||||
pthread_cleanup_pop(FALSE);
|
pthread_cleanup_pop(FALSE);
|
||||||
#endif // HAVE_THREAD_CLEANUP_FUNCTIONS
|
#endif // HAVE_THREAD_CLEANUP_FUNCTIONS
|
||||||
|
|
||||||
// terminate the thread
|
if ( dontRunAtAll )
|
||||||
thread->Exit(pthread->m_exitcode);
|
{
|
||||||
|
delete thread;
|
||||||
|
|
||||||
wxFAIL_MSG(wxT("wxThread::Exit() can't return."));
|
return EXITCODE_CANCELLED;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
// terminate the thread
|
||||||
|
thread->Exit(pthread->m_exitcode);
|
||||||
|
|
||||||
return NULL;
|
wxFAIL_MSG(wxT("wxThread::Exit() can't return."));
|
||||||
|
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#if HAVE_THREAD_CLEANUP_FUNCTIONS
|
#if HAVE_THREAD_CLEANUP_FUNCTIONS
|
||||||
@@ -556,9 +683,13 @@ wxThreadInternal::wxThreadInternal()
|
|||||||
m_threadId = 0;
|
m_threadId = 0;
|
||||||
m_exitcode = 0;
|
m_exitcode = 0;
|
||||||
|
|
||||||
|
// set to TRUE only when the thread starts waiting on m_condSuspend
|
||||||
|
m_isPaused = FALSE;
|
||||||
|
|
||||||
// defaults for joinable threads
|
// defaults for joinable threads
|
||||||
m_shouldBeJoined = TRUE;
|
m_shouldBeJoined = TRUE;
|
||||||
m_shouldBroadcast = TRUE;
|
m_shouldBroadcast = TRUE;
|
||||||
|
m_isDetached = FALSE;
|
||||||
}
|
}
|
||||||
|
|
||||||
wxThreadInternal::~wxThreadInternal()
|
wxThreadInternal::~wxThreadInternal()
|
||||||
@@ -570,7 +701,7 @@ wxThreadError wxThreadInternal::Run()
|
|||||||
wxCHECK_MSG( GetState() == STATE_NEW, wxTHREAD_RUNNING,
|
wxCHECK_MSG( GetState() == STATE_NEW, wxTHREAD_RUNNING,
|
||||||
wxT("thread may only be started once after Create()") );
|
wxT("thread may only be started once after Create()") );
|
||||||
|
|
||||||
m_condRun.Signal();
|
SignalRun();
|
||||||
|
|
||||||
SetState(STATE_RUNNING);
|
SetState(STATE_RUNNING);
|
||||||
|
|
||||||
@@ -584,29 +715,39 @@ void wxThreadInternal::Wait()
|
|||||||
if ( wxThread::IsMain() )
|
if ( wxThread::IsMain() )
|
||||||
wxMutexGuiLeave();
|
wxMutexGuiLeave();
|
||||||
|
|
||||||
|
bool isDetached = m_isDetached;
|
||||||
|
long id = GetId();
|
||||||
wxLogTrace(TRACE_THREADS, _T("Starting to wait for thread %ld to exit."),
|
wxLogTrace(TRACE_THREADS, _T("Starting to wait for thread %ld to exit."),
|
||||||
GetId());
|
id);
|
||||||
|
|
||||||
// wait until the thread terminates (we're blocking in _another_ thread,
|
// wait until the thread terminates (we're blocking in _another_ thread,
|
||||||
// of course)
|
// of course)
|
||||||
m_condEnd.Wait();
|
m_condEnd.Wait();
|
||||||
|
|
||||||
// to avoid memory leaks we should call pthread_join(), but it must only
|
wxLogTrace(TRACE_THREADS, _T("Finished waiting for thread %ld."), id);
|
||||||
// be done once
|
|
||||||
wxCriticalSectionLocker lock(m_csJoinFlag);
|
|
||||||
|
|
||||||
if ( m_shouldBeJoined )
|
// we can't use any member variables any more if the thread is detached
|
||||||
|
// because it could be already deleted
|
||||||
|
if ( !isDetached )
|
||||||
{
|
{
|
||||||
// FIXME shouldn't we set cancellation type to DISABLED here? If we're
|
// to avoid memory leaks we should call pthread_join(), but it must
|
||||||
// cancelled inside pthread_join(), things will almost certainly
|
// only be done once
|
||||||
// break - but if we disable the cancellation, we might deadlock
|
wxCriticalSectionLocker lock(m_csJoinFlag);
|
||||||
if ( pthread_join(GetId(), &m_exitcode) != 0 )
|
|
||||||
{
|
|
||||||
wxLogError(_T("Failed to join a thread, potential memory leak "
|
|
||||||
"detected - please restart the program"));
|
|
||||||
}
|
|
||||||
|
|
||||||
m_shouldBeJoined = FALSE;
|
if ( m_shouldBeJoined )
|
||||||
|
{
|
||||||
|
// FIXME shouldn't we set cancellation type to DISABLED here? If
|
||||||
|
// we're cancelled inside pthread_join(), things will almost
|
||||||
|
// certainly break - but if we disable the cancellation, we
|
||||||
|
// might deadlock
|
||||||
|
if ( pthread_join(id, &m_exitcode) != 0 )
|
||||||
|
{
|
||||||
|
wxLogError(_("Failed to join a thread, potential memory leak "
|
||||||
|
"detected - please restart the program"));
|
||||||
|
}
|
||||||
|
|
||||||
|
m_shouldBeJoined = FALSE;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// reacquire GUI mutex
|
// reacquire GUI mutex
|
||||||
@@ -648,10 +789,23 @@ void wxThreadInternal::Resume()
|
|||||||
wxCHECK_RET( m_state == STATE_PAUSED,
|
wxCHECK_RET( m_state == STATE_PAUSED,
|
||||||
wxT("can't resume thread which is not suspended.") );
|
wxT("can't resume thread which is not suspended.") );
|
||||||
|
|
||||||
wxLogTrace(TRACE_THREADS, _T("Waking up thread %ld"), GetId());
|
// the thread might be not actually paused yet - if there were no call to
|
||||||
|
// TestDestroy() since the last call to Pause() for example
|
||||||
|
if ( IsReallyPaused() )
|
||||||
|
{
|
||||||
|
wxLogTrace(TRACE_THREADS, _T("Waking up thread %ld"), GetId());
|
||||||
|
|
||||||
// wake up Pause()
|
// wake up Pause()
|
||||||
m_condSuspend.Signal();
|
m_condSuspend.Signal();
|
||||||
|
|
||||||
|
// reset the flag
|
||||||
|
SetReallyPaused(FALSE);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
wxLogTrace(TRACE_THREADS, _T("Thread %ld is not yet really paused"),
|
||||||
|
GetId());
|
||||||
|
}
|
||||||
|
|
||||||
SetState(STATE_RUNNING);
|
SetState(STATE_RUNNING);
|
||||||
}
|
}
|
||||||
@@ -866,6 +1020,9 @@ unsigned long wxThread::GetId() const
|
|||||||
|
|
||||||
wxThreadError wxThread::Pause()
|
wxThreadError wxThread::Pause()
|
||||||
{
|
{
|
||||||
|
wxCHECK_MSG( This() != this, wxTHREAD_MISC_ERROR,
|
||||||
|
_T("a thread can't pause itself") );
|
||||||
|
|
||||||
wxCriticalSectionLocker lock(m_critsect);
|
wxCriticalSectionLocker lock(m_critsect);
|
||||||
|
|
||||||
if ( m_internal->GetState() != STATE_RUNNING )
|
if ( m_internal->GetState() != STATE_RUNNING )
|
||||||
@@ -875,6 +1032,9 @@ wxThreadError wxThread::Pause()
|
|||||||
return wxTHREAD_NOT_RUNNING;
|
return wxTHREAD_NOT_RUNNING;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
wxLogTrace(TRACE_THREADS, _T("Asking thread %ld to pause."),
|
||||||
|
GetId());
|
||||||
|
|
||||||
// just set a flag, the thread will be really paused only during the next
|
// just set a flag, the thread will be really paused only during the next
|
||||||
// call to TestDestroy()
|
// call to TestDestroy()
|
||||||
m_internal->SetState(STATE_PAUSED);
|
m_internal->SetState(STATE_PAUSED);
|
||||||
@@ -884,20 +1044,17 @@ wxThreadError wxThread::Pause()
|
|||||||
|
|
||||||
wxThreadError wxThread::Resume()
|
wxThreadError wxThread::Resume()
|
||||||
{
|
{
|
||||||
m_critsect.Enter();
|
wxCHECK_MSG( This() != this, wxTHREAD_MISC_ERROR,
|
||||||
|
_T("a thread can't resume itself") );
|
||||||
|
|
||||||
|
wxCriticalSectionLocker lock(m_critsect);
|
||||||
|
|
||||||
wxThreadState state = m_internal->GetState();
|
wxThreadState state = m_internal->GetState();
|
||||||
|
|
||||||
// the thread might be not actually paused yet - if there were no call to
|
|
||||||
// TestDestroy() since the last call to Pause(), so avoid that
|
|
||||||
// TestDestroy() deadlocks trying to enter m_critsect by leaving it before
|
|
||||||
// calling Resume()
|
|
||||||
m_critsect.Leave();
|
|
||||||
|
|
||||||
switch ( state )
|
switch ( state )
|
||||||
{
|
{
|
||||||
case STATE_PAUSED:
|
case STATE_PAUSED:
|
||||||
wxLogTrace(TRACE_THREADS, _T("Thread %ld is suspended, resuming."),
|
wxLogTrace(TRACE_THREADS, _T("Thread %ld suspended, resuming."),
|
||||||
GetId());
|
GetId());
|
||||||
|
|
||||||
m_internal->Resume();
|
m_internal->Resume();
|
||||||
@@ -935,6 +1092,9 @@ wxThread::ExitCode wxThread::Wait()
|
|||||||
|
|
||||||
wxThreadError wxThread::Delete(ExitCode *rc)
|
wxThreadError wxThread::Delete(ExitCode *rc)
|
||||||
{
|
{
|
||||||
|
wxCHECK_MSG( This() != this, wxTHREAD_MISC_ERROR,
|
||||||
|
_T("a thread can't delete itself") );
|
||||||
|
|
||||||
m_critsect.Enter();
|
m_critsect.Enter();
|
||||||
wxThreadState state = m_internal->GetState();
|
wxThreadState state = m_internal->GetState();
|
||||||
|
|
||||||
@@ -954,6 +1114,12 @@ wxThreadError wxThread::Delete(ExitCode *rc)
|
|||||||
switch ( state )
|
switch ( state )
|
||||||
{
|
{
|
||||||
case STATE_NEW:
|
case STATE_NEW:
|
||||||
|
// we need to wake up the thread so that PthreadStart() will
|
||||||
|
// terminate - right now it's blocking on m_condRun
|
||||||
|
m_internal->SignalRun();
|
||||||
|
|
||||||
|
// fall through
|
||||||
|
|
||||||
case STATE_EXITED:
|
case STATE_EXITED:
|
||||||
// nothing to do
|
// nothing to do
|
||||||
break;
|
break;
|
||||||
@@ -1032,6 +1198,10 @@ wxThreadError wxThread::Kill()
|
|||||||
|
|
||||||
void wxThread::Exit(ExitCode status)
|
void wxThread::Exit(ExitCode status)
|
||||||
{
|
{
|
||||||
|
wxASSERT_MSG( This() == this,
|
||||||
|
_T("wxThread::Exit() can only be called in the "
|
||||||
|
"context of the same thread") );
|
||||||
|
|
||||||
// from the moment we call OnExit(), the main program may terminate at any
|
// from the moment we call OnExit(), the main program may terminate at any
|
||||||
// moment, so mark this thread as being already in process of being
|
// moment, so mark this thread as being already in process of being
|
||||||
// deleted or wxThreadModule::OnExit() will try to delete it again
|
// deleted or wxThreadModule::OnExit() will try to delete it again
|
||||||
@@ -1076,10 +1246,16 @@ void wxThread::Exit(ExitCode status)
|
|||||||
// also test whether we were paused
|
// also test whether we were paused
|
||||||
bool wxThread::TestDestroy()
|
bool wxThread::TestDestroy()
|
||||||
{
|
{
|
||||||
|
wxASSERT_MSG( This() == this,
|
||||||
|
_T("wxThread::TestDestroy() can only be called in the "
|
||||||
|
"context of the same thread") );
|
||||||
|
|
||||||
m_critsect.Enter();
|
m_critsect.Enter();
|
||||||
|
|
||||||
if ( m_internal->GetState() == STATE_PAUSED )
|
if ( m_internal->GetState() == STATE_PAUSED )
|
||||||
{
|
{
|
||||||
|
m_internal->SetReallyPaused(TRUE);
|
||||||
|
|
||||||
// leave the crit section or the other threads will stop too if they
|
// leave the crit section or the other threads will stop too if they
|
||||||
// try to call any of (seemingly harmless) IsXXX() functions while we
|
// try to call any of (seemingly harmless) IsXXX() functions while we
|
||||||
// sleep
|
// sleep
|
||||||
@@ -1105,8 +1281,8 @@ wxThread::~wxThread()
|
|||||||
if ( m_internal->GetState() != STATE_EXITED &&
|
if ( m_internal->GetState() != STATE_EXITED &&
|
||||||
m_internal->GetState() != STATE_NEW )
|
m_internal->GetState() != STATE_NEW )
|
||||||
{
|
{
|
||||||
wxLogDebug(_T("The thread is being destroyed although it is still "
|
wxLogDebug(_T("The thread %ld is being destroyed although it is still "
|
||||||
"running! The application may crash."));
|
"running! The application may crash."), GetId());
|
||||||
}
|
}
|
||||||
|
|
||||||
m_critsect.Leave();
|
m_critsect.Leave();
|
||||||
@@ -1116,6 +1292,17 @@ wxThread::~wxThread()
|
|||||||
|
|
||||||
// remove this thread from the global array
|
// remove this thread from the global array
|
||||||
gs_allThreads.Remove(this);
|
gs_allThreads.Remove(this);
|
||||||
|
|
||||||
|
// detached thread will decrement this counter in DeleteThread(), but it
|
||||||
|
// is not called for the joinable threads, so do it here
|
||||||
|
if ( !m_isDetached )
|
||||||
|
{
|
||||||
|
MutexLock lock(gs_mutexDeleteThread);
|
||||||
|
gs_nThreadsBeingDeleted--;
|
||||||
|
|
||||||
|
wxLogTrace(TRACE_THREADS, _T("%u scheduled for deletion threads left."),
|
||||||
|
gs_nThreadsBeingDeleted - 1);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// -----------------------------------------------------------------------------
|
// -----------------------------------------------------------------------------
|
||||||
@@ -1212,7 +1399,10 @@ void wxThreadModule::OnExit()
|
|||||||
// terminate any threads left
|
// terminate any threads left
|
||||||
size_t count = gs_allThreads.GetCount();
|
size_t count = gs_allThreads.GetCount();
|
||||||
if ( count != 0u )
|
if ( count != 0u )
|
||||||
wxLogDebug(wxT("Some threads were not terminated by the application."));
|
{
|
||||||
|
wxLogDebug(wxT("%u threads were not terminated by the application."),
|
||||||
|
count);
|
||||||
|
}
|
||||||
|
|
||||||
for ( size_t n = 0u; n < count; n++ )
|
for ( size_t n = 0u; n < count; n++ )
|
||||||
{
|
{
|
||||||
@@ -1268,6 +1458,9 @@ static void DeleteThread(wxThread *This)
|
|||||||
"one?") );
|
"one?") );
|
||||||
}
|
}
|
||||||
|
|
||||||
|
wxLogTrace(TRACE_THREADS, _T("%u scheduled for deletion threads left."),
|
||||||
|
gs_nThreadsBeingDeleted - 1);
|
||||||
|
|
||||||
if ( !--gs_nThreadsBeingDeleted )
|
if ( !--gs_nThreadsBeingDeleted )
|
||||||
{
|
{
|
||||||
// no more threads left, signal it
|
// no more threads left, signal it
|
||||||
|
Reference in New Issue
Block a user