1 ///////////////////////////////////////////////////////////////////////////// 
   3 // Purpose:     wxWindows thread sample 
   4 // Author:      Guilhem Lavaux, Vadim Zeitlin 
   8 // Copyright:   (c) 1998-2002 wxWindows team 
   9 // Licence:     wxWindows license 
  10 ///////////////////////////////////////////////////////////////////////////// 
  12 // For compilers that support precompilation, includes "wx/wx.h". 
  13 #include "wx/wxprec.h" 
  24     #error "This sample requires thread support!" 
  25 #endif // wxUSE_THREADS 
  27 #include "wx/thread.h" 
  28 #include "wx/dynarray.h" 
  30 #include "wx/progdlg.h" 
  32 // define this to use wxExecute in the exec tests, otherwise just use system 
  36     #define EXEC(cmd) wxExecute((cmd), wxEXEC_SYNC) 
  38     #define EXEC(cmd) system(cmd) 
  42 WX_DEFINE_ARRAY_NO_PTR(wxThread 
*, wxArrayThread
); 
  44 // Define a new application type 
  45 class MyApp 
: public wxApp
 
  51     virtual bool OnInit(); 
  54     // all the threads currently alive - as soon as the thread terminates, it's 
  55     // removed from the array 
  56     wxArrayThread m_threads
; 
  58     // crit section protects access to all of the arrays below 
  59     wxCriticalSection m_critsect
; 
  61     // semaphore used to wait for the threads to exit, see MyFrame::OnQuit() 
  62     wxSemaphore m_semAllDone
; 
  64     // the last exiting thread should post to m_semAllDone if this is true 
  65     // (protected by the same m_critsect) 
  66     bool m_waitingUntilAllDone
; 
  69 // Create a new application object 
  72 // Define a new frame type 
  73 class MyFrame
: public wxFrame
 
  77     MyFrame(wxFrame 
*frame
, const wxString
& title
, int x
, int y
, int w
, int h
); 
  81     void WriteText(const wxString
& text
) { m_txtctrl
->WriteText(text
); } 
  83     // accessors for MyWorkerThread (called in its context!) 
  88     void OnQuit(wxCommandEvent
& event
); 
  89     void OnClear(wxCommandEvent
& event
); 
  91     void OnStartThread(wxCommandEvent
& event
); 
  92     void OnStartThreads(wxCommandEvent
& event
); 
  93     void OnStopThread(wxCommandEvent
& event
); 
  94     void OnPauseThread(wxCommandEvent
& event
); 
  95     void OnResumeThread(wxCommandEvent
& event
); 
  97     void OnStartWorker(wxCommandEvent
& event
); 
  98     void OnWorkerEvent(wxCommandEvent
& event
); 
  99     void OnUpdateWorker(wxUpdateUIEvent
& event
); 
 101     void OnExecMain(wxCommandEvent
& event
); 
 102     void OnExecThread(wxCommandEvent
& event
); 
 104     void OnShowCPUs(wxCommandEvent
& event
); 
 105     void OnAbout(wxCommandEvent
& event
); 
 107     void OnIdle(wxIdleEvent 
&event
); 
 110     // helper function - creates a new thread (but doesn't run it) 
 111     MyThread 
*CreateThread(); 
 113     // just some place to put our messages in 
 114     wxTextCtrl 
*m_txtctrl
; 
 116     // remember the number of running threads and total number of threads 
 117     size_t m_nRunning
, m_nCount
; 
 119     // the progress dialog which we show while worker thread is running 
 120     wxProgressDialog 
*m_dlgProgress
; 
 122     // was the worker thread cancelled by user? 
 125     // protects m_cancelled 
 126     wxCriticalSection m_critsectWork
; 
 128     DECLARE_EVENT_TABLE() 
 131 // ID for the menu commands 
 137     THREAD_START_THREAD  
= 201, 
 138     THREAD_START_THREADS
, 
 141     THREAD_RESUME_THREAD
, 
 150     WORKER_EVENT    
// this one gets sent from the worker thread 
 153 // ---------------------------------------------------------------------------- 
 155 // ---------------------------------------------------------------------------- 
 157 class MyThread 
: public wxThread
 
 160     MyThread(MyFrame 
*frame
); 
 162     // thread execution starts here 
 163     virtual void *Entry(); 
 165     // called when the thread exits - whether it terminates normally or is 
 166     // stopped with Delete() (but not when it is Kill()ed!) 
 167     virtual void OnExit(); 
 169     // write something to the text control 
 170     void WriteText(const wxString
& text
); 
 177 MyThread::MyThread(MyFrame 
*frame
) 
 184 void MyThread::WriteText(const wxString
& text
) 
 188     // before doing any GUI calls we must ensure that this thread is the only 
 194     m_frame
