]> git.saurik.com Git - wxWidgets.git/blobdiff - samples/thread/thread.cpp
expat must be added to the template, not generated wxWindows.dsp
[wxWidgets.git] / samples / thread / thread.cpp
index 9c5d3d816ec92db75394124e2c174b754cef7b7b..1040e283d047e0c47aa02e1f8ac814d7ff552710 100644 (file)
@@ -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"
 
 
 #include "wx/thread.h"
 #include "wx/dynarray.h"
-#include "wx/time.h"
 
 #include "wx/progdlg.h"
 
-// uncomment this to get some debugging messages from the trace code
-//#define TRACE
+// 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
+    #define EXEC(cmd) system(cmd)
+#endif
 
 class MyThread;
-WX_DEFINE_ARRAY(wxThread *, wxArrayThread);
+WX_DEFINE_ARRAY_NO_PTR(wxThread *, wxArrayThread);
 
 // Define a new application type
 class MyApp : public wxApp
 {
 public:
+    MyApp();
+    virtual ~MyApp();
+
     virtual bool OnInit();
 
 public:
@@ -54,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
@@ -72,9 +84,9 @@ public:
     // accessors for MyWorkerThread (called in its context!)
     bool Cancelled();
 
+protected:
     // callbacks
     void OnQuit(wxCommandEvent& event);
-    void OnAbout(wxCommandEvent& event);
     void OnClear(wxCommandEvent& event);
 
     void OnStartThread(wxCommandEvent& event);
@@ -87,6 +99,12 @@ public:
     void OnWorkerEvent(wxCommandEvent& event);
     void OnUpdateWorker(wxUpdateUIEvent& event);
 
+    void OnExecMain(wxCommandEvent& event);
+    void OnExecThread(wxCommandEvent& event);
+
+    void OnShowCPUs(wxCommandEvent& event);
+    void OnAbout(wxCommandEvent& event);
+
     void OnIdle(wxIdleEvent &event);
 
 private:
@@ -114,22 +132,28 @@ private:
 // ID for the menu commands
 enum
 {
-    TEST_QUIT          = 1,
-    TEST_TEXT          = 101,
-    TEST_ABOUT,
-    TEST_CLEAR,
-    TEST_START_THREAD  = 201,
-    TEST_START_THREADS,
-    TEST_STOP_THREAD,
-    TEST_PAUSE_THREAD,
-    TEST_RESUME_THREAD,
-    TEST_START_WORKER,
+    THREAD_QUIT          = 1,
+    THREAD_TEXT          = 101,
+    THREAD_CLEAR,
+    THREAD_START_THREAD  = 201,
+    THREAD_START_THREADS,
+    THREAD_STOP_THREAD,
+    THREAD_PAUSE_THREAD,
+    THREAD_RESUME_THREAD,
+    THREAD_START_WORKER,
+
+    THREAD_EXEC_MAIN,
+    THREAD_EXEC_THREAD,
+
+    THREAD_SHOWCPUS,
+    THREAD_ABOUT,
+
     WORKER_EVENT    // this one gets sent from the worker thread
 };
 
-//--------------------------------------------------
+// ----------------------------------------------------------------------------
 // GUI thread
-//--------------------------------------------------
+// ----------------------------------------------------------------------------
 
 class MyThread : public wxThread
 {
@@ -177,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
@@ -195,23 +233,23 @@ 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
 
     return NULL;
 }
 
-//--------------------------------------------------
+// ----------------------------------------------------------------------------
 // worker thread
-//--------------------------------------------------
+// ----------------------------------------------------------------------------
 
 class MyWorkerThread : public wxThread
 {
@@ -249,20 +287,13 @@ void *MyWorkerThread::Entry()
         if ( TestDestroy() )
             break;
 
-        wxString text;
-        text.Printf(wxT("[%u] Thread 0x%x here!!"), m_count, GetId());
-
         // create any type of command event here
         wxCommandEvent event( wxEVT_COMMAND_MENU_SELECTED, WORKER_EVENT );
         event.SetInt( m_count );
-        event.SetString( text );
 
         // send in a thread-safe way
         wxPostEvent( m_frame, event );
 
-        // same as:
-        // m_frame->AddPendingEvent( event );
-
         // wxSleep() can't be called from non-main thread!
         wxThread::Sleep(200);
     }
@@ -274,61 +305,113 @@ void *MyWorkerThread::Entry()
     return NULL;
 }
 
