X-Git-Url: https://git.saurik.com/wxWidgets.git/blobdiff_plain/8520f1374ceec64abae3802984c4f8654d8836ba..b44f5137ce562c2c281542b09f2ed6379dcbf00b:/samples/thread/thread.cpp diff --git a/samples/thread/thread.cpp b/samples/thread/thread.cpp index dd421d794d..ad2ca59220 100644 --- a/samples/thread/thread.cpp +++ b/samples/thread/thread.cpp @@ -30,6 +30,8 @@ #include "wx/progdlg.h" +#include "../sample.xpm" + // define this to use wxExecute in the exec tests, otherwise just use system #define USE_EXECUTE @@ -47,24 +49,22 @@ class MyApp : public wxApp { public: MyApp(); - virtual ~MyApp(); + virtual ~MyApp(){}; 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 @@ -78,14 +78,23 @@ public: 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); @@ -107,15 +116,27 @@ protected: 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; @@ -132,7 +153,8 @@ private: // ID for the menu commands enum { - THREAD_QUIT = 1, + THREAD_QUIT = wxID_EXIT, + THREAD_ABOUT = wxID_ABOUT, THREAD_TEXT = 101, THREAD_CLEAR, THREAD_START_THREAD = 201, @@ -146,7 +168,6 @@ enum THREAD_EXEC_THREAD, THREAD_SHOWCPUS, - THREAD_ABOUT, WORKER_EVENT // this one gets sent from the worker thread }; @@ -159,19 +180,19 @@ class MyThread : public wxThread { 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: - size_t m_count; + unsigned m_count; MyFrame *m_frame; }; @@ -182,22 +203,7 @@ MyThread::MyThread(MyFrame *frame) 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); @@ -208,9 +214,9 @@ void MyThread::OnExit() { // 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(); } @@ -228,7 +234,15 @@ void *MyThread::Entry() 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; @@ -264,7 +278,7 @@ public: public: MyFrame *m_frame; - size_t m_count; + unsigned m_count; }; MyWorkerThread::MyWorkerThread(MyFrame *frame) @@ -291,15 +305,14 @@ void *MyWorkerThread::Entry() event.SetInt( m_count ); // send in a thread-safe way - wxPostEvent( m_frame, event ); + wxQueueEvent( m_frame, new wxCommandEvent(event) ); - // wxSleep() can't be called from non-main thread! - wxThread::Sleep(200); + wxMilliSleep(200); } wxCommandEvent event( wxEVT_COMMAND_MENU_SELECTED, WORKER_EVENT ); event.SetInt(-1); // that's all - wxPostEvent( m_frame, event ); + wxQueueEvent( m_frame, new wxCommandEvent(event) ); return NULL; } @@ -319,7 +332,7 @@ public: virtual ExitCode Entry() { - return (ExitCode)EXEC(m_command); + return wxUIntToPtr(EXEC(m_command)); } private: @@ -353,18 +366,16 @@ BEGIN_EVENT_TABLE(MyFrame, wxFrame) END_EVENT_TABLE() MyApp::MyApp() - : m_semAllDone() -{ - m_waitingUntilAllDone = false; -} - -MyApp::~MyApp() { + m_shuttingDown = false; } // `Main program' equivalent, creating windows and returning main app frame bool MyApp::OnInit() { + if ( !wxApp::OnInit() ) + return false; + // uncomment this to get some debugging messages from the trace code // on the console (or just set WXTRACE env variable to include "thread") //wxLog::AddTraceMask("thread"); @@ -419,6 +430,8 @@ MyFrame::MyFrame(wxFrame *frame, const wxString& title, int x, int y, int w, int h) : wxFrame(frame, wxID_ANY, title, wxPoint(x, y), wxSize(w, h)) { + SetIcon(wxIcon(sample_xpm)); + m_nRunning = m_nCount = 0; m_dlgProgress = (wxProgressDialog *)NULL; @@ -441,40 +454,23 @@ MyFrame::~MyFrame() // 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; - - // stop all threads - while ( ! threads.IsEmpty() ) - { - thread = threads.Last(); + wxCriticalSectionLocker locker(wxGetApp().m_critsect); - 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() @@ -494,7 +490,7 @@ MyThread *MyFrame::CreateThread() void MyFrame::OnStartThreads(wxCommandEvent& WXUNUSED(event) ) { - static long s_num = 10; + static long s_num; s_num = wxGetNumberFromUser(_T("How many threads to start: "), _T(""), _T("wxThread sample"), s_num, 1, 10000, this); @@ -505,7 +501,7 @@ void MyFrame::OnStartThreads(wxCommandEvent& WXUNUSED(event) ) return; } - size_t count = (size_t)s_num, n; + unsigned count = unsigned(s_num), n; wxArrayThread threads; @@ -556,28 +552,19 @@ void MyFrame::OnStartThread(wxCommandEvent& WXUNUSED(event) ) 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 } } @@ -628,8 +615,29 @@ void MyFrame::OnPauseThread(wxCommandEvent& WXUNUSED(event) ) } } -// 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); @@ -647,11 +655,9 @@ void MyFrame::OnIdle(wxIdleEvent& event) m_nRunning = nRunning; m_nCount = nCount; - wxLogStatus(this, wxT("%u threads total, %u running."), nCount, nRunning); + 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) ) @@ -671,7 +677,7 @@ void MyFrame::OnExecThread(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)) @@ -730,6 +736,7 @@ void MyFrame::OnStartWorker(wxCommandEvent& WXUNUSED(event)) if ( thread->Create() != wxTHREAD_NO_ERROR ) { wxLogError(wxT("Can't create thread!")); + return; } m_dlgProgress = new wxProgressDialog @@ -753,11 +760,6 @@ void MyFrame::OnStartWorker(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 ) { @@ -778,7 +780,6 @@ void MyFrame::OnWorkerEvent(wxCommandEvent& event) m_cancelled = true; } } -#endif } bool MyFrame::Cancelled()