->WriteText(msg
); 
 199 void MyThread::OnExit() 
 201     wxCriticalSectionLocker 
locker(wxGetApp().m_critsect
); 
 203     wxArrayThread
& threads 
= wxGetApp().m_threads
; 
 204     threads
.Remove(this); 
 206     if ( threads
.IsEmpty() ) 
 208         // signal the main thread that there are no more threads left if it is 
 210         if ( wxGetApp().m_waitingUntilAllDone 
) 
 212             wxGetApp().m_waitingUntilAllDone 
= FALSE
; 
 214             wxGetApp().m_semAllDone
.Post(); 
 219 void *MyThread::Entry() 
 223     text
.Printf(wxT("Thread 0x%lx started (priority = %u).\n"), 
 224                 GetId(), GetPriority()); 
 226     // wxLogMessage(text); -- test wxLog thread safeness 
 228     for ( m_count 
= 0; m_count 
< 10; m_count
++ ) 
 230         // check if we were asked to exit 
 234         text
.Printf(wxT("[%u] Thread 0x%lx here.\n"), m_count
, GetId()); 
 237         // wxSleep() can't be called from non-GUI thread! 
 238         wxThread::Sleep(1000); 
 241     text
.Printf(wxT("Thread 0x%lx finished.\n"), GetId()); 
 243     // wxLogMessage(text); -- test wxLog thread safeness 
 248 // ---------------------------------------------------------------------------- 
 250 // ---------------------------------------------------------------------------- 
 252 class MyWorkerThread 
: public wxThread
 
 255     MyWorkerThread(MyFrame 
*frame
); 
 257     // thread execution starts here 
 258     virtual void *Entry(); 
 260     // called when the thread exits - whether it terminates normally or is 
 261     // stopped with Delete() (but not when it is Kill()ed!) 
 262     virtual void OnExit(); 
 269 MyWorkerThread::MyWorkerThread(MyFrame 
*frame
) 
 276 void MyWorkerThread::OnExit() 
 280 void *MyWorkerThread::Entry() 
 282     for ( m_count 
= 0; !m_frame
->Cancelled() && (m_count 
< 100); m_count
++ ) 
 284         // check if we were asked to exit 
 288         // create any type of command event here 
 289         wxCommandEvent 
event( wxEVT_COMMAND_MENU_SELECTED
, WORKER_EVENT 
); 
 290         event
.SetInt( m_count 
); 
 292         // send in a thread-safe way 
 293         wxPostEvent( m_frame
, event 
); 
 295         // wxSleep() can't be called from non-main thread! 
 296         wxThread::Sleep(200); 
 299     wxCommandEvent 
event( wxEVT_COMMAND_MENU_SELECTED
, WORKER_EVENT 
); 
 300     event
.SetInt(-1); // that's all 
 301     wxPostEvent( m_frame
, event 
); 
 306 // ---------------------------------------------------------------------------- 
 307 // a thread which simply calls wxExecute 
 308 // ---------------------------------------------------------------------------- 
 310 class MyExecThread 
: public wxThread
 
 313     MyExecThread(const wxChar 
*command
) : wxThread(wxTHREAD_JOINABLE
), 
 319     virtual ExitCode 
Entry() 
 321         return (ExitCode
)EXEC(m_command
); 
 328 // ---------------------------------------------------------------------------- 
 330 // ---------------------------------------------------------------------------- 
 332 BEGIN_EVENT_TABLE(MyFrame
, wxFrame
) 
 333     EVT_MENU(THREAD_QUIT
, MyFrame::OnQuit
) 
 334     EVT_MENU(THREAD_CLEAR
, MyFrame::OnClear
) 
 335     EVT_MENU(THREAD_START_THREAD
, MyFrame::OnStartThread
) 
 336     EVT_MENU(THREAD_START_THREADS
, MyFrame::OnStartThreads
) 
 337     EVT_MENU(THREAD_STOP_THREAD
, MyFrame::OnStopThread
) 
 338     EVT_MENU(THREAD_PAUSE_THREAD
, MyFrame::OnPauseThread
) 
 339     EVT_MENU(THREAD_RESUME_THREAD
, MyFrame::OnResumeThread
) 
 341     EVT_MENU(THREAD_EXEC_MAIN
, MyFrame::OnExecMain
) 
 342     EVT_MENU(THREAD_EXEC_THREAD
, MyFrame::OnExecThread
) 
 344     EVT_MENU(THREAD_SHOWCPUS
, MyFrame::OnShowCPUs
) 
 345     EVT_MENU(THREAD_ABOUT
, MyFrame::OnAbout
) 
 347     EVT_UPDATE_UI(THREAD_START_WORKER
, MyFrame::OnUpdateWorker
) 
 348     EVT_MENU(THREAD_START_WORKER
, MyFrame::OnStartWorker
) 
 349     EVT_MENU(WORKER_EVENT
, MyFrame::OnWorkerEvent
) 
 351     EVT_IDLE(MyFrame::OnIdle
) 
 357     m_waitingUntilAllDone 
