{
public:
// lock the mutex in the ctor
- wxMutexLocker(wxMutex *mutex)
- { m_isOk = mutex && ((m_mutex = mutex)->Lock() == wxMUTEX_NO_ERROR); }
+ wxMutexLocker(wxMutex& mutex) : m_mutex(mutex)
+ { m_isOk = m_mutex.Lock() == wxMUTEX_NO_ERROR; }
// returns TRUE if mutex was successfully locked in ctor
- bool IsOk() const { return m_isOk; }
+ bool IsOk() const
+ { return m_isOk; }
// unlock the mutex in dtor
- ~wxMutexLocker() { if ( IsOk() ) m_mutex->Unlock(); }
+ ~wxMutexLocker()
+ { if ( IsOk() ) m_mutex.Unlock(); }
private:
// no assignment operator nor copy ctor
wxMutexLocker& operator=(const wxMutexLocker&);
bool m_isOk;
- wxMutex *m_mutex;
+ wxMutex& m_mutex;
};
// ----------------------------------------------------------------------------
bool OnInit();
};
+class MyThread;
WX_DEFINE_ARRAY(wxThread *, wxArrayThread);
// Define a new frame type
void OnClear(wxCommandEvent& event);
void OnStartThread(wxCommandEvent& event);
+ void OnStartThreads(wxCommandEvent& event);
void OnStopThread(wxCommandEvent& event);
void OnPauseThread(wxCommandEvent& event);
void OnResumeThread(wxCommandEvent& event);
void OnIdle(wxIdleEvent &event);
bool OnClose() { return TRUE; }
- // called by dying thread
+ // called by dying thread _in_that_thread_context_
void OnThreadExit(wxThread *thread);
-public:
- wxArrayThread m_threads;
-
private:
- // crit section protects access to the array below
+ // helper function - creates a new thread (but doesn't run it)
+ MyThread *CreateThread();
+
+ // crit section protects access to all of the arrays below
wxCriticalSection m_critsect;
+ // all the threads currently alive - as soon as the thread terminates, it's
+ // removed from the array
+ wxArrayThread m_threads;
+
+ // both of these arrays are only valid between 2 iterations of OnIdle(),
+ // they're cleared each time it is excuted.
+
// the array of threads which finished (either because they did their work
// or because they were explicitly stopped)
- wxArrayInt m_aToDelete;
+ wxArrayThread m_terminated;
+ // the array of threads which were stopped by the user and not terminated
+ // by themselves - these threads shouldn't be Delete()d second time from
+ // OnIdle()
+ wxArrayThread m_stopped;
+
// just some place to put our messages in
wxTextCtrl *m_txtctrl;
+ // remember the number of running threads and total number of threads
+ size_t m_nRunning, m_nCount;
+
DECLARE_EVENT_TABLE()
};
{
TEST_QUIT = 1,
TEST_TEXT = 101,
- TEST_ABOUT = 102,
- TEST_CLEAR = 103,
- TEST_START_THREAD = 203,
- TEST_STOP_THREAD = 204,
- TEST_PAUSE_THREAD = 205,
- TEST_RESUME_THREAD = 206
+ TEST_ABOUT,
+ TEST_CLEAR,
+ TEST_START_THREAD = 201,
+ TEST_START_THREADS,
+ TEST_STOP_THREAD,
+ TEST_PAUSE_THREAD,
+ TEST_RESUME_THREAD
};
BEGIN_EVENT_TABLE(MyFrame, wxFrame)
EVT_MENU(TEST_ABOUT, MyFrame::OnAbout)
EVT_MENU(TEST_CLEAR, MyFrame::OnClear)
EVT_MENU(TEST_START_THREAD, MyFrame::OnStartThread)
+ EVT_MENU(TEST_START_THREADS, MyFrame::OnStartThreads)
EVT_MENU(TEST_STOP_THREAD, MyFrame::OnStopThread)
EVT_MENU(TEST_PAUSE_THREAD, MyFrame::OnPauseThread)
EVT_MENU(TEST_RESUME_THREAD, MyFrame::OnResumeThread)
wxMenu *thread_menu = new wxMenu;
thread_menu->Append(TEST_START_THREAD, "&Start a new thread");
+ thread_menu->Append(TEST_START_THREADS, "Start &many threads at once");
thread_menu->Append(TEST_STOP_THREAD, "S&top a running thread");
thread_menu->AppendSeparator();
thread_menu->Append(TEST_PAUSE_THREAD, "&Pause a running thread");
int x, int y, int w, int h)
: wxFrame(frame, -1, title, wxPoint(x, y), wxSize(w, h))
{
- CreateStatusBar();
+ m_nRunning = m_nCount = 0;
+
+ CreateStatusBar(2);
m_txtctrl = new wxTextCtrl(this, -1, "", wxPoint(0, 0), wxSize(0, 0),
wxTE_MULTILINE | wxTE_READONLY);
}
-void MyFrame::OnStartThread(wxCommandEvent& WXUNUSED(event) )
+MyThread *MyFrame::CreateThread()
{
MyThread *thread = new MyThread(this);
wxCriticalSectionLocker enter(m_critsect);
m_threads.Add(thread);
+ return thread;
+}
+
+void MyFrame::OnStartThreads(wxCommandEvent& WXUNUSED(event) )
+{
+ static wxString s_str;
+ s_str = wxGetTextFromUser("How many threads to start: ",
+ "wxThread sample",
+ s_str, this);
+ if ( s_str.IsEmpty() )
+ return;
+
+ size_t count, n;
+ sscanf(s_str, "%u", &count);
+ if ( count == 0 )
+ return;
+
+ wxArrayThread threads;
+
+ // first create them all...
+ for ( n = 0; n < count; n++ )
+ {
+ threads.Add(CreateThread());
+ }
+
+ wxString msg;
+ msg.Printf("%d new threads created.", count);
+ SetStatusText(msg, 1);
+
+ // ...and then start them
+ for ( n = 0; n < count; n++ )
+ {
+ threads[n]->Run();
+ }
+}
+
+void MyFrame::OnStartThread(wxCommandEvent& WXUNUSED(event) )
+{
+ MyThread *thread = CreateThread();
+
if ( thread->Run() != wxTHREAD_NO_ERROR )
{
wxLogError("Can't start thread!");
}
+
+ SetStatusText("New thread started.", 1);
}
void MyFrame::OnStopThread(wxCommandEvent& WXUNUSED(event) )
}
else
{
- m_threads.Last()->Delete();
+ m_critsect.Enter();
+
+ wxThread *thread = m_threads.Last();
+ m_stopped.Add(thread);
+
+ // it's important to leave critical section before calling Delete()
+ // because delete will (implicitly) call OnThreadExit() which also tries
+ // to enter the same crit section - would dead lock.
+ m_critsect.Leave();
+
+ thread->Delete();
+
+ SetStatusText("Thread stopped.", 1);
}
}
n++;
if ( n == count )
+ {
wxLogError("No thread to resume!");
+ }
else
+ {
m_threads[n]->Resume();
+
+ SetStatusText("Thread resumed.", 1);
+ }
}
void MyFrame::OnPauseThread(wxCommandEvent& WXUNUSED(event) )
n--;
if ( n < 0 )
+ {
wxLogError("No thread to pause!");
+ }
else
+ {
m_threads[n]->Pause();
+
+ SetStatusText("Thread paused.", 1);
+ }
}
// set the frame title indicating the current number of threads
void MyFrame::OnIdle(wxIdleEvent &event)
{
- // first remove from the array all the threads which died since last call
+ // first wait for all the threads which dies since the last call
{
wxCriticalSectionLocker enter(m_critsect);
- size_t nCount = m_aToDelete.Count();
+ size_t nCount = m_terminated.GetCount();
for ( size_t n = 0; n < nCount; n++ )
{
- // index should be shifted by n because we've already deleted
- // n-1 elements from the array
- m_threads.Remove((size_t)m_aToDelete[n] - n);
+ // don't delete the threads which were stopped - they were already
+ // deleted in OnStopThread()
+ wxThread *thread = m_terminated[n];
+ if ( m_stopped.Index(thread) == wxNOT_FOUND )
+ thread->Delete();
}
- m_aToDelete.Empty();
+ m_stopped.Empty();
+ m_terminated.Empty();
}
size_t nRunning = 0,
nRunning++;
}
- wxLogStatus(this, "%u threads total, %u running.", nCount, nRunning);
+ if ( nCount != m_nCount || nRunning != m_nRunning )
+ {
+ m_nRunning = nRunning;
+ m_nCount = nCount;
+
+ wxLogStatus(this, "%u threads total, %u running.", nCount, nRunning);
+ }
+ //else: avoid flicker - don't print anything
}
void MyFrame::OnQuit(wxCommandEvent& WXUNUSED(event) )
void MyFrame::OnThreadExit(wxThread *thread)
{
- int index = m_threads.Index(thread);
- wxCHECK_RET( index != -1, "unknown thread being deleted??" );
-
wxCriticalSectionLocker enter(m_critsect);
- m_aToDelete.Add(index);
+ m_threads.Remove(thread);
+ m_terminated.Add(thread);
}
#include "wx/utils.h"
#include "wx/log.h"
#include "wx/intl.h"
+#include "wx/dynarray.h"
#include "gdk/gdk.h"
#include "gtk/gtk.h"
STATE_EXITED
};
-//--------------------------------------------------------------------
+WX_DEFINE_ARRAY(wxThread *, wxArrayThread);
+
+// -----------------------------------------------------------------------------
// global data
-//--------------------------------------------------------------------
+// -----------------------------------------------------------------------------
+
+// we keep the list of all threads created by the application to be able to
+// terminate them on exit if there are some left - otherwise the process would
+// be left in memory
+static wxArrayThread gs_allThreads;
// the id of the main thread
-static pthread_t gs_pidMain;
+static pthread_t gs_tidMain;
// the key for the pointer to the associated wxThread object
static pthread_key_t gs_keySelf;
wxMutex::~wxMutex()
{
if (m_locked > 0)
- wxLogDebug( "wxMutex warning: freeing a locked mutex (%d locks)", m_locked );
+ wxLogDebug("Freeing a locked mutex (%d locks)", m_locked);
pthread_mutex_destroy( &(p_internal->p_mutex) );
delete p_internal;
int err = pthread_mutex_lock( &(p_internal->p_mutex) );
if (err == EDEADLK)
{
+ wxLogDebug("Locking this mutex would lead to deadlock!");
+
return wxMUTEX_DEAD_LOCK;
}
}
else
{
+ wxLogDebug("Unlocking not locked mutex.");
+
return wxMUTEX_UNLOCKED;
}
class wxThreadInternal
{
public:
- wxThreadInternal() { m_state = STATE_NEW; }
- ~wxThreadInternal() {}
+ wxThreadInternal();
+ ~wxThreadInternal();
// thread entry function
static void *PthreadStart(void *ptr);
- // start the thread
+ // thread actions
+ // start the thread
wxThreadError Run();
+ // ask the thread to terminate
+ void Cancel();
+ // wake up threads waiting for our termination
+ void SignalExit();
+ // go to sleep until Resume() is called
+ void Pause();
+ // resume the thread
+ void Resume();
// accessors
// priority
// id
pthread_t GetId() const { return thread_id; }
// "cancelled" flag
- void Cancel();
bool WasCancelled() const { return m_cancelled; }
//private: -- should be!
// set when the thread should terminate
bool m_cancelled;
- // we start running when this condition becomes true
- wxMutex m_mutexRun;
- wxCondition m_condRun;
-
- // this condition becomes true when we get back to PthreadStart() function
- wxMutex m_mutexStop;
- wxCondition m_condStop;
+ // this (mutex, cond) pair is used to synchronize the main thread and this
+ // thread in several situations:
+ // 1. The thread function blocks until condition is signaled by Run() when
+ // it's initially created - this allows create thread in "suspended"
+ // state
+ // 2. The Delete() function blocks until the condition is signaled when the
+ // thread exits.
+ wxMutex m_mutex;
+ wxCondition m_cond;
+
+ // another (mutex, cond) pair for Pause()/Resume() usage
+ //
+ // VZ: it's possible that we might reuse the mutex and condition from above
+ // for this too, but as I'm not at all sure that it won't create subtle
+ // problems with race conditions between, say, Pause() and Delete() I
+ // prefer this may be a bit less efficient but much safer solution
+ wxMutex m_mutexSuspend;
+ wxCondition m_condSuspend;
};
void *wxThreadInternal::PthreadStart(void *ptr)
}
// wait for the condition to be signaled from Run()
- pthread->m_mutexRun.Lock();
- pthread->m_condRun.Wait(pthread->m_mutexRun);
+ // mutex state: currently locked by the thread which created us
+ pthread->m_cond.Wait(pthread->m_mutex);
+
+ // mutex state: locked again on exit of Wait()
// call the main entry
void* status = thread->Entry();
- pthread->m_mutexRun.Unlock();
-
- // wake up the pthread(s) waiting for our termination
- pthread->m_condStop.Broadcast();
-
// terminate the thread
thread->Exit(status);
return NULL;
}
+wxThreadInternal::wxThreadInternal()
+{
+ m_state = STATE_NEW;
+ m_cancelled = FALSE;
+
+ // this mutex is locked during almost all thread lifetime - it will only be
+ // unlocked in the very end
+ m_mutex.Lock();
+
+ // this mutex is used in Pause()/Resume() and is also locked all the time
+ // unless the thread is paused
+ m_mutexSuspend.Lock();
+}
+
+wxThreadInternal::~wxThreadInternal()
+{
+ m_mutexSuspend.Unlock();
+
+ // note that m_mutex will be unlocked by the thread which waits for our
+ // termination
+}
+
wxThreadError wxThreadInternal::Run()
{
wxCHECK_MSG( GetState() == STATE_NEW, wxTHREAD_RUNNING,
"thread may only be started once after successful Create()" );
- wxMutexLocker lock(&m_mutexRun);
- m_condRun.Signal();
+ // the mutex was locked on Create(), so we will be able to lock it again
+ // only when the thread really starts executing and enters the wait -
+ // otherwise we might signal the condition before anybody is waiting for it
+ wxMutexLocker lock(m_mutex);
+ m_cond.Signal();
+
+ m_state = STATE_RUNNING;
return wxTHREAD_NO_ERROR;
+
+ // now the mutex is unlocked back - but just to allow Wait() function to
+ // terminate by relocking it, so the net result is that the worker thread
+ // starts executing and the mutex is still locked
}
void wxThreadInternal::Cancel()
{
- wxMutexLocker lock(&m_mutexStop);
+ // if the thread we're waiting for is waiting for the GUI mutex, we will
+ // deadlock so make sure we release it temporarily
+ if ( wxThread::IsMain() )
+ wxMutexGuiLeave();
+
+ // nobody ever writes this variable so it's safe to not use any
+ // synchronization here
m_cancelled = TRUE;
- m_condStop.Wait(m_mutexStop);
+
+ // entering Wait() releases the mutex thus allowing SignalExit() to acquire
+ // it and to signal us its termination
+ m_cond.Wait(m_mutex);
+
+ // mutex is still in the locked state - relocked on exit from Wait(), so
+ // unlock it - we don't need it any more, the thread has already terminated
+ m_mutex.Unlock();
+
+ // reacquire GUI mutex
+ if ( wxThread::IsMain() )
+ wxMutexGuiEnter();
+}
+
+void wxThreadInternal::SignalExit()
+{
+ // as mutex is currently locked, this will block until some other thread
+ // (normally the same which created this one) unlocks it by entering Wait()
+ m_mutex.Lock();
+
+ // wake up all the threads waiting for our termination
+ m_cond.Broadcast();
+
+ // after this call mutex will be finally unlocked
+ m_mutex.Unlock();
+}
+
+void wxThreadInternal::Pause()
+{
+ wxCHECK_RET( m_state == STATE_PAUSED,
+ "thread must first be paused with wxThread::Pause()." );
+
+ // wait until the condition is signaled from Resume()
+ m_condSuspend.Wait(m_mutexSuspend);
+}
+
+void wxThreadInternal::Resume()
+{
+ wxCHECK_RET( m_state == STATE_PAUSED,
+ "can't resume thread which is not suspended." );
+
+ // we will be able to lock this mutex only when Pause() starts waiting
+ wxMutexLocker lock(m_mutexSuspend);
+ m_condSuspend.Signal();
+
+ SetState(STATE_RUNNING);
}
// -----------------------------------------------------------------------------
bool wxThread::IsMain()
{
- return (bool)pthread_equal(pthread_self(), gs_pidMain);
+ return (bool)pthread_equal(pthread_self(), gs_tidMain);
}
void wxThread::Yield()
wxThread::wxThread()
{
+ // add this thread to the global list of all threads
+ gs_allThreads.Add(this);
+
p_internal = new wxThreadInternal();
}
pthread_attr_setschedparam(&attr, &sp);
}
- // this is the point of no return
+ // create the new OS thread object
int rc = pthread_create(&p_internal->thread_id, &attr,
wxThreadInternal::PthreadStart, (void *)this);
pthread_attr_destroy(&attr);
if ( p_internal->GetState() == STATE_PAUSED )
{
- p_internal->SetState(STATE_RUNNING);
+ p_internal->Resume();
return wxTHREAD_NO_ERROR;
}
void wxThread::Exit(void *status)
{
- wxThread *ptr = this;
- THREAD_SEND_EXIT_MSG(ptr);
-
+ // first call user-level clean up code
OnExit();
+ // next wake up the threads waiting for us (OTOH, this function won't return
+ // until someone waited for us!)
+ p_internal->SignalExit();
+
p_internal->SetState(STATE_EXITED);
+ // delete both C++ thread object and terminate the OS thread object
delete this;
-
pthread_exit(status);
}
+// also test whether we were paused
bool wxThread::TestDestroy() const
{
wxCriticalSectionLocker lock((wxCriticalSection&)m_critsect);
+ if ( p_internal->GetState() == STATE_PAUSED )
+ {
+ // leave the crit section or the other threads will stop too if they try
+ // to call any of (seemingly harmless) IsXXX() functions while we sleep
+ m_critsect.Leave();
+
+ p_internal->Pause();
+
+ // enter it back before it's finally left in lock object dtor
+ m_critsect.Enter();
+ }
+
return p_internal->WasCancelled();
}
wxThread::~wxThread()
{
+ // remove this thread from the global array
+ gs_allThreads.Remove(this);
}
// -----------------------------------------------------------------------------
gs_mutexGui = new wxMutex();
wxThreadGuiInit();
- gs_pidMain = (int)getpid();
+ gs_tidMain = pthread_self();
gs_mutexGui->Lock();
return TRUE;
void wxThreadModule::OnExit()
{
+ wxASSERT_MSG( wxThread::IsMain(), "only main thread can be here" );
+
+ // terminate any threads left
+ size_t count = gs_allThreads.GetCount();
+ if ( count != 0u )
+ wxLogDebug("Some threads were not terminated by the application.");
+
+ for ( size_t n = 0u; n < count; n++ )
+ {
+ gs_allThreads[n]->Delete();
+ }
+
+ // destroy GUI mutex
gs_mutexGui->Unlock();
wxThreadGuiExit();
delete gs_mutexGui;
+ // and free TLD slot
(void)pthread_key_delete(gs_keySelf);
}
#include "wx/utils.h"
#include "wx/log.h"
#include "wx/intl.h"
+#include "wx/dynarray.h"
#include "gdk/gdk.h"
#include "gtk/gtk.h"
STATE_EXITED
};
-//--------------------------------------------------------------------
+WX_DEFINE_ARRAY(wxThread *, wxArrayThread);
+
+// -----------------------------------------------------------------------------
// global data
-//--------------------------------------------------------------------
+// -----------------------------------------------------------------------------
+
+// we keep the list of all threads created by the application to be able to
+// terminate them on exit if there are some left - otherwise the process would
+// be left in memory
+static wxArrayThread gs_allThreads;
// the id of the main thread
-static pthread_t gs_pidMain;
+static pthread_t gs_tidMain;
// the key for the pointer to the associated wxThread object
static pthread_key_t gs_keySelf;
wxMutex::~wxMutex()
{
if (m_locked > 0)
- wxLogDebug( "wxMutex warning: freeing a locked mutex (%d locks)", m_locked );
+ wxLogDebug("Freeing a locked mutex (%d locks)", m_locked);
pthread_mutex_destroy( &(p_internal->p_mutex) );
delete p_internal;
int err = pthread_mutex_lock( &(p_internal->p_mutex) );
if (err == EDEADLK)
{
+ wxLogDebug("Locking this mutex would lead to deadlock!");
+
return wxMUTEX_DEAD_LOCK;
}
}
else
{
+ wxLogDebug("Unlocking not locked mutex.");
+
return wxMUTEX_UNLOCKED;
}
class wxThreadInternal
{
public:
- wxThreadInternal() { m_state = STATE_NEW; }
- ~wxThreadInternal() {}
+ wxThreadInternal();
+ ~wxThreadInternal();
// thread entry function
static void *PthreadStart(void *ptr);
- // start the thread
+ // thread actions
+ // start the thread
wxThreadError Run();
+ // ask the thread to terminate
+ void Cancel();
+ // wake up threads waiting for our termination
+ void SignalExit();
+ // go to sleep until Resume() is called
+ void Pause();
+ // resume the thread
+ void Resume();
// accessors
// priority
// id
pthread_t GetId() const { return thread_id; }
// "cancelled" flag
- void Cancel();
bool WasCancelled() const { return m_cancelled; }
//private: -- should be!
// set when the thread should terminate
bool m_cancelled;
- // we start running when this condition becomes true
- wxMutex m_mutexRun;
- wxCondition m_condRun;
-
- // this condition becomes true when we get back to PthreadStart() function
- wxMutex m_mutexStop;
- wxCondition m_condStop;
+ // this (mutex, cond) pair is used to synchronize the main thread and this
+ // thread in several situations:
+ // 1. The thread function blocks until condition is signaled by Run() when
+ // it's initially created - this allows create thread in "suspended"
+ // state
+ // 2. The Delete() function blocks until the condition is signaled when the
+ // thread exits.
+ wxMutex m_mutex;
+ wxCondition m_cond;
+
+ // another (mutex, cond) pair for Pause()/Resume() usage
+ //
+ // VZ: it's possible that we might reuse the mutex and condition from above
+ // for this too, but as I'm not at all sure that it won't create subtle
+ // problems with race conditions between, say, Pause() and Delete() I
+ // prefer this may be a bit less efficient but much safer solution
+ wxMutex m_mutexSuspend;
+ wxCondition m_condSuspend;
};
void *wxThreadInternal::PthreadStart(void *ptr)
}
// wait for the condition to be signaled from Run()
- pthread->m_mutexRun.Lock();
- pthread->m_condRun.Wait(pthread->m_mutexRun);
+ // mutex state: currently locked by the thread which created us
+ pthread->m_cond.Wait(pthread->m_mutex);
+
+ // mutex state: locked again on exit of Wait()
// call the main entry
void* status = thread->Entry();
- pthread->m_mutexRun.Unlock();
-
- // wake up the pthread(s) waiting for our termination
- pthread->m_condStop.Broadcast();
-
// terminate the thread
thread->Exit(status);
return NULL;
}
+wxThreadInternal::wxThreadInternal()
+{
+ m_state = STATE_NEW;
+ m_cancelled = FALSE;
+
+ // this mutex is locked during almost all thread lifetime - it will only be
+ // unlocked in the very end
+ m_mutex.Lock();
+
+ // this mutex is used in Pause()/Resume() and is also locked all the time
+ // unless the thread is paused
+ m_mutexSuspend.Lock();
+}
+
+wxThreadInternal::~wxThreadInternal()
+{
+ m_mutexSuspend.Unlock();
+
+ // note that m_mutex will be unlocked by the thread which waits for our
+ // termination
+}
+
wxThreadError wxThreadInternal::Run()
{
wxCHECK_MSG( GetState() == STATE_NEW, wxTHREAD_RUNNING,
"thread may only be started once after successful Create()" );
- wxMutexLocker lock(&m_mutexRun);
- m_condRun.Signal();
+ // the mutex was locked on Create(), so we will be able to lock it again
+ // only when the thread really starts executing and enters the wait -
+ // otherwise we might signal the condition before anybody is waiting for it
+ wxMutexLocker lock(m_mutex);
+ m_cond.Signal();
+
+ m_state = STATE_RUNNING;
return wxTHREAD_NO_ERROR;
+
+ // now the mutex is unlocked back - but just to allow Wait() function to
+ // terminate by relocking it, so the net result is that the worker thread
+ // starts executing and the mutex is still locked
}
void wxThreadInternal::Cancel()
{
- wxMutexLocker lock(&m_mutexStop);
+ // if the thread we're waiting for is waiting for the GUI mutex, we will
+ // deadlock so make sure we release it temporarily
+ if ( wxThread::IsMain() )
+ wxMutexGuiLeave();
+
+ // nobody ever writes this variable so it's safe to not use any
+ // synchronization here
m_cancelled = TRUE;
- m_condStop.Wait(m_mutexStop);
+
+ // entering Wait() releases the mutex thus allowing SignalExit() to acquire
+ // it and to signal us its termination
+ m_cond.Wait(m_mutex);
+
+ // mutex is still in the locked state - relocked on exit from Wait(), so
+ // unlock it - we don't need it any more, the thread has already terminated
+ m_mutex.Unlock();
+
+ // reacquire GUI mutex
+ if ( wxThread::IsMain() )
+ wxMutexGuiEnter();
+}
+
+void wxThreadInternal::SignalExit()
+{
+ // as mutex is currently locked, this will block until some other thread
+ // (normally the same which created this one) unlocks it by entering Wait()
+ m_mutex.Lock();
+
+ // wake up all the threads waiting for our termination
+ m_cond.Broadcast();
+
+ // after this call mutex will be finally unlocked
+ m_mutex.Unlock();
+}
+
+void wxThreadInternal::Pause()
+{
+ wxCHECK_RET( m_state == STATE_PAUSED,
+ "thread must first be paused with wxThread::Pause()." );
+
+ // wait until the condition is signaled from Resume()
+ m_condSuspend.Wait(m_mutexSuspend);
+}
+
+void wxThreadInternal::Resume()
+{
+ wxCHECK_RET( m_state == STATE_PAUSED,
+ "can't resume thread which is not suspended." );
+
+ // we will be able to lock this mutex only when Pause() starts waiting
+ wxMutexLocker lock(m_mutexSuspend);
+ m_condSuspend.Signal();
+
+ SetState(STATE_RUNNING);
}
// -----------------------------------------------------------------------------
bool wxThread::IsMain()
{
- return (bool)pthread_equal(pthread_self(), gs_pidMain);
+ return (bool)pthread_equal(pthread_self(), gs_tidMain);
}
void wxThread::Yield()
wxThread::wxThread()
{
+ // add this thread to the global list of all threads
+ gs_allThreads.Add(this);
+
p_internal = new wxThreadInternal();
}
pthread_attr_setschedparam(&attr, &sp);
}
- // this is the point of no return
+ // create the new OS thread object
int rc = pthread_create(&p_internal->thread_id, &attr,
wxThreadInternal::PthreadStart, (void *)this);
pthread_attr_destroy(&attr);
if ( p_internal->GetState() == STATE_PAUSED )
{
- p_internal->SetState(STATE_RUNNING);
+ p_internal->Resume();
return wxTHREAD_NO_ERROR;
}
void wxThread::Exit(void *status)
{
- wxThread *ptr = this;
- THREAD_SEND_EXIT_MSG(ptr);
-
+ // first call user-level clean up code
OnExit();
+ // next wake up the threads waiting for us (OTOH, this function won't return
+ // until someone waited for us!)
+ p_internal->SignalExit();
+
p_internal->SetState(STATE_EXITED);
+ // delete both C++ thread object and terminate the OS thread object
delete this;
-
pthread_exit(status);
}
+// also test whether we were paused
bool wxThread::TestDestroy() const
{
wxCriticalSectionLocker lock((wxCriticalSection&)m_critsect);
+ if ( p_internal->GetState() == STATE_PAUSED )
+ {
+ // leave the crit section or the other threads will stop too if they try
+ // to call any of (seemingly harmless) IsXXX() functions while we sleep
+ m_critsect.Leave();
+
+ p_internal->Pause();
+
+ // enter it back before it's finally left in lock object dtor
+ m_critsect.Enter();
+ }
+
return p_internal->WasCancelled();
}
wxThread::~wxThread()
{
+ // remove this thread from the global array
+ gs_allThreads.Remove(this);
}
// -----------------------------------------------------------------------------
gs_mutexGui = new wxMutex();
wxThreadGuiInit();
- gs_pidMain = (int)getpid();
+ gs_tidMain = pthread_self();
gs_mutexGui->Lock();
return TRUE;
void wxThreadModule::OnExit()
{
+ wxASSERT_MSG( wxThread::IsMain(), "only main thread can be here" );
+
+ // terminate any threads left
+ size_t count = gs_allThreads.GetCount();
+ if ( count != 0u )
+ wxLogDebug("Some threads were not terminated by the application.");
+
+ for ( size_t n = 0u; n < count; n++ )
+ {
+ gs_allThreads[n]->Delete();
+ }
+
+ // destroy GUI mutex
gs_mutexGui->Unlock();
wxThreadGuiExit();
delete gs_mutexGui;
+ // and free TLD slot
(void)pthread_key_delete(gs_keySelf);
}