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