= FALSE
; 
 364 // `Main program' equivalent, creating windows and returning main app frame 
 367     // uncomment this to get some debugging messages from the trace code 
 368     // on the console (or just set WXTRACE env variable to include "thread") 
 369     //wxLog::AddTraceMask("thread"); 
 371     // Create the main frame window 
 372     MyFrame 
*frame 
= new MyFrame((wxFrame 
*)NULL
, _T("wxWindows threads sample"), 
 376     wxMenuBar 
*menuBar 
= new wxMenuBar
; 
 378     wxMenu 
*menuFile 
= new wxMenu
; 
 379     menuFile
->Append(THREAD_CLEAR
, _T("&Clear log\tCtrl-L")); 
 380     menuFile
->AppendSeparator(); 
 381     menuFile
->Append(THREAD_QUIT
, _T("E&xit\tAlt-X")); 
 382     menuBar
->Append(menuFile
, _T("&File")); 
 384     wxMenu 
*menuThread 
= new wxMenu
; 
 385     menuThread
->Append(THREAD_START_THREAD
, _T("&Start a new thread\tCtrl-N")); 
 386     menuThread
->Append(THREAD_START_THREADS
, _T("Start &many threads at once")); 
 387     menuThread
->Append(THREAD_STOP_THREAD
, _T("S&top a running thread\tCtrl-S")); 
 388     menuThread
->AppendSeparator(); 
 389     menuThread
->Append(THREAD_PAUSE_THREAD
, _T("&Pause a running thread\tCtrl-P")); 
 390     menuThread
->Append(THREAD_RESUME_THREAD
, _T("&Resume suspended thread\tCtrl-R")); 
 391     menuThread
->AppendSeparator(); 
 392     menuThread
->Append(THREAD_START_WORKER
, _T("Start &worker thread\tCtrl-W")); 
 393     menuBar
->Append(menuThread
, _T("&Thread")); 
 395     wxMenu 
*menuExec 
= new wxMenu
; 
 396     menuExec
->Append(THREAD_EXEC_MAIN
, _T("&Launch a program from main thread\tF5")); 
 397     menuExec
->Append(THREAD_EXEC_THREAD
, _T("L&aunch a program from a thread\tCtrl-F5")); 
 398     menuBar
->Append(menuExec
, _T("&Execute")); 
 400     wxMenu 
*menuHelp 
= new wxMenu
; 
 401     menuHelp
->Append(THREAD_SHOWCPUS
, _T("&Show CPU count")); 
 402     menuHelp
->AppendSeparator(); 
 403     menuHelp
->Append(THREAD_ABOUT
, _T("&About...")); 
 404     menuBar
->Append(menuHelp
, _T("&Help")); 
 406     frame
->SetMenuBar(menuBar
); 
 416 // My frame constructor 
 417 MyFrame::MyFrame(wxFrame 
*frame
, const wxString
& title
, 
 418                  int x
, int y
, int w
, int h
) 
 419        : wxFrame(frame
, -1, title
, wxPoint(x
, y
), wxSize(w
, h
)) 
 421     m_nRunning 
= m_nCount 
= 0; 
 423     m_dlgProgress 
= (wxProgressDialog 
*)NULL
; 
 427     m_txtctrl 
= new wxTextCtrl(this, -1, _T(""), wxPoint(0, 0), wxSize(0, 0), 
 428                                wxTE_MULTILINE 
| wxTE_READONLY
); 
 434     // NB: although the OS will terminate all the threads anyhow when the main 
 435     //     one exits, it's good practice to do it ourselves -- even if it's not 
 436     //     completely trivial in this example 
 438     // tell all the threads to terminate: note that they can't terminate while 
 439     // we're deleting them because they will block in their OnExit() -- this is 
 440     // important as otherwise we might access invalid array elements 
 443     wxGetApp().m_critsect
.Enter(); 
 445     // check if we have any threads running first 
 446     const wxArrayThread
& threads 
= wxGetApp().m_threads
; 
 447     size_t count 
= threads
.GetCount(); 
 451         // set the flag for MyThread::OnExit() 
 452         wxGetApp().m_waitingUntilAllDone 
= TRUE
; 
 455         while ( ! threads
.IsEmpty() ) 
 457             thread 
= threads
.Last(); 
 459             wxGetApp().m_critsect
.Leave(); 
 463             wxGetApp().m_critsect
.Enter(); 
 467     wxGetApp().m_critsect
.Leave(); 
 471         // now wait for them to really terminate 
 472         wxGetApp().m_semAllDone
.Wait(); 
 474     //else: no threads to terminate, no condition to wait for 
 477 MyThread 
*MyFrame::CreateThread() 
 479     MyThread 
*thread 
= new MyThread(this); 
 481     if ( thread
->Create() != wxTHREAD_NO_ERROR 
) 
 483         wxLogError(wxT("Can't create thread!")); 
 486     wxCriticalSectionLocker 
enter(wxGetApp().m_critsect
); 
 487     wxGetApp().m_threads
.Add(thread
); 
 492 void MyFrame::OnStartThreads(wxCommandEvent
& WXUNUSED(event
) ) 
 494     static long s_num 
= 10; 
 496     s_num 
= wxGetNumberFromUser(_T("How many threads to start: "), _T(""), 
 497                                 _T("wxThread sample"), s_num
, 1, 10000, this); 
 505     size_t count 
= (size_t)s_num
, n
; 
 507     wxArrayThread threads
; 
 509     // first create them all... 
 510     for ( n 
= 0; n 
< count
; n
++ ) 
 512         wxThread 
*thr 
= CreateThread(); 
 514         // we want to show the effect of SetPriority(): the first thread will 
 515         // have the lowest priority, the second - the highest, all the rest 
 518             thr
->SetPriority(WXTHREAD_MIN_PRIORITY
); 
 520             thr
->SetPriority(WXTHREAD_MAX_PRIORITY
); 
 522             thr
->SetPriority(WXTHREAD_DEFAULT_PRIORITY
); 
 528     msg
.Printf(wxT("%d new threads created."), count
); 
 529     SetStatusText(msg
, 1); 
 531     // ...and then start them 
 532     for ( n 
= 0; n 
< count
; n
++ ) 
 538 void MyFrame::OnStartThread(wxCommandEvent
& WXUNUSED(event
) ) 
 540     MyThread 
*thread 
= CreateThread(); 
 542     if ( thread
->Run() != wxTHREAD_NO_ERROR 
) 
 544         wxLogError(wxT("Can't start thread!")); 
 547     SetStatusText(_T("New thread started."), 1); 
 550 void MyFrame::OnStopThread(wxCommandEvent
& WXUNUSED(event
) ) 
 552     wxGetApp().m_critsect
.Enter(); 
 554     // stop the last thread 
 555     if ( wxGetApp().m_threads
.IsEmpty() ) 
 557         wxLogError(wxT("No thread to stop!")); 
 559         wxGetApp().m_critsect
.Leave(); 
 563         wxThread 
*thread 
= wxGetApp().m_threads
.Last(); 
 565         // it's important to leave critical section before calling Delete() 
 566         // because delete will (implicitly) call OnExit() which also tries 
 567         // to enter the same crit section - would dead lock. 
 568         wxGetApp().m_critsect
.Leave(); 
 572         SetStatusText(_T("Thread stopped."), 1); 
 576 void MyFrame::OnResumeThread(wxCommandEvent
& WXUNUSED(event
) ) 
 578     wxCriticalSectionLocker 
enter(wxGetApp().m_critsect
); 
 580     // resume first suspended thread 
 581     size_t n 
= 0, count 
= wxGetApp().m_threads
.Count(); 
 582     while ( n 
< count 
&& !wxGetApp().m_threads
[n
]->IsPaused() ) 
 587         wxLogError(wxT("No thread to resume!")); 
 591         wxGetApp().m_threads
[n
]->Resume(); 
 593         SetStatusText(_T("Thread resumed."), 1); 
 597 void MyFrame::OnPauseThread(wxCommandEvent
& WXUNUSED(event
) ) 
 599     wxCriticalSectionLocker 
enter(wxGetApp().m_critsect
); 
 601     // pause last running thread 
 602     int n 
= wxGetApp().m_threads
.Count() - 1; 
 603     while ( n 
>= 0 && !wxGetApp().m_threads
[n
]->IsRunning() ) 
 608         wxLogError(wxT("No thread to pause!")); 
 612         wxGetApp().m_threads
[n
]->Pause(); 
 614         SetStatusText(_T("Thread paused."), 1); 
 618 // set the frame title indicating the current number of threads 
 619 void MyFrame::OnIdle(wxIdleEvent
& event
) 
 621     wxCriticalSectionLocker 
enter(wxGetApp().m_critsect
); 
 623     // update the counts of running/total threads 
 625            nCount 
= wxGetApp().m_threads
.Count(); 
 626     for ( size_t n 
= 0; n 
< nCount
; n
++ ) 
 628         if ( wxGetApp().m_threads
[n
]->IsRunning() ) 
 632     if ( nCount 
!= m_nCount 
|| nRunning 
!= m_nRunning 
) 
 634         m_nRunning 
= nRunning
; 
 637         wxLogStatus(this, wxT("%u threads total, %u running."), nCount
, nRunning
); 
 639     //else: avoid flicker - don't print anything 
 644 void MyFrame::OnQuit(wxCommandEvent
& WXUNUSED(event
) ) 
 649 void MyFrame::OnExecMain(wxCommandEvent
& WXUNUSED(event
)) 
 651     wxLogMessage(wxT("The exit code from the main program is %ld"), 
 652                  EXEC(_T("/bin/echo \"main program\""))); 
 655 void MyFrame::OnExecThread(wxCommandEvent
& WXUNUSED(event
)) 
 657     MyExecThread 
thread(wxT("/bin/echo \"child thread\"")); 
 660     wxLogMessage(wxT("The exit code from a child thread is %ld"), 
 661                  (long)thread
.Wait()); 
 664 void MyFrame::OnShowCPUs(wxCommandEvent
& WXUNUSED(event
)) 
 668     int nCPUs 
= wxThread::GetCPUCount(); 
 672             msg 
= _T("Unknown number of CPUs"); 
 676             msg 
= _T("WARNING: you're running without any CPUs!"); 
 680             msg 
= _T("This system only has one CPU."); 
 684             msg
.Printf(wxT("This system has %d CPUs"), nCPUs
); 
 690 void MyFrame::OnAbout(wxCommandEvent
& WXUNUSED(event
) ) 
 692     wxMessageDialog 
dialog(this, 
 693                            _T("wxWindows multithreaded application sample\n") 
 694                            _T("(c) 1998 Julian Smart, Guilhem Lavaux\n") 
 695                            _T("(c) 1999 Vadim Zeitlin\n") 
 696                            _T("(c) 2000 Robert Roebling"), 
 697                            _T("About wxThread sample"), 
 698                            wxOK 
| wxICON_INFORMATION
); 
 703 void MyFrame::OnClear(wxCommandEvent
& WXUNUSED(event
)) 
 708 void MyFrame::OnUpdateWorker(wxUpdateUIEvent
& event
) 
 710     event
.Enable( m_dlgProgress 
== NULL 
); 
 713 void MyFrame::OnStartWorker(wxCommandEvent
& WXUNUSED(event
)) 
 715     MyWorkerThread 
*thread 
= new MyWorkerThread(this); 
 717     if ( thread
->Create() != wxTHREAD_NO_ERROR 
) 
 719         wxLogError(wxT("Can't create thread!")); 
 722     m_dlgProgress 
= new wxProgressDialog
 
 724                          _T("Progress dialog"), 
 725                          _T("Wait until the thread terminates or press [Cancel]"), 
 731                          wxPD_ESTIMATED_TIME 
| 
 735     // thread is not running yet, no need for crit sect 
 741 void MyFrame::OnWorkerEvent(wxCommandEvent
& event
) 
 744     WriteText( _T("Got message from worker thread: ") ); 
 745     WriteText( event
.GetString() ); 
 746     WriteText( _T("\n") ); 
 748     int n 
= event
.GetInt(); 
 751         m_dlgProgress
->Destroy(); 
 752         m_dlgProgress 
= (wxProgressDialog 
*)NULL
; 
 754         // the dialog is aborted because the event came from another thread, so 
 755         // we may need to wake up the main event loop for the dialog to be 
 761         if ( !m_dlgProgress
->Update(n
) ) 
 763             wxCriticalSectionLocker 
lock(m_critsectWork
); 
 771 bool MyFrame::Cancelled() 
 773     wxCriticalSectionLocker 
lock(m_critsectWork
);