home *** CD-ROM | disk | FTP | other *** search
/ OS/2 Shareware BBS: 10 Tools / 10-Tools.zip / wxos2233.zip / wxOS2-2_3_3.zip / wxWindows-2.3.3 / samples / thread / thread.cpp < prev    next >
C/C++ Source or Header  |  2002-08-02  |  21KB  |  779 lines

  1. /////////////////////////////////////////////////////////////////////////////
  2. // Name:        thread.cpp
  3. // Purpose:     wxWindows thread sample
  4. // Author:      Guilhem Lavaux, Vadim Zeitlin
  5. // Modified by:
  6. // Created:     06/16/98
  7. // RCS-ID:      $Id: thread.cpp,v 1.9 2002/08/01 19:12:24 MBN Exp $
  8. // Copyright:   (c) 1998-2002 wxWindows team
  9. // Licence:     wxWindows license
  10. /////////////////////////////////////////////////////////////////////////////
  11.  
  12. // For compilers that support precompilation, includes "wx/wx.h".
  13. #include "wx/wxprec.h"
  14.  
  15. #ifdef __BORLANDC__
  16.     #pragma hdrstop
  17. #endif
  18.  
  19. #ifndef WX_PRECOMP
  20.     #include "wx/wx.h"
  21. #endif
  22.  
  23. #if !wxUSE_THREADS
  24.     #error "This sample requires thread support!"
  25. #endif // wxUSE_THREADS
  26.  
  27. #include "wx/thread.h"
  28. #include "wx/dynarray.h"
  29. #include "wx/time.h"
  30.  
  31. #include "wx/progdlg.h"
  32.  
  33. // define this to use wxExecute in the exec tests, otherwise just use system
  34. #define USE_EXECUTE
  35.  
  36. #ifdef USE_EXECUTE
  37.     #define EXEC(cmd) wxExecute((cmd), wxEXEC_SYNC)
  38. #else
  39.     #define EXEC(cmd) system(cmd)
  40. #endif
  41.  
  42. class MyThread;
  43. WX_DEFINE_ARRAY(wxThread *, wxArrayThread);
  44.  
  45. // Define a new application type
  46. class MyApp : public wxApp
  47. {
  48. public:
  49.     MyApp();
  50.     virtual ~MyApp();
  51.  
  52.     virtual bool OnInit();
  53.  
  54. public:
  55.     // all the threads currently alive - as soon as the thread terminates, it's
  56.     // removed from the array
  57.     wxArrayThread m_threads;
  58.  
  59.     // crit section protects access to all of the arrays below
  60.     wxCriticalSection m_critsect;
  61.  
  62.     // the (mutex, condition) pair used to wait for the threads to exit, see
  63.     // MyFrame::OnQuit()
  64.     wxMutex m_mutexAllDone;
  65.     wxCondition m_condAllDone;
  66.  
  67.     // the last exiting thread should signal m_condAllDone if this is true
  68.     // (protected by the same m_critsect)
  69.     bool m_waitingUntilAllDone;
  70. };
  71.  
  72. // Create a new application object
  73. IMPLEMENT_APP(MyApp)
  74.  
  75. // Define a new frame type
  76. class MyFrame: public wxFrame
  77. {
  78. public:
  79.     // ctor
  80.     MyFrame(wxFrame *frame, const wxString& title, int x, int y, int w, int h);
  81.  
  82.     // operations
  83.     void WriteText(const wxString& text) { m_txtctrl->WriteText(text); }
  84.  
  85.     // accessors for MyWorkerThread (called in its context!)
  86.     bool Cancelled();
  87.  
  88. protected:
  89.     // callbacks
  90.     void OnQuit(wxCommandEvent& event);
  91.     void OnClear(wxCommandEvent& event);
  92.  
  93.     void OnStartThread(wxCommandEvent& event);
  94.     void OnStartThreads(wxCommandEvent& event);
  95.     void OnStopThread(wxCommandEvent& event);
  96.     void OnPauseThread(wxCommandEvent& event);
  97.     void OnResumeThread(wxCommandEvent& event);
  98.  
  99.     void OnStartWorker(wxCommandEvent& event);
  100.     void OnWorkerEvent(wxCommandEvent& event);
  101.     void OnUpdateWorker(wxUpdateUIEvent& event);
  102.  
  103.     void OnExecMain(wxCommandEvent& event);
  104.     void OnExecThread(wxCommandEvent& event);
  105.  
  106.     void OnShowCPUs(wxCommandEvent& event);
  107.     void OnAbout(wxCommandEvent& event);
  108.  
  109.     void OnIdle(wxIdleEvent &event);
  110.  
  111. private:
  112.     // helper function - creates a new thread (but doesn't run it)
  113.     MyThread *CreateThread();
  114.  
  115.     // just some place to put our messages in
  116.     wxTextCtrl *m_txtctrl;
  117.  
  118.     // remember the number of running threads and total number of threads
  119.     size_t m_nRunning, m_nCount;
  120.  
  121.     // the progress dialog which we show while worker thread is running
  122.     wxProgressDialog *m_dlgProgress;
  123.  
  124.     // was the worker thread cancelled by user?
  125.     bool m_cancelled;
  126.  
  127.     // protects m_cancelled
  128.     wxCriticalSection m_critsectWork;
  129.  
  130.     DECLARE_EVENT_TABLE()
  131. };
  132.  
  133. // ID for the menu commands
  134. enum
  135. {
  136.     THREAD_QUIT          = 1,
  137.     THREAD_TEXT          = 101,
  138.     THREAD_CLEAR,
  139.     THREAD_START_THREAD  = 201,
  140.     THREAD_START_THREADS,
  141.     THREAD_STOP_THREAD,
  142.     THREAD_PAUSE_THREAD,
  143.     THREAD_RESUME_THREAD,
  144.     THREAD_START_WORKER,
  145.  
  146.     THREAD_EXEC_MAIN,
  147.     THREAD_EXEC_THREAD,
  148.  
  149.     THREAD_SHOWCPUS,
  150.     THREAD_ABOUT,
  151.  
  152.     WORKER_EVENT    // this one gets sent from the worker thread
  153. };
  154.  
  155. // ----------------------------------------------------------------------------
  156. // GUI thread
  157. // ----------------------------------------------------------------------------
  158.  
  159. class MyThread : public wxThread
  160. {
  161. public:
  162.     MyThread(MyFrame *frame);
  163.  
  164.     // thread execution starts here
  165.     virtual void *Entry();
  166.  
  167.     // called when the thread exits - whether it terminates normally or is
  168.     // stopped with Delete() (but not when it is Kill()ed!)
  169.     virtual void OnExit();
  170.  
  171.     // write something to the text control
  172.     void WriteText(const wxString& text);
  173.  
  174. public:
  175.     size_t   m_count;
  176.     MyFrame *m_frame;
  177. };
  178.  
  179. MyThread::MyThread(MyFrame *frame)
  180.         : wxThread()
  181. {
  182.     m_count = 0;
  183.     m_frame = frame;
  184. }
  185.  
  186. void MyThread::WriteText(const wxString& text)
  187. {
  188.     wxString msg;
  189.  
  190.     // before doing any GUI calls we must ensure that this thread is the only
  191.     // one doing it!
  192.  
  193.     wxMutexGuiEnter();
  194.  
  195.     msg << text;
  196.     m_frame->WriteText(msg);
  197.  
  198.     wxMutexGuiLeave();
  199. }
  200.  
  201. void MyThread::OnExit()
  202. {
  203.     wxCriticalSectionLocker locker(wxGetApp().m_critsect);
  204.  
  205.     wxArrayThread& threads = wxGetApp().m_threads;
  206.     threads.Remove(this);
  207.  
  208.     if ( threads.IsEmpty() )
  209.     {
  210.         // signal the main thread that there are no more threads left if it is
  211.         // waiting for us
  212.         if ( wxGetApp().m_waitingUntilAllDone )
  213.         {
  214.             wxGetApp().m_waitingUntilAllDone = FALSE;
  215.  
  216.             wxMutexLocker lock(wxGetApp().m_mutexAllDone);
  217.             wxGetApp().m_condAllDone.Signal();
  218.         }
  219.     }
  220. }
  221.  
  222. void *MyThread::Entry()
  223. {
  224.     wxString text;
  225.  
  226.     text.Printf(wxT("Thread 0x%x started (priority = %u).\n"),
  227.                 GetId(), GetPriority());
  228.     WriteText(text);
  229.     // wxLogMessage(text); -- test wxLog thread safeness
  230.  
  231.     for ( m_count = 0; m_count < 10; m_count++ )
  232.     {
  233.         // check if we were asked to exit
  234.         if ( TestDestroy() )
  235.             break;
  236.  
  237.         text.Printf(wxT("[%u] Thread 0x%x here.\n"), m_count, GetId());
  238.         WriteText(text);
  239.  
  240.         // wxSleep() can't be called from non-GUI thread!
  241.         wxThread::Sleep(1000);
  242.     }
  243.  
  244.     text.Printf(wxT("Thread 0x%x finished.\n"), GetId());
  245.     WriteText(text);
  246.     // wxLogMessage(text); -- test wxLog thread safeness
  247.  
  248.     return NULL;
  249. }
  250.  
  251. // ----------------------------------------------------------------------------
  252. // worker thread
  253. // ----------------------------------------------------------------------------
  254.  
  255. class MyWorkerThread : public wxThread
  256. {
  257. public:
  258.     MyWorkerThread(MyFrame *frame);
  259.  
  260.     // thread execution starts here
  261.     virtual void *Entry();
  262.  
  263.     // called when the thread exits - whether it terminates normally or is
  264.     // stopped with Delete() (but not when it is Kill()ed!)
  265.     virtual void OnExit();
  266.  
  267. public:
  268.     MyFrame *m_frame;
  269.     size_t   m_count;
  270. };
  271.  
  272. MyWorkerThread::MyWorkerThread(MyFrame *frame)
  273.         : wxThread()
  274. {
  275.     m_frame = frame;
  276.     m_count = 0;
  277. }
  278.  
  279. void MyWorkerThread::OnExit()
  280. {
  281. }
  282.  
  283. void *MyWorkerThread::Entry()
  284. {
  285.     for ( m_count = 0; !m_frame->Cancelled() && (m_count < 100); m_count++ )
  286.     {
  287.         // check if we were asked to exit
  288.         if ( TestDestroy() )
  289.             break;
  290.  
  291.         // create any type of command event here
  292.         wxCommandEvent event( wxEVT_COMMAND_MENU_SELECTED, WORKER_EVENT );
  293.         event.SetInt( m_count );
  294.  
  295.         // send in a thread-safe way
  296.         wxPostEvent( m_frame, event );
  297.  
  298.         // wxSleep() can't be called from non-main thread!
  299.         wxThread::Sleep(200);
  300.     }
  301.  
  302.     wxCommandEvent event( wxEVT_COMMAND_MENU_SELECTED, WORKER_EVENT );
  303.     event.SetInt(-1); // that's all
  304.     wxPostEvent( m_frame, event );
  305.  
  306.     return NULL;
  307. }
  308.  
  309. // ----------------------------------------------------------------------------
  310. // a thread which simply calls wxExecute
  311. // ----------------------------------------------------------------------------
  312.  
  313. class MyExecThread : public wxThread
  314. {
  315. public:
  316.     MyExecThread(const wxChar *command) : wxThread(wxTHREAD_JOINABLE),
  317.                                           m_command(command)
  318.     {
  319.         Create();
  320.     }
  321.  
  322.     virtual ExitCode Entry()
  323.     {
  324.         return (ExitCode)EXEC(m_command);
  325.     }
  326.  
  327. private:
  328.     wxString m_command;
  329. };
  330.  
  331. // ----------------------------------------------------------------------------
  332. // implementation
  333. // ----------------------------------------------------------------------------
  334.  
  335. BEGIN_EVENT_TABLE(MyFrame, wxFrame)
  336.     EVT_MENU(THREAD_QUIT, MyFrame::OnQuit)
  337.     EVT_MENU(THREAD_CLEAR, MyFrame::OnClear)
  338.     EVT_MENU(THREAD_START_THREAD, MyFrame::OnStartThread)
  339.     EVT_MENU(THREAD_START_THREADS, MyFrame::OnStartThreads)
  340.     EVT_MENU(THREAD_STOP_THREAD, MyFrame::OnStopThread)
  341.     EVT_MENU(THREAD_PAUSE_THREAD, MyFrame::OnPauseThread)
  342.     EVT_MENU(THREAD_RESUME_THREAD, MyFrame::OnResumeThread)
  343.  
  344.     EVT_MENU(THREAD_EXEC_MAIN, MyFrame::OnExecMain)
  345.     EVT_MENU(THREAD_EXEC_THREAD, MyFrame::OnExecThread)
  346.  
  347.     EVT_MENU(THREAD_SHOWCPUS, MyFrame::OnShowCPUs)
  348.     EVT_MENU(THREAD_ABOUT, MyFrame::OnAbout)
  349.  
  350.     EVT_UPDATE_UI(THREAD_START_WORKER, MyFrame::OnUpdateWorker)
  351.     EVT_MENU(THREAD_START_WORKER, MyFrame::OnStartWorker)
  352.     EVT_MENU(WORKER_EVENT, MyFrame::OnWorkerEvent)
  353.  
  354.     EVT_IDLE(MyFrame::OnIdle)
  355. END_EVENT_TABLE()
  356.  
  357. MyApp::MyApp()
  358.      : m_condAllDone(m_mutexAllDone)
  359. {
  360.     // the mutex associated with a condition must be initially locked, it will
  361.     // only be unlocked when we call Wait()
  362.     m_mutexAllDone.Lock();
  363.  
  364.     m_waitingUntilAllDone = FALSE;
  365. }
  366.  
  367. MyApp::~MyApp()
  368. {
  369.     // the mutex must be unlocked before being destroyed
  370.     m_mutexAllDone.Unlock();
  371. }
  372.  
  373. // `Main program' equivalent, creating windows and returning main app frame
  374. bool MyApp::OnInit()
  375. {
  376.     // uncomment this to get some debugging messages from the trace code
  377.     // on the console (or just set WXTRACE env variable to include "thread")
  378.     //wxLog::AddTraceMask("thread");
  379.  
  380.     // Create the main frame window
  381.     MyFrame *frame = new MyFrame((wxFrame *)NULL, "wxWindows threads sample",
  382.                                  50, 50, 450, 340);
  383.  
  384.     // Make a menubar
  385.     wxMenuBar *menuBar = new wxMenuBar;
  386.  
  387.     wxMenu *menuFile = new wxMenu;
  388.     menuFile->Append(THREAD_CLEAR, "&Clear log\tCtrl-L");
  389.     menuFile->AppendSeparator();
  390.     menuFile->Append(THREAD_QUIT, "E&xit\tAlt-X");
  391.     menuBar->Append(menuFile, "&File");
  392.  
  393.     wxMenu *menuThread = new wxMenu;
  394.     menuThread->Append(THREAD_START_THREAD, "&Start a new thread\tCtrl-N");
  395.     menuThread->Append(THREAD_START_THREADS, "Start &many threads at once");
  396.     menuThread->Append(THREAD_STOP_THREAD, "S&top a running thread\tCtrl-S");
  397.     menuThread->AppendSeparator();
  398.     menuThread->Append(THREAD_PAUSE_THREAD, "&Pause a running thread\tCtrl-P");
  399.     menuThread->Append(THREAD_RESUME_THREAD, "&Resume suspended thread\tCtrl-R");
  400.     menuThread->AppendSeparator();
  401.     menuThread->Append(THREAD_START_WORKER, "Start &worker thread\tCtrl-W");
  402.     menuBar->Append(menuThread, "&Thread");
  403.  
  404.     wxMenu *menuExec = new wxMenu;
  405.     menuExec->Append(THREAD_EXEC_MAIN, "&Launch a program from main thread\tF5");
  406.     menuExec->Append(THREAD_EXEC_THREAD, "L&aunch a program from a thread\tCtrl-F5");
  407.     menuBar->Append(menuExec, "&Execute");
  408.  
  409.     wxMenu *menuHelp = new wxMenu;
  410.     menuHelp->Append(THREAD_SHOWCPUS, "&Show CPU count");
  411.     menuHelp->AppendSeparator();
  412.     menuHelp->Append(THREAD_ABOUT, "&About...");
  413.     menuBar->Append(menuHelp, "&Help");
  414.  
  415.     frame->SetMenuBar(menuBar);
  416.  
  417.     // Show the frame
  418.     frame->Show(TRUE);
  419.  
  420.     SetTopWindow(frame);
  421.  
  422.     return TRUE;
  423. }
  424.  
  425. // My frame constructor
  426. MyFrame::MyFrame(wxFrame *frame, const wxString& title,
  427.                  int x, int y, int w, int h)
  428.        : wxFrame(frame, -1, title, wxPoint(x, y), wxSize(w, h))
  429. {
  430.     m_nRunning = m_nCount = 0;
  431.  
  432.     m_dlgProgress = (wxProgressDialog *)NULL;
  433.  
  434.     CreateStatusBar(2);
  435.  
  436.     m_txtctrl = new wxTextCtrl(this, -1, "", wxPoint(0, 0), wxSize(0, 0),
  437.                                wxTE_MULTILINE | wxTE_READONLY);
  438.  
  439. }
  440.  
  441. MyThread *MyFrame::CreateThread()
  442. {
  443.     MyThread *thread = new MyThread(this);
  444.  
  445.     if ( thread->Create() != wxTHREAD_NO_ERROR )
  446.     {
  447.         wxLogError(wxT("Can't create thread!"));
  448.     }
  449.  
  450.     wxCriticalSectionLocker enter(wxGetApp().m_critsect);
  451.     wxGetApp().m_threads.Add(thread);
  452.  
  453.     return thread;
  454. }
  455.  
  456. void MyFrame::OnStartThreads(wxCommandEvent& WXUNUSED(event) )
  457. {
  458.     static long s_num = 10;
  459.  
  460.     s_num = wxGetNumberFromUser("How many threads to start: ", "",
  461.                                 "wxThread sample", s_num, 1, 10000, this);
  462.     if ( s_num == -1 )
  463.     {
  464.         s_num = 10;
  465.  
  466.         return;
  467.     }
  468.  
  469.     size_t count = (size_t)s_num, n;
  470.  
  471.     wxArrayThread threads;
  472.  
  473.     // first create them all...
  474.     for ( n = 0; n < count; n++ )
  475.     {
  476.         wxThread *thr = CreateThread();
  477.  
  478.         // we want to show the effect of SetPriority(): the first thread will
  479.         // have the lowest priority, the second - the highest, all the rest
  480.         // the normal one
  481.         if ( n == 0 )
  482.             thr->SetPriority(WXTHREAD_MIN_PRIORITY);
  483.         else if ( n == 1 )
  484.             thr->SetPriority(WXTHREAD_MAX_PRIORITY);
  485.         else
  486.             thr->SetPriority(WXTHREAD_DEFAULT_PRIORITY);
  487.  
  488.         threads.Add(thr);
  489.     }
  490.  
  491.     wxString msg;
  492.     msg.Printf(wxT("%d new threads created."), count);
  493.     SetStatusText(msg, 1);
  494.  
  495.     // ...and then start them
  496.     for ( n = 0; n < count; n++ )
  497.     {
  498.         threads[n]->Run();
  499.     }
  500. }
  501.  
  502. void MyFrame::OnStartThread(wxCommandEvent& WXUNUSED(event) )
  503. {
  504.     MyThread *thread = CreateThread();
  505.  
  506.     if ( thread->Run() != wxTHREAD_NO_ERROR )
  507.     {
  508.         wxLogError(wxT("Can't start thread!"));
  509.     }
  510.  
  511.     SetStatusText("New thread started.", 1);
  512. }
  513.  
  514. void MyFrame::OnStopThread(wxCommandEvent& WXUNUSED(event) )
  515. {
  516.     wxGetApp().m_critsect.Enter();
  517.  
  518.     // stop the last thread
  519.     if ( wxGetApp().m_threads.IsEmpty() )
  520.     {
  521.         wxLogError(wxT("No thread to stop!"));
  522.  
  523.         wxGetApp().m_critsect.Leave();
  524.     }
  525.     else
  526.     {
  527.         wxThread *thread = wxGetApp().m_threads.Last();
  528.  
  529.         // it's important to leave critical section before calling Delete()
  530.         // because delete will (implicitly) call OnExit() which also tries
  531.         // to enter the same crit section - would dead lock.
  532.         wxGetApp().m_critsect.Leave();
  533.  
  534.         thread->Delete();
  535.  
  536.         SetStatusText("Thread stopped.", 1);
  537.     }
  538. }
  539.  
  540. void MyFrame::OnResumeThread(wxCommandEvent& WXUNUSED(event) )
  541. {
  542.     wxCriticalSectionLocker enter(wxGetApp().m_critsect);
  543.  
  544.     // resume first suspended thread
  545.     size_t n = 0, count = wxGetApp().m_threads.Count();
  546.     while ( n < count && !wxGetApp().m_threads[n]->IsPaused() )
  547.         n++;
  548.  
  549.     if ( n == count )
  550.     {
  551.         wxLogError(wxT("No thread to resume!"));
  552.     }
  553.     else
  554.     {
  555.         wxGetApp().m_threads[n]->Resume();
  556.  
  557.         SetStatusText("Thread resumed.", 1);
  558.     }
  559. }
  560.  
  561. void MyFrame::OnPauseThread(wxCommandEvent& WXUNUSED(event) )
  562. {
  563.     wxCriticalSectionLocker enter(wxGetApp().m_critsect);
  564.  
  565.     // pause last running thread
  566.     int n = wxGetApp().m_threads.Count() - 1;
  567.     while ( n >= 0 && !wxGetApp().m_threads[n]->IsRunning() )
  568.         n--;
  569.  
  570.     if ( n < 0 )
  571.     {
  572.         wxLogError(wxT("No thread to pause!"));
  573.     }
  574.     else
  575.     {
  576.         wxGetApp().m_threads[n]->Pause();
  577.  
  578.         SetStatusText("Thread paused.", 1);
  579.     }
  580. }
  581.  
  582. // set the frame title indicating the current number of threads
  583. void MyFrame::OnIdle(wxIdleEvent &event)
  584. {
  585.     wxCriticalSectionLocker enter(wxGetApp().m_critsect);
  586.  
  587.     // update the counts of running/total threads
  588.     size_t nRunning = 0,
  589.            nCount = wxGetApp().m_threads.Count();
  590.     for ( size_t n = 0; n < nCount; n++ )
  591.     {
  592.         if ( wxGetApp().m_threads[n]->IsRunning() )
  593.             nRunning++;
  594.     }
  595.  
  596.     if ( nCount != m_nCount || nRunning != m_nRunning )
  597.     {
  598.         m_nRunning = nRunning;
  599.         m_nCount = nCount;
  600.  
  601.         wxLogStatus(this, wxT("%u threads total, %u running."), nCount, nRunning);
  602.     }
  603.     //else: avoid flicker - don't print anything
  604. }
  605.  
  606. void MyFrame::OnQuit(wxCommandEvent& WXUNUSED(event) )
  607. {
  608.     // NB: although the OS will terminate all the threads anyhow when the main
  609.     //     one exits, it's good practice to do it ourselves -- even if it's not
  610.     //     completely trivial in this example
  611.  
  612.     // tell all the threads to terminate: note that they can't terminate while
  613.     // we're deleting them because they will block in their OnExit() -- this is
  614.     // important as otherwise we might access invalid array elements
  615.     {
  616.         wxGetApp().m_critsect.Enter();
  617.  
  618.         // check if we have any threads running first
  619.         const wxArrayThread& threads = wxGetApp().m_threads;
  620.         size_t count = threads.GetCount();
  621.  
  622.         if ( count )
  623.         {
  624.             // we do, ask them to stop
  625.             for ( size_t n = 0; n < count; n++ )
  626.             {
  627.                 threads[n]->Delete();
  628.             }
  629.  
  630.             // set the flag for MyThread::OnExit()
  631.             wxGetApp().m_waitingUntilAllDone = TRUE;
  632.         }
  633.  
  634.         wxGetApp().m_critsect.Leave();
  635.  
  636.         if ( count )
  637.         {
  638.             // now wait for them to really terminate but leave the GUI mutex
  639.             // before doing it as otherwise we might dead lock
  640.             wxMutexGuiLeave();
  641.  
  642.             wxGetApp().m_condAllDone.Wait();
  643.  
  644.             wxMutexGuiEnter();
  645.         }
  646.         //else: no threads to terminate, no condition to wait for
  647.     }
  648.  
  649.     Close(TRUE);
  650. }
  651.  
  652. void MyFrame::OnExecMain(wxCommandEvent& WXUNUSED(event))
  653. {
  654.     wxLogMessage(wxT("The exit code from the main program is %ld"),
  655.                  EXEC("/bin/echo \"main program\""));
  656. }
  657.  
  658. void MyFrame::OnExecThread(wxCommandEvent& WXUNUSED(event))
  659. {
  660.     MyExecThread thread(wxT("/bin/echo \"child thread\""));
  661.     thread.Run();
  662.  
  663.     wxLogMessage(wxT("The exit code from a child thread is %ld"),
  664.                  (long)thread.Wait());
  665. }
  666.  
  667. void MyFrame::OnShowCPUs(wxCommandEvent& WXUNUSED(event))
  668. {
  669.     wxString msg;
  670.  
  671.     int nCPUs = wxThread::GetCPUCount();
  672.     switch ( nCPUs )
  673.     {
  674.         case -1:
  675.             msg = "Unknown number of CPUs";
  676.             break;
  677.  
  678.         case 0:
  679.             msg = "WARNING: you're running without any CPUs!";
  680.             break;
  681.  
  682.         case 1:
  683.             msg = "This system only has one CPU.";
  684.             break;
  685.  
  686.         default:
  687.             msg.Printf(wxT("This system has %d CPUs"), nCPUs);
  688.     }
  689.             
  690.     wxLogMessage(msg);
  691. }
  692.  
  693. void MyFrame::OnAbout(wxCommandEvent& WXUNUSED(event) )
  694. {
  695.     wxMessageDialog dialog(this, "wxWindows multithreaded application sample\n"
  696.                                  "(c) 1998 Julian Smart, Guilhem Lavaux\n"
  697.                                  "(c) 1999 Vadim Zeitlin\n"
  698.                                  "(c) 2000 Robert Roebling",
  699.                            "About wxThread sample",
  700.                            wxOK | wxICON_INFORMATION);
  701.  
  702.     dialog.ShowModal();
  703. }
  704.  
  705. void MyFrame::OnClear(wxCommandEvent& WXUNUSED(event))
  706. {
  707.     m_txtctrl->Clear();
  708. }
  709.  
  710. void MyFrame::OnUpdateWorker(wxUpdateUIEvent& event)
  711. {
  712.     event.Enable( m_dlgProgress == NULL );
  713. }
  714.  
  715. void MyFrame::OnStartWorker(wxCommandEvent& WXUNUSED(event))
  716. {
  717.     MyWorkerThread *thread = new MyWorkerThread(this);
  718.  
  719.     if ( thread->Create() != wxTHREAD_NO_ERROR )
  720.     {
  721.         wxLogError(wxT("Can't create thread!"));
  722.     }
  723.  
  724.     m_dlgProgress = new wxProgressDialog
  725.                         (
  726.                          "Progress dialog",
  727.                          "Wait until the thread terminates or press [Cancel]",
  728.                          100,
  729.                          this,
  730.                          wxPD_CAN_ABORT |
  731.                          wxPD_APP_MODAL |
  732.                          wxPD_ELAPSED_TIME |
  733.                          wxPD_ESTIMATED_TIME |
  734.                          wxPD_REMAINING_TIME
  735.                         );
  736.  
  737.     // thread is not running yet, no need for crit sect
  738.     m_cancelled = FALSE;
  739.  
  740.     thread->Run();
  741. }
  742.  
  743. void MyFrame::OnWorkerEvent(wxCommandEvent& event)
  744. {
  745. #if 0
  746.     WriteText( "Got message from worker thread: " );
  747.     WriteText( event.GetString() );
  748.     WriteText( "\n" );
  749. #else
  750.     int n = event.GetInt();
  751.     if ( n == -1 )
  752.     {
  753.         m_dlgProgress->Destroy();
  754.         m_dlgProgress = (wxProgressDialog *)NULL;
  755.  
  756.         // the dialog is aborted because the event came from another thread, so
  757.         // we may need to wake up the main event loop for the dialog to be
  758.         // really closed
  759.         wxWakeUpIdle();
  760.     }
  761.     else
  762.     {
  763.         if ( !m_dlgProgress->Update(n) )
  764.         {
  765.             wxCriticalSectionLocker lock(m_critsectWork);
  766.  
  767.             m_cancelled = TRUE;
  768.         }
  769.     }
  770. #endif
  771. }
  772.  
  773. bool MyFrame::Cancelled()
  774. {
  775.     wxCriticalSectionLocker lock(m_critsectWork);
  776.  
  777.     return m_cancelled;
  778. }
  779.