X-Git-Url: https://git.saurik.com/wxWidgets.git/blobdiff_plain/cd5e9298159e58f57e05f3b76c9d4a45e1eefe12..3e03bdd85f736ea544012048c9f18abb3ccf7cd6:/samples/thread/thread.cpp diff --git a/samples/thread/thread.cpp b/samples/thread/thread.cpp index 48aef6531e..1fc119359c 100644 --- a/samples/thread/thread.cpp +++ b/samples/thread/thread.cpp @@ -1,19 +1,14 @@ ///////////////////////////////////////////////////////////////////////////// // Name: thread.cpp // Purpose: wxWindows thread sample -// Author: Julian Smart(minimal)/Guilhem Lavaux(thread test) +// Author: Guilhem Lavaux, Vadim Zeitlin // Modified by: // Created: 06/16/98 // RCS-ID: $Id$ -// Copyright: (c) Julian Smart, Markus Holzem, Guilhem Lavaux +// Copyright: (c) 1998-2002 wxWindows team // Licence: wxWindows license ///////////////////////////////////////////////////////////////////////////// -/* - TODO: use worker threads to update progress controls instead of writing - messages - it will be more visual - */ - // For compilers that support precompilation, includes "wx/wx.h". #include "wx/wxprec.h" @@ -31,11 +26,12 @@ #include "wx/thread.h" #include "wx/dynarray.h" -#include "wx/time.h" #include "wx/progdlg.h" +// define this to use wxExecute in the exec tests, otherwise just use system #define USE_EXECUTE + #ifdef USE_EXECUTE #define EXEC(cmd) wxExecute((cmd), wxEXEC_SYNC) #else @@ -49,6 +45,9 @@ WX_DEFINE_ARRAY(wxThread *, wxArrayThread); class MyApp : public wxApp { public: + MyApp(); + virtual ~MyApp(); + virtual bool OnInit(); public: @@ -58,6 +57,15 @@ public: // crit section protects access to all of the arrays below wxCriticalSection m_critsect; + + // the (mutex, condition) pair used to wait for the threads to exit, see + // MyFrame::OnQuit() + wxMutex m_mutexAllDone; + wxCondition m_condAllDone; + + // the last exiting thread should signal m_condAllDone if this is true + // (protected by the same m_critsect) + bool m_waitingUntilAllDone; }; // Create a new application object @@ -76,6 +84,7 @@ public: // accessors for MyWorkerThread (called in its context!) bool Cancelled(); +protected: // callbacks void OnQuit(wxCommandEvent& event); void OnClear(wxCommandEvent& event); @@ -192,14 +201,28 @@ void MyThread::OnExit() { wxCriticalSectionLocker locker(wxGetApp().m_critsect); - wxGetApp().m_threads.Remove(this); + wxArrayThread& threads = wxGetApp().m_threads; + threads.Remove(this); + + if ( threads.IsEmpty() ) + { + // signal the main thread that there are no more threads left if it is + // waiting for us + if ( wxGetApp().m_waitingUntilAllDone ) + { + wxGetApp().m_waitingUntilAllDone = FALSE; + + wxMutexLocker lock(wxGetApp().m_mutexAllDone); + wxGetApp().m_condAllDone.Signal(); + } + } } void *MyThread::Entry() { wxString text; - text.Printf(wxT("Thread 0x%x started (priority = %d).\n"), + text.Printf(wxT("Thread 0x%lx started (priority = %u).\n"), GetId(), GetPriority()); WriteText(text); // wxLogMessage(text); -- test wxLog thread safeness @@ -210,14 +233,14 @@ void *MyThread::Entry() if ( TestDestroy() ) break; - text.Printf(wxT("[%u] Thread 0x%x here.\n"), m_count, GetId()); + text.Printf(wxT("[%u] Thread 0x%lx here.\n"), m_count, GetId()); WriteText(text); // wxSleep() can't be called from non-GUI thread! wxThread::Sleep(1000); } - text.Printf(wxT("Thread 0x%x finished.\n"), GetId()); + text.Printf(wxT("Thread 0x%lx finished.\n"), GetId()); WriteText(text); // wxLogMessage(text); -- test wxLog thread safeness @@ -330,6 +353,22 @@ BEGIN_EVENT_TABLE(MyFrame, wxFrame) EVT_IDLE(MyFrame::OnIdle) END_EVENT_TABLE() +MyApp::MyApp() + : m_condAllDone(m_mutexAllDone) +{ + // the mutex associated with a condition must be initially locked, it will + // only be unlocked when we call Wait() + m_mutexAllDone.Lock(); + + m_waitingUntilAllDone = FALSE; +} + +MyApp::~MyApp() +{ + // the mutex must be unlocked before being destroyed + m_mutexAllDone.Unlock(); +} + // `Main program' equivalent, creating windows and returning main app frame bool MyApp::OnInit() { @@ -338,39 +377,39 @@ bool MyApp::OnInit() //wxLog::AddTraceMask("thread"); // Create the main frame window - MyFrame *frame = new MyFrame((wxFrame *)NULL, "wxWindows threads sample", + MyFrame *frame = new MyFrame((wxFrame *)NULL, _T("wxWindows threads sample"), 50, 50, 450, 340); // Make a menubar wxMenuBar *menuBar = new wxMenuBar; wxMenu *menuFile = new wxMenu; - menuFile->Append(THREAD_CLEAR, "&Clear log\tCtrl-L"); + menuFile->Append(THREAD_CLEAR, _T("&Clear log\tCtrl-L")); menuFile->AppendSeparator(); - menuFile->Append(THREAD_QUIT, "E&xit\tAlt-X"); - menuBar->Append(menuFile, "&File"); + menuFile->Append(THREAD_QUIT, _T("E&xit\tAlt-X")); + menuBar->Append(menuFile, _T("&File")); wxMenu *menuThread = new wxMenu; - menuThread->Append(THREAD_START_THREAD, "&Start a new thread\tCtrl-N"); - menuThread->Append(THREAD_START_THREADS, "Start &many threads at once"); - menuThread->Append(THREAD_STOP_THREAD, "S&top a running thread\tCtrl-S"); + menuThread->Append(THREAD_START_THREAD, _T("&Start a new thread\tCtrl-N")); + menuThread->Append(THREAD_START_THREADS, _T("Start &many threads at once")); + menuThread->Append(THREAD_STOP_THREAD, _T("S&top a running thread\tCtrl-S")); menuThread->AppendSeparator(); - menuThread->Append(THREAD_PAUSE_THREAD, "&Pause a running thread\tCtrl-P"); - menuThread->Append(THREAD_RESUME_THREAD, "&Resume suspended thread\tCtrl-R"); + menuThread->Append(THREAD_PAUSE_THREAD, _T("&Pause a running thread\tCtrl-P")); + menuThread->Append(THREAD_RESUME_THREAD, _T("&Resume suspended thread\tCtrl-R")); menuThread->AppendSeparator(); - menuThread->Append(THREAD_START_WORKER, "Start &worker thread\tCtrl-W"); - menuBar->Append(menuThread, "&Thread"); + menuThread->Append(THREAD_START_WORKER, _T("Start &worker thread\tCtrl-W")); + menuBar->Append(menuThread, _T("&Thread")); wxMenu *menuExec = new wxMenu; - menuExec->Append(THREAD_EXEC_MAIN, "&Launch a program from main thread\tF5"); - menuExec->Append(THREAD_EXEC_THREAD, "L&aunch a program from a thread\tCtrl-F5"); - menuBar->Append(menuExec, "&Execute"); + menuExec->Append(THREAD_EXEC_MAIN, _T("&Launch a program from main thread\tF5")); + menuExec->Append(THREAD_EXEC_THREAD, _T("L&aunch a program from a thread\tCtrl-F5")); + menuBar->Append(menuExec, _T("&Execute")); wxMenu *menuHelp = new wxMenu; - menuHelp->Append(THREAD_SHOWCPUS, "&Show CPU count"); + menuHelp->Append(THREAD_SHOWCPUS, _T("&Show CPU count")); menuHelp->AppendSeparator(); - menuHelp->Append(THREAD_ABOUT, "&About..."); - menuBar->Append(menuHelp, "&Help"); + menuHelp->Append(THREAD_ABOUT, _T("&About...")); + menuBar->Append(menuHelp, _T("&Help")); frame->SetMenuBar(menuBar); @@ -393,7 +432,7 @@ MyFrame::MyFrame(wxFrame *frame, const wxString& title, CreateStatusBar(2); - m_txtctrl = new wxTextCtrl(this, -1, "", wxPoint(0, 0), wxSize(0, 0), + m_txtctrl = new wxTextCtrl(this, -1, _T(""), wxPoint(0, 0), wxSize(0, 0), wxTE_MULTILINE | wxTE_READONLY); } @@ -417,8 +456,8 @@ void MyFrame::OnStartThreads(wxCommandEvent& WXUNUSED(event) ) { static long s_num = 10; - s_num = wxGetNumberFromUser("How many threads to start: ", "", - "wxThread sample", s_num, 1, 10000, this); + s_num = wxGetNumberFromUser(_T("How many threads to start: "), _T(""), + _T("wxThread sample"), s_num, 1, 10000, this); if ( s_num == -1 ) { s_num = 10; @@ -468,7 +507,7 @@ void MyFrame::OnStartThread(wxCommandEvent& WXUNUSED(event) ) wxLogError(wxT("Can't start thread!")); } - SetStatusText("New thread started.", 1); + SetStatusText(_T("New thread started."), 1); } void MyFrame::OnStopThread(wxCommandEvent& WXUNUSED(event) ) @@ -493,7 +532,7 @@ void MyFrame::OnStopThread(wxCommandEvent& WXUNUSED(event) ) thread->Delete(); - SetStatusText("Thread stopped.", 1); + SetStatusText(_T("Thread stopped."), 1); } } @@ -514,7 +553,7 @@ void MyFrame::OnResumeThread(wxCommandEvent& WXUNUSED(event) ) { wxGetApp().m_threads[n]->Resume(); - SetStatusText("Thread resumed.", 1); + SetStatusText(_T("Thread resumed."), 1); } } @@ -535,13 +574,15 @@ void MyFrame::OnPauseThread(wxCommandEvent& WXUNUSED(event) ) { wxGetApp().m_threads[n]->Pause(); - SetStatusText("Thread paused.", 1); + SetStatusText(_T("Thread paused."), 1); } } // set the frame title indicating the current number of threads -void MyFrame::OnIdle(wxIdleEvent &event) +void MyFrame::OnIdle(wxIdleEvent& event) { + wxCriticalSectionLocker enter(wxGetApp().m_critsect); + // update the counts of running/total threads size_t nRunning = 0, nCount = wxGetApp().m_threads.Count(); @@ -559,14 +600,51 @@ void MyFrame::OnIdle(wxIdleEvent &event) wxLogStatus(this, wxT("%u threads total, %u running."), nCount, nRunning); } //else: avoid flicker - don't print anything + + event.Skip(); } void MyFrame::OnQuit(wxCommandEvent& WXUNUSED(event) ) { - size_t count = wxGetApp().m_threads.Count(); - for ( size_t i = 0; i < count; i++ ) + // NB: although the OS will terminate all the threads anyhow when the main + // one exits, it's good practice to do it ourselves -- even if it's not + // completely trivial in this example + + // 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 { - wxGetApp().m_threads[0]->Delete(); + 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 ) + { + // we do, ask them to stop + for ( size_t n = 0; n < count; n++ ) + { + threads[n]->Delete(); + } + + // set the flag for MyThread::OnExit() + wxGetApp().m_waitingUntilAllDone = TRUE; + } + + wxGetApp().m_critsect.Leave(); + + if ( count ) + { + // now wait for them to really terminate but leave the GUI mutex + // before doing it as otherwise we might dead lock + wxMutexGuiLeave(); + + wxGetApp().m_condAllDone.Wait(); + + wxMutexGuiEnter(); + } + //else: no threads to terminate, no condition to wait for } Close(TRUE); @@ -574,16 +652,16 @@ void MyFrame::OnQuit(wxCommandEvent& WXUNUSED(event) ) void MyFrame::OnExecMain(wxCommandEvent& WXUNUSED(event)) { - wxLogMessage("The exit code from the main program is %ld", - EXEC("/bin/echo \"main program\"")); + wxLogMessage(wxT("The exit code from the main program is %ld"), + EXEC(_T("/bin/echo \"main program\""))); } void MyFrame::OnExecThread(wxCommandEvent& WXUNUSED(event)) { - MyExecThread thread("/bin/echo \"child thread\""); + MyExecThread thread(wxT("/bin/echo \"child thread\"")); thread.Run(); - wxLogMessage("The exit code from a child thread is %ld", + wxLogMessage(wxT("The exit code from a child thread is %ld"), (long)thread.Wait()); } @@ -595,31 +673,32 @@ void MyFrame::OnShowCPUs(wxCommandEvent& WXUNUSED(event)) switch ( nCPUs ) { case -1: - msg = "Unknown number of CPUs"; + msg = _T("Unknown number of CPUs"); break; case 0: - msg = "WARNING: you're running without any CPUs!"; + msg = _T("WARNING: you're running without any CPUs!"); break; case 1: - msg = "This system only has one CPU."; + msg = _T("This system only has one CPU."); break; default: - msg.Printf("This system has %d CPUs", nCPUs); + msg.Printf(wxT("This system has %d CPUs"), nCPUs); } - + wxLogMessage(msg); } void MyFrame::OnAbout(wxCommandEvent& WXUNUSED(event) ) { - wxMessageDialog dialog(this, "wxWindows multithreaded application sample\n" - "(c) 1998 Julian Smart, Guilhem Lavaux\n" - "(c) 1999 Vadim Zeitlin\n" - "(c) 2000 Robert Roebling", - "About wxThread sample", + wxMessageDialog dialog(this, + _T("wxWindows multithreaded application sample\n") + _T("(c) 1998 Julian Smart, Guilhem Lavaux\n") + _T("(c) 1999 Vadim Zeitlin\n") + _T("(c) 2000 Robert Roebling"), + _T("About wxThread sample"), wxOK | wxICON_INFORMATION); dialog.ShowModal(); @@ -646,8 +725,8 @@ void MyFrame::OnStartWorker(wxCommandEvent& WXUNUSED(event)) m_dlgProgress = new wxProgressDialog ( - "Progress dialog", - "Wait until the thread terminates or press [Cancel]", + _T("Progress dialog"), + _T("Wait until the thread terminates or press [Cancel]"), 100, this, wxPD_CAN_ABORT | @@ -666,9 +745,9 @@ void MyFrame::OnStartWorker(wxCommandEvent& WXUNUSED(event)) void MyFrame::OnWorkerEvent(wxCommandEvent& event) { #if 0 - WriteText( "Got message from worker thread: " ); + WriteText( _T("Got message from worker thread: ") ); WriteText( event.GetString() ); - WriteText( "\n" ); + WriteText( _T("\n") ); #else int n = event.GetInt(); if ( n == -1 )