virtual bool OnInit();
-public:
+ // critical section protects access to all of the fields below
+ wxCriticalSection m_critsect;
+
// all the threads currently alive - as soon as the thread terminates, it's
// removed from the array
wxArrayThread m_threads;
- // crit section protects access to all of the arrays below
- wxCriticalSection m_critsect;
-
// semaphore used to wait for the threads to exit, see MyFrame::OnQuit()
wxSemaphore m_semAllDone;
- // the last exiting thread should post to m_semAllDone if this is true
- // (protected by the same m_critsect)
- bool m_waitingUntilAllDone;
+ // indicates that we're shutting down and all threads should exit
+ bool m_shuttingDown;
};
// Create a new application object
MyFrame(wxFrame *frame, const wxString& title, int x, int y, int w, int h);
virtual ~MyFrame();
- // operations
- void WriteText(const wxString& text) { m_txtctrl->WriteText(text); }
+ // this function is MT-safe, i.e. it can be called from worker threads
+ // safely without any additional locking
+ void LogThreadMessage(const wxString& text)
+ {
+ wxCriticalSectionLocker lock(m_csMessages);
+ m_messages.push_back(text);
+
+ // as we effectively log the messages from the idle event handler,
+ // ensure it's going to be called now that we have some messages to log
+ wxWakeUpIdle();
+ }
// accessors for MyWorkerThread (called in its context!)
bool Cancelled();
-protected:
- // callbacks
+private:
+ // event handlers
void OnQuit(wxCommandEvent& event);
void OnClear(wxCommandEvent& event);
void OnIdle(wxIdleEvent &event);
-private:
// helper function - creates a new thread (but doesn't run it)
MyThread *CreateThread();
+ // update display in our status bar: called during idle handling
+ void UpdateThreadStatus();
+
+ // log the messages queued by LogThreadMessage()
+ void DoLogThreadMessages();
+
+
// just some place to put our messages in
wxTextCtrl *m_txtctrl;
+ // the array of pending messages to be displayed and the critical section
+ // protecting it
+ wxArrayString m_messages;
+ wxCriticalSection m_csMessages;
+
// remember the number of running threads and total number of threads
- size_t m_nRunning, m_nCount;
+ size_t m_nRunning,
+ m_nCount;
// the progress dialog which we show while worker thread is running
wxProgressDialog *m_dlgProgress;
{
public:
MyThread(MyFrame *frame);
+ virtual ~MyThread();
// thread execution starts here
virtual void *Entry();
- // called when the thread exits - whether it terminates normally or is
- // stopped with Delete() (but not when it is Kill()ed!)
- virtual void OnExit();
-
- // write something to the text control
- void WriteText(const wxString& text);
+ // write something to the text control in the main frame
+ void WriteText(const wxString& text)
+ {
+ m_frame->LogThreadMessage(text);
+ }
public:
unsigned m_count;
m_frame = frame;
}
-void MyThread::WriteText(const wxString& text)
-{
- wxString msg;
-
- // before doing any GUI calls we must ensure that this thread is the only
- // one doing it!
-
- wxMutexGuiEnter();
-
- msg << text;
- m_frame->WriteText(msg);
-
- wxMutexGuiLeave();
-}
-
-void MyThread::OnExit()
+MyThread::~MyThread()
{
wxCriticalSectionLocker locker(wxGetApp().m_critsect);
{
// signal the main thread that there are no more threads left if it is
// waiting for us
- if ( wxGetApp().m_waitingUntilAllDone )
+ if ( wxGetApp().m_shuttingDown )
{
- wxGetApp().m_waitingUntilAllDone = false;
+ wxGetApp().m_shuttingDown = false;
wxGetApp().m_semAllDone.Post();
}
for ( m_count = 0; m_count < 10; m_count++ )
{
- // check if we were asked to exit
+ // check if the application is shutting down: in this case all threads
+ // should stop a.s.a.p.
+ {
+ wxCriticalSectionLocker locker(wxGetApp().m_critsect);
+ if ( wxGetApp().m_shuttingDown )
+ return NULL;
+ }
+
+ // check if just this thread was asked to exit
if ( TestDestroy() )
break;
// send in a thread-safe way
wxPostEvent( m_frame, event );
- // wxSleep() can't be called from non-main thread!
- wxThread::Sleep(200);
+ wxMilliSleep(200);
}
wxCommandEvent event( wxEVT_COMMAND_MENU_SELECTED, WORKER_EVENT );
virtual ExitCode Entry()
{
- return (ExitCode)EXEC(m_command);
+ return wxUIntToPtr(EXEC(m_command));
}
private:
END_EVENT_TABLE()
MyApp::MyApp()
- : m_semAllDone()
{
- m_waitingUntilAllDone = false;
+ m_shuttingDown = false;
}
// `Main program' equivalent, creating windows and returning main app frame
// tell all the threads to terminate: note that they can't terminate while
// we're deleting them because they will block in their OnExit() -- this is
// important as otherwise we might access invalid array elements
- wxThread *thread;
-
- wxGetApp().m_critsect.Enter();
-
- // check if we have any threads running first
- const wxArrayThread& threads = wxGetApp().m_threads;
- size_t count = threads.GetCount();
- if ( count )
{
- // set the flag for MyThread::OnExit()
- wxGetApp().m_waitingUntilAllDone = true;
+ wxCriticalSectionLocker locker(wxGetApp().m_critsect);
- // stop all threads
- while ( ! threads.IsEmpty() )
- {
- thread = threads.Last();
-
- wxGetApp().m_critsect.Leave();
+ // check if we have any threads running first
+ const wxArrayThread& threads = wxGetApp().m_threads;
+ size_t count = threads.GetCount();
- thread->Delete();
+ if ( !count )
+ return;
- wxGetApp().m_critsect.Enter();
- }
+ // set the flag indicating that all threads should exit
+ wxGetApp().m_shuttingDown = true;
}
- wxGetApp().m_critsect.Leave();
-
- if ( count )
- {
- // now wait for them to really terminate
- wxGetApp().m_semAllDone.Wait();
- }
- //else: no threads to terminate, no condition to wait for
+ // now wait for them to really terminate
+ wxGetApp().m_semAllDone.Wait();
}
MyThread *MyFrame::CreateThread()
void MyFrame::OnStopThread(wxCommandEvent& WXUNUSED(event) )
{
- wxGetApp().m_critsect.Enter();
+ wxCriticalSectionLocker enter(wxGetApp().m_critsect);
// stop the last thread
if ( wxGetApp().m_threads.IsEmpty() )
{
wxLogError(wxT("No thread to stop!"));
-
- wxGetApp().m_critsect.Leave();
}
else
{
- wxThread *thread = wxGetApp().m_threads.Last();
-
- // it's important to leave critical section before calling Delete()
- // because delete will (implicitly) call OnExit() which also tries
- // to enter the same crit section - would dead lock.
- wxGetApp().m_critsect.Leave();
-
- thread->Delete();
+ wxGetApp().m_threads.Last()->Delete();
#if wxUSE_STATUSBAR
- SetStatusText(_T("Thread stopped."), 1);
+ SetStatusText(_T("Last thread stopped."), 1);
#endif // wxUSE_STATUSBAR
}
}
}
}
-// set the frame title indicating the current number of threads
void MyFrame::OnIdle(wxIdleEvent& event)
+{
+ DoLogThreadMessages();
+
+ UpdateThreadStatus();
+
+ event.Skip();
+}
+
+void MyFrame::DoLogThreadMessages()
+{
+ wxCriticalSectionLocker lock(m_csMessages);
+
+ const size_t count = m_messages.size();
+ for ( size_t n = 0; n < count; n++ )
+ {
+ m_txtctrl->AppendText(m_messages[n]);
+ }
+
+ m_messages.clear();
+}
+
+void MyFrame::UpdateThreadStatus()
{
wxCriticalSectionLocker enter(wxGetApp().m_critsect);
wxLogStatus(this, wxT("%u threads total, %u running."), unsigned(nCount), unsigned(nRunning));
}
//else: avoid flicker - don't print anything
-
- event.Skip();
}
void MyFrame::OnQuit(wxCommandEvent& WXUNUSED(event) )
thread.Run();
wxLogMessage(wxT("The exit code from a child thread is %ld"),
- (long)thread.Wait());
+ (long)wxPtrToUInt(thread.Wait()));
}
void MyFrame::OnShowCPUs(wxCommandEvent& WXUNUSED(event))
void MyFrame::OnWorkerEvent(wxCommandEvent& event)
{
-#if 0
- WriteText( _T("Got message from worker thread: ") );
- WriteText( event.GetString() );
- WriteText( _T("\n") );
-#else
int n = event.GetInt();
if ( n == -1 )
{
m_cancelled = true;
}
}
-#endif
}
bool MyFrame::Cancelled()