-//--------------------------------------------------
-// main program
-//--------------------------------------------------
+// ----------------------------------------------------------------------------
+// a thread which simply calls wxExecute
+// ----------------------------------------------------------------------------
+
+class MyExecThread : public wxThread
+{
+public:
+    MyExecThread(const wxChar *command) : wxThread(wxTHREAD_JOINABLE),
+                                          m_command(command)
+    {
+        Create();
+    }
+
+    virtual ExitCode Entry()
+    {
+        return (ExitCode)EXEC(m_command);
+    }
+
+private:
+    wxString m_command;
+};
+
+// ----------------------------------------------------------------------------
+// implementation
+// ----------------------------------------------------------------------------
 
 BEGIN_EVENT_TABLE(MyFrame, wxFrame)
-    EVT_MENU(TEST_QUIT, MyFrame::OnQuit)
-    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)
-
-    EVT_UPDATE_UI(TEST_START_WORKER, MyFrame::OnUpdateWorker)
-    EVT_MENU(TEST_START_WORKER, MyFrame::OnStartWorker)
+    EVT_MENU(THREAD_QUIT, MyFrame::OnQuit)
+    EVT_MENU(THREAD_CLEAR, MyFrame::OnClear)
+    EVT_MENU(THREAD_START_THREAD, MyFrame::OnStartThread)
+    EVT_MENU(THREAD_START_THREADS, MyFrame::OnStartThreads)
+    EVT_MENU(THREAD_STOP_THREAD, MyFrame::OnStopThread)
+    EVT_MENU(THREAD_PAUSE_THREAD, MyFrame::OnPauseThread)
+    EVT_MENU(THREAD_RESUME_THREAD, MyFrame::OnResumeThread)
+
+    EVT_MENU(THREAD_EXEC_MAIN, MyFrame::OnExecMain)
+    EVT_MENU(THREAD_EXEC_THREAD, MyFrame::OnExecThread)
+
+    EVT_MENU(THREAD_SHOWCPUS, MyFrame::OnShowCPUs)
+    EVT_MENU(THREAD_ABOUT, MyFrame::OnAbout)
+
+    EVT_UPDATE_UI(THREAD_START_WORKER, MyFrame::OnUpdateWorker)
+    EVT_MENU(THREAD_START_WORKER, MyFrame::OnStartWorker)
     EVT_MENU(WORKER_EVENT, MyFrame::OnWorkerEvent)
 
     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()
 {
-#ifdef TRACE
-    wxLog::AddTraceMask("thread");
-#endif
+    // 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");
 
     // 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
-    wxMenu *file_menu = new wxMenu;
-
-    file_menu->Append(TEST_CLEAR, "&Clear log\tCtrl-L");
-    file_menu->AppendSeparator();
-    file_menu->Append(TEST_ABOUT, "&About");
-    file_menu->AppendSeparator();
-    file_menu->Append(TEST_QUIT, "E&xit\tAlt-X");
-    wxMenuBar *menu_bar = new wxMenuBar;
-    menu_bar->Append(file_menu, "&File");
-
-    wxMenu *thread_menu = new wxMenu;
-    thread_menu->Append(TEST_START_THREAD, "&Start a new thread\tCtrl-N");
-    thread_menu->Append(TEST_START_THREADS, "Start &many threads at once");
-    thread_menu->Append(TEST_STOP_THREAD, "S&top a running thread\tCtrl-S");
-    thread_menu->AppendSeparator();
-    thread_menu->Append(TEST_PAUSE_THREAD, "&Pause a running thread\tCtrl-P");
-    thread_menu->Append(TEST_RESUME_THREAD, "&Resume suspended thread\tCtrl-R");
-    thread_menu->AppendSeparator();
-    thread_menu->Append(TEST_START_WORKER, "Start &worker thread\tCtrl-W");
-
-    menu_bar->Append(thread_menu, "&Thread");
-    frame->SetMenuBar(menu_bar);
+    wxMenuBar *menuBar = new wxMenuBar;
+
+    wxMenu *menuFile = new wxMenu;
+    menuFile->Append(THREAD_CLEAR, _T("&Clear log\tCtrl-L"));
+    menuFile->AppendSeparator();
+    menuFile->Append(THREAD_QUIT, _T("E&xit\tAlt-X"));
+    menuBar->Append(menuFile, _T("&File"));
+
+    wxMenu *menuThread = new wxMenu;
+    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, _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, _T("Start &worker thread\tCtrl-W"));
+    menuBar->Append(menuThread, _T("&Thread"));
+
+    wxMenu *menuExec = new wxMenu;
+    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, _T("&Show CPU count"));
+    menuHelp->AppendSeparator();
+    menuHelp->Append(THREAD_ABOUT, _T("&About..."));
+    menuBar->Append(menuHelp, _T("&Help"));
+
+    frame->SetMenuBar(menuBar);
 
     // Show the frame
     frame->Show(TRUE);
