1 /////////////////////////////////////////////////////////////////////////////
3 // Purpose: wxWidgets thread sample
4 // Author: Guilhem Lavaux, Vadim Zeitlin
8 // Copyright: (c) 1998-2002 wxWidgets team
9 // Licence: wxWindows license
10 /////////////////////////////////////////////////////////////////////////////
12 // ============================================================================
14 // ============================================================================
16 // ----------------------------------------------------------------------------
18 // ----------------------------------------------------------------------------
20 // For compilers that support precompilation, includes "wx/wx.h".
21 #include "wx/wxprec.h"
32 #error "This sample requires thread support!"
33 #endif // wxUSE_THREADS
35 #include "wx/thread.h"
36 #include "wx/dynarray.h"
37 #include "wx/numdlg.h"
38 #include "wx/progdlg.h"
40 // ----------------------------------------------------------------------------
42 // ----------------------------------------------------------------------------
44 #include "../sample.xpm"
46 // ----------------------------------------------------------------------------
48 // ----------------------------------------------------------------------------
50 // define this to use wxExecute in the exec tests, otherwise just use system
54 #define EXEC(cmd) wxExecute((cmd), wxEXEC_SYNC)
56 #define EXEC(cmd) system(cmd)
60 WX_DEFINE_ARRAY_PTR(wxThread
*, wxArrayThread
);
62 // Define a new application type
63 class MyApp
: public wxApp
69 virtual bool OnInit();
71 // critical section protects access to all of the fields below
72 wxCriticalSection m_critsect
;
74 // all the threads currently alive - as soon as the thread terminates, it's
75 // removed from the array
76 wxArrayThread m_threads
;
78 // semaphore used to wait for the threads to exit, see MyFrame::OnQuit()
79 wxSemaphore m_semAllDone
;
81 // indicates that we're shutting down and all threads should exit
85 // Define a new frame type
86 class MyFrame
: public wxFrame
90 MyFrame(wxFrame
*frame
, const wxString
& title
, int x
, int y
, int w
, int h
);
93 // this function is MT-safe, i.e. it can be called from worker threads
94 // safely without any additional locking
95 void LogThreadMessage(const wxString
& text
)
97 wxCriticalSectionLocker
lock(m_csMessages
);
98 m_messages
.push_back(text
);
100 // as we effectively log the messages from the idle event handler,
101 // ensure it's going to be called now that we have some messages to log
105 // accessors for MyWorkerThread (called in its context!)
110 void OnQuit(wxCommandEvent
& event
);
111 void OnClear(wxCommandEvent
& event
);
113 void OnStartThread(wxCommandEvent
& event
);
114 void OnStartThreads(wxCommandEvent
& event
);
115 void OnStopThread(wxCommandEvent
& event
);
116 void OnPauseThread(wxCommandEvent
& event
);
117 void OnResumeThread(wxCommandEvent
& event
);
119 void OnStartWorker(wxCommandEvent
& event
);
120 void OnWorkerEvent(wxThreadEvent
& event
);
121 void OnUpdateWorker(wxUpdateUIEvent
& event
);
123 void OnExecMain(wxCommandEvent
& event
);
124 void OnExecThread(wxCommandEvent
& event
);
126 void OnShowCPUs(wxCommandEvent
& event
);
127 void OnAbout(wxCommandEvent
& event
);
129 void OnIdle(wxIdleEvent
&event
);
131 // helper function - creates a new thread (but doesn't run it)
132 MyThread
*CreateThread();
134 // update display in our status bar: called during idle handling
135 void UpdateThreadStatus();
137 // log the messages queued by LogThreadMessage()
138 void DoLogThreadMessages();
141 // just some place to put our messages in
142 wxTextCtrl
*m_txtctrl
;
144 // the array of pending messages to be displayed and the critical section
146 wxArrayString m_messages
;
147 wxCriticalSection m_csMessages
;
149 // remember the number of running threads and total number of threads
153 // the progress dialog which we show while worker thread is running
154 wxProgressDialog
*m_dlgProgress
;
156 // was the worker thread cancelled by user?
159 // protects m_cancelled
160 wxCriticalSection m_critsectWork
;
162 DECLARE_EVENT_TABLE()
165 // ----------------------------------------------------------------------------
167 // ----------------------------------------------------------------------------
169 // ID for the menu commands
172 THREAD_QUIT
= wxID_EXIT
,
173 THREAD_ABOUT
= wxID_ABOUT
,
176 THREAD_START_THREAD
= 201,
177 THREAD_START_THREADS
,
180 THREAD_RESUME_THREAD
,
188 WORKER_EVENT
= wxID_HIGHEST
+1 // this one gets sent from the worker thread
191 // ----------------------------------------------------------------------------
193 // ----------------------------------------------------------------------------
195 class MyThread
: public wxThread
198 MyThread(MyFrame
*frame
);
201 // thread execution starts here
202 virtual void *Entry();
204 // write something to the text control in the main frame
205 void WriteText(const wxString
& text
)
207 m_frame
->LogThreadMessage(text
);
215 // ----------------------------------------------------------------------------
217 // ----------------------------------------------------------------------------
219 class MyWorkerThread
: public wxThread
222 MyWorkerThread(MyFrame
*frame
);
224 // thread execution starts here
225 virtual void *Entry();
227 // called when the thread exits - whether it terminates normally or is
228 // stopped with Delete() (but not when it is Kill()ed!)
229 virtual void OnExit();
236 // ----------------------------------------------------------------------------
237 // a thread which simply calls wxExecute
238 // ----------------------------------------------------------------------------
240 class MyExecThread
: public wxThread
243 MyExecThread(const wxChar
*command
) : wxThread(wxTHREAD_JOINABLE
),
249 virtual ExitCode
Entry()
251 return wxUIntToPtr(EXEC(m_command
));
258 // ============================================================================
260 // ============================================================================
262 // ----------------------------------------------------------------------------
263 // the application class
264 // ----------------------------------------------------------------------------
266 // Create a new application object
271 m_shuttingDown
= false;
274 // `Main program' equivalent, creating windows and returning main app frame
277 if ( !wxApp
::OnInit() )
280 // uncomment this to get some debugging messages from the trace code
281 // on the console (or just set WXTRACE env variable to include "thread")
282 //wxLog::AddTraceMask("thread");
284 // Create the main frame window
285 MyFrame
*frame
= new MyFrame((wxFrame
*)NULL
, _T("wxWidgets threads sample"),
295 // ----------------------------------------------------------------------------
297 // ----------------------------------------------------------------------------
299 BEGIN_EVENT_TABLE(MyFrame
, wxFrame
)
300 EVT_MENU(THREAD_QUIT
, MyFrame
::OnQuit
)
301 EVT_MENU(THREAD_CLEAR
, MyFrame
::OnClear
)
302 EVT_MENU(THREAD_START_THREAD
, MyFrame
::OnStartThread
)
303 EVT_MENU(THREAD_START_THREADS
, MyFrame
::OnStartThreads
)
304 EVT_MENU(THREAD_STOP_THREAD
, MyFrame
::OnStopThread
)
305 EVT_MENU(THREAD_PAUSE_THREAD
, MyFrame
::OnPauseThread
)
306 EVT_MENU(THREAD_RESUME_THREAD
, MyFrame
::OnResumeThread
)
308 EVT_MENU(THREAD_EXEC_MAIN
, MyFrame
::OnExecMain
)
309 EVT_MENU(THREAD_EXEC_THREAD
, MyFrame
::OnExecThread
)
311 EVT_MENU(THREAD_SHOWCPUS
, MyFrame
::OnShowCPUs
)
312 EVT_MENU(THREAD_ABOUT
, MyFrame
::OnAbout
)
314 EVT_UPDATE_UI(THREAD_START_WORKER
, MyFrame
::OnUpdateWorker
)
315 EVT_MENU(THREAD_START_WORKER
, MyFrame
::OnStartWorker
)
317 EVT_THREAD(WORKER_EVENT
, MyFrame
::OnWorkerEvent
)
319 EVT_IDLE(MyFrame
::OnIdle
)
322 // My frame constructor
323 MyFrame
::MyFrame(wxFrame
*frame
, const wxString
& title
,
324 int x
, int y
, int w
, int h
)
325 : wxFrame(frame
, wxID_ANY
, title
, wxPoint(x
, y
), wxSize(w
, h
))
327 SetIcon(wxIcon(sample_xpm
));
330 wxMenuBar
*menuBar
= new wxMenuBar
;
332 wxMenu
*menuFile
= new wxMenu
;
333 menuFile
->Append(THREAD_CLEAR
, _T("&Clear log\tCtrl-L"));
334 menuFile
->AppendSeparator();
335 menuFile
->Append(THREAD_QUIT
, _T("E&xit\tAlt-X"));
336 menuBar
->Append(menuFile
, _T("&File"));
338 wxMenu
*menuThread
= new wxMenu
;
339 menuThread
->Append(THREAD_START_THREAD
, _T("&Start a new thread\tCtrl-N"));
340 menuThread
->Append(THREAD_START_THREADS
, _T("Start &many threads at once"));
341 menuThread
->Append(THREAD_STOP_THREAD
, _T("S&top a running thread\tCtrl-S"));
342 menuThread
->AppendSeparator();
343 menuThread
->Append(THREAD_PAUSE_THREAD
, _T("&Pause a running thread\tCtrl-P"));
344 menuThread
->Append(THREAD_RESUME_THREAD
, _T("&Resume suspended thread\tCtrl-R"));
345 menuThread
->AppendSeparator();
346 menuThread
->Append(THREAD_START_WORKER
, _T("Start &worker thread\tCtrl-W"));
347 menuBar
->Append(menuThread
, _T("&Thread"));
349 wxMenu
*menuExec
= new wxMenu
;
350 menuExec
->Append(THREAD_EXEC_MAIN
, _T("&Launch a program from main thread\tF5"));
351 menuExec
->Append(THREAD_EXEC_THREAD
, _T("L&aunch a program from a thread\tCtrl-F5"));
352 menuBar
->Append(menuExec
, _T("&Execute"));
354 wxMenu
*menuHelp
= new wxMenu
;
355 menuHelp
->Append(THREAD_SHOWCPUS
, _T("&Show CPU count"));
356 menuHelp
->AppendSeparator();
357 menuHelp
->Append(THREAD_ABOUT
, _T("&About..."));
358 menuBar
->Append(menuHelp
, _T("&Help"));
362 m_nRunning
= m_nCount
= 0;
364 m_dlgProgress
= (wxProgressDialog
*)NULL
;
368 #endif // wxUSE_STATUSBAR
370 m_txtctrl
= new wxTextCtrl(this, wxID_ANY
, _T(""), wxPoint(0, 0), wxSize(0, 0),
371 wxTE_MULTILINE
| wxTE_READONLY
);
376 // NB: although the OS will terminate all the threads anyhow when the main
377 // one exits, it's good practice to do it ourselves -- even if it's not
378 // completely trivial in this example
380 // tell all the threads to terminate: note that they can't terminate while
381 // we're deleting them because they will block in their OnExit() -- this is
382 // important as otherwise we might access invalid array elements
385 wxCriticalSectionLocker
locker(wxGetApp().m_critsect
);
387 // check if we have any threads running first
388 const wxArrayThread
& threads
= wxGetApp().m_threads
;
389 size_t count
= threads
.GetCount();
394 // set the flag indicating that all threads should exit
395 wxGetApp().m_shuttingDown
= true;
398 // now wait for them to really terminate
399 wxGetApp().m_semAllDone
.Wait();
402 MyThread
*MyFrame
::CreateThread()
404 MyThread
*thread
= new MyThread(this);
406 if ( thread
->Create() != wxTHREAD_NO_ERROR
)
408 wxLogError(wxT("Can't create thread!"));
411 wxCriticalSectionLocker
enter(wxGetApp().m_critsect
);
412 wxGetApp().m_threads
.Add(thread
);
417 void MyFrame
::OnStartThreads(wxCommandEvent
& WXUNUSED(event
) )
421 s_num
= wxGetNumberFromUser(_T("How many threads to start: "), _T(""),
422 _T("wxThread sample"), s_num
, 1, 10000, this);
430 unsigned count
= unsigned(s_num
), n
;
432 wxArrayThread threads
;
434 // first create them all...
435 for ( n
= 0; n
< count
; n
++ )
437 wxThread
*thr
= CreateThread();
439 // we want to show the effect of SetPriority(): the first thread will
440 // have the lowest priority, the second - the highest, all the rest
443 thr
->SetPriority(WXTHREAD_MIN_PRIORITY
);
445 thr
->SetPriority(WXTHREAD_MAX_PRIORITY
);
447 thr
->SetPriority(WXTHREAD_DEFAULT_PRIORITY
);
454 msg
.Printf(wxT("%d new threads created."), count
);
455 SetStatusText(msg
, 1);
456 #endif // wxUSE_STATUSBAR
458 // ...and then start them
459 for ( n
= 0; n
< count
; n
++ )
465 void MyFrame
::OnStartThread(wxCommandEvent
& WXUNUSED(event
) )
467 MyThread
*thread
= CreateThread();
469 if ( thread
->Run() != wxTHREAD_NO_ERROR
)
471 wxLogError(wxT("Can't start thread!"));
475 SetStatusText(_T("New thread started."), 1);
476 #endif // wxUSE_STATUSBAR
479 void MyFrame
::OnStopThread(wxCommandEvent
& WXUNUSED(event
) )
481 wxCriticalSectionLocker
enter(wxGetApp().m_critsect
);
483 // stop the last thread
484 if ( wxGetApp().m_threads
.IsEmpty() )
486 wxLogError(wxT("No thread to stop!"));
490 wxGetApp().m_threads
.Last()->Delete();
493 SetStatusText(_T("Last thread stopped."), 1);
494 #endif // wxUSE_STATUSBAR
498 void MyFrame
::OnResumeThread(wxCommandEvent
& WXUNUSED(event
) )
500 wxCriticalSectionLocker
enter(wxGetApp().m_critsect
);
502 // resume first suspended thread
503 size_t n
= 0, count
= wxGetApp().m_threads
.Count();
504 while ( n
< count
&& !wxGetApp().m_threads
[n
]->IsPaused() )
509 wxLogError(wxT("No thread to resume!"));
513 wxGetApp().m_threads
[n
]->Resume();
516 SetStatusText(_T("Thread resumed."), 1);
517 #endif // wxUSE_STATUSBAR
521 void MyFrame
::OnPauseThread(wxCommandEvent
& WXUNUSED(event
) )
523 wxCriticalSectionLocker
enter(wxGetApp().m_critsect
);
525 // pause last running thread
526 int n
= wxGetApp().m_threads
.Count() - 1;
527 while ( n
>= 0 && !wxGetApp().m_threads
[n
]->IsRunning() )
532 wxLogError(wxT("No thread to pause!"));
536 wxGetApp().m_threads
[n
]->Pause();
539 SetStatusText(_T("Thread paused."), 1);
540 #endif // wxUSE_STATUSBAR
544 void MyFrame
::OnIdle(wxIdleEvent
& event
)
546 DoLogThreadMessages();
548 UpdateThreadStatus();
553 void MyFrame
::DoLogThreadMessages()
555 wxCriticalSectionLocker
lock(m_csMessages
);
557 const size_t count
= m_messages
.size();
558 for ( size_t n
= 0; n
< count
; n
++ )
560 m_txtctrl
->AppendText(m_messages
[n
]);
566 void MyFrame
::UpdateThreadStatus()
568 wxCriticalSectionLocker
enter(wxGetApp().m_critsect
);
570 // update the counts of running/total threads
572 nCount
= wxGetApp().m_threads
.Count();
573 for ( size_t n
= 0; n
< nCount
; n
++ )
575 if ( wxGetApp().m_threads
[n
]->IsRunning() )
579 if ( nCount
!= m_nCount
|| nRunning
!= m_nRunning
)
581 m_nRunning
= nRunning
;
584 wxLogStatus(this, wxT("%u threads total, %u running."), unsigned(nCount
), unsigned(nRunning
));
586 //else: avoid flicker - don't print anything
589 void MyFrame
::OnQuit(wxCommandEvent
& WXUNUSED(event
) )
594 void MyFrame
::OnExecMain(wxCommandEvent
& WXUNUSED(event
))
596 wxLogMessage(wxT("The exit code from the main program is %ld"),
597 EXEC(_T("/bin/echo \"main program\"")));
600 void MyFrame
::OnExecThread(wxCommandEvent
& WXUNUSED(event
))
602 MyExecThread
thread(wxT("/bin/echo \"child thread\""));
605 wxLogMessage(wxT("The exit code from a child thread is %ld"),
606 (long)wxPtrToUInt(thread
.Wait()));
609 void MyFrame
::OnShowCPUs(wxCommandEvent
& WXUNUSED(event
))
613 int nCPUs
= wxThread
::GetCPUCount();
617 msg
= _T("Unknown number of CPUs");
621 msg
= _T("WARNING: you're running without any CPUs!");
625 msg
= _T("This system only has one CPU.");
629 msg
.Printf(wxT("This system has %d CPUs"), nCPUs
);
635 void MyFrame
::OnAbout(wxCommandEvent
& WXUNUSED(event
) )
637 wxMessageDialog
dialog(this,
638 _T("wxWidgets multithreaded application sample\n")
639 _T("(c) 1998 Julian Smart, Guilhem Lavaux\n")
640 _T("(c) 1999 Vadim Zeitlin\n")
641 _T("(c) 2000 Robert Roebling"),
642 _T("About wxThread sample"),
643 wxOK
| wxICON_INFORMATION
);
648 void MyFrame
::OnClear(wxCommandEvent
& WXUNUSED(event
))
653 void MyFrame
::OnUpdateWorker(wxUpdateUIEvent
& event
)
655 event
.Enable( m_dlgProgress
== NULL
);
658 void MyFrame
::OnStartWorker(wxCommandEvent
& WXUNUSED(event
))
660 MyWorkerThread
*thread
= new MyWorkerThread(this);
662 if ( thread
->Create() != wxTHREAD_NO_ERROR
)
664 wxLogError(wxT("Can't create thread!"));
668 m_dlgProgress
= new wxProgressDialog
670 _T("Progress dialog"),
671 _T("Wait until the thread terminates or press [Cancel]"),
677 wxPD_ESTIMATED_TIME
|
681 // thread is not running yet, no need for crit sect
687 void MyFrame
::OnWorkerEvent(wxThreadEvent
& event
)
689 int n
= event
.GetInt();
692 m_dlgProgress
->Destroy();
693 m_dlgProgress
= (wxProgressDialog
*)NULL
;
695 // the dialog is aborted because the event came from another thread, so
696 // we may need to wake up the main event loop for the dialog to be
702 if ( !m_dlgProgress
->Update(n
) )
704 wxCriticalSectionLocker
lock(m_critsectWork
);
711 bool MyFrame
::Cancelled()
713 wxCriticalSectionLocker
lock(m_critsectWork
);
719 // ----------------------------------------------------------------------------
721 // ----------------------------------------------------------------------------
723 MyThread
::MyThread(MyFrame
*frame
)
730 MyThread
::~MyThread()
732 wxCriticalSectionLocker
locker(wxGetApp().m_critsect
);
734 wxArrayThread
& threads
= wxGetApp().m_threads
;
735 threads
.Remove(this);
737 if ( threads
.IsEmpty() )
739 // signal the main thread that there are no more threads left if it is
741 if ( wxGetApp().m_shuttingDown
)
743 wxGetApp().m_shuttingDown
= false;
745 wxGetApp().m_semAllDone
.Post();
750 void *MyThread
::Entry()
754 text
.Printf(wxT("Thread 0x%lx started (priority = %u).\n"),
755 GetId(), GetPriority());
757 // wxLogMessage(text); -- test wxLog thread safeness
759 for ( m_count
= 0; m_count
< 10; m_count
++ )
761 // check if the application is shutting down: in this case all threads
762 // should stop a.s.a.p.
764 wxCriticalSectionLocker
locker(wxGetApp().m_critsect
);
765 if ( wxGetApp().m_shuttingDown
)
769 // check if just this thread was asked to exit
773 text
.Printf(wxT("[%u] Thread 0x%lx here.\n"), m_count
, GetId());
776 // wxSleep() can't be called from non-GUI thread!
777 wxThread
::Sleep(1000);
780 text
.Printf(wxT("Thread 0x%lx finished.\n"), GetId());
782 // wxLogMessage(text); -- test wxLog thread safeness
788 // ----------------------------------------------------------------------------
790 // ----------------------------------------------------------------------------
792 MyWorkerThread
::MyWorkerThread(MyFrame
*frame
)
799 void MyWorkerThread
::OnExit()
803 // define this symbol to 1 to test if the YieldFor() call in the wxProgressDialog::Update
804 // function provokes a race condition in which the second wxThreadEvent posted by
805 // MyWorkerThread::Entry is processed by the YieldFor() call of wxProgressDialog::Update
806 // and results in the destruction of the progress dialog itself, resulting in a crash later.
807 #define TEST_YIELD_RACE_CONDITION 0
809 void *MyWorkerThread
::Entry()
811 #if TEST_YIELD_RACE_CONDITION
815 wxThreadEvent
event( wxEVT_COMMAND_THREAD
, WORKER_EVENT
);
818 wxQueueEvent( m_frame
, event
.Clone() );
821 wxQueueEvent( m_frame
, event
.Clone() );
823 for ( m_count
= 0; !m_frame
->Cancelled() && (m_count
< 100); m_count
++ )
825 // check if we were asked to exit
829 // create any type of command event here
830 wxThreadEvent
event( wxEVT_COMMAND_THREAD
, WORKER_EVENT
);
831 event
.SetInt( m_count
);
833 // send in a thread-safe way
834 wxQueueEvent( m_frame
, event
.Clone() );
839 wxThreadEvent
event( wxEVT_COMMAND_THREAD
, WORKER_EVENT
);
840 event
.SetInt(-1); // that's all
841 wxQueueEvent( m_frame
, event
.Clone() );