@@ -349,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);
 
 }
@@ -373,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;
@@ -424,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) )
@@ -449,7 +532,7 @@ void MyFrame::OnStopThread(wxCommandEvent& WXUNUSED(event) )
 
         thread->Delete();
 
-        SetStatusText("Thread stopped.", 1);
+        SetStatusText(_T("Thread stopped."), 1);
     }
 }
 
@@ -470,7 +553,7 @@ void MyFrame::OnResumeThread(wxCommandEvent& WXUNUSED(event) )
     {
         wxGetApp().m_threads[n]->Resume();
 
-        SetStatusText("Thread resumed.", 1);
+        SetStatusText(_T("Thread resumed."), 1);
     }
 }
 
@@ -491,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(wxIdleEventevent)
 {
+    wxCriticalSectionLocker enter(wxGetApp().m_critsect);
+
     // update the counts of running/total threads
     size_t nRunning = 0,
            nCount = wxGetApp().m_threads.Count();
@@ -515,26 +600,105 @@ 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);
 }
 
+void MyFrame::OnExecMain(wxCommandEvent& WXUNUSED(event))
+{
+    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(wxT("/bin/echo \"child thread\""));
+    thread.Run();
+
+    wxLogMessage(wxT("The exit code from a child thread is %ld"),
+                 (long)thread.Wait());
+}
+
+void MyFrame::OnShowCPUs(wxCommandEvent& WXUNUSED(event))
+{
+    wxString msg;
+
+    int nCPUs = wxThread::GetCPUCount();
+    switch ( nCPUs )
+    {
+        case -1:
+            msg = _T("Unknown number of CPUs");
+            break;
+
+        case 0:
+            msg = _T("WARNING: you're running without any CPUs!");
+            break;
+
+        case 1:
+            msg = _T("This system only has one CPU.");
+            break;
+
+        default:
+            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();
@@ -542,11 +706,6 @@ void MyFrame::OnAbout(wxCommandEvent& WXUNUSED(event) )
 
 void MyFrame::OnClear(wxCommandEvent& WXUNUSED(event))
 {
-#ifdef TRACE
-    // log a separator
-    wxLogTrace("-------- log window cleared --------");
-#endif
-
     m_txtctrl->Clear();
 }
 
@@ -566,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 |
@@ -586,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 )