]> git.saurik.com Git - wxWidgets.git/blob - samples/thread/thread.cpp
as after recent change wxID_SEPARATOR is now -2, it is better for idMenuTitle to...
[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_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 virtual ~MyFrame();
79
80 // operations
81 void WriteText(const wxString& text) { m_txtctrl->WriteText(text); }
82
83 // accessors for MyWorkerThread (called in its context!)
84 bool Cancelled();
85
86 protected:
87 // callbacks
88 void OnQuit(wxCommandEvent& event);
89 void OnClear(wxCommandEvent& event);
90
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);
96
97 void OnStartWorker(wxCommandEvent& event);
98 void OnWorkerEvent(wxCommandEvent& event);
99 void OnUpdateWorker(wxUpdateUIEvent& event);
100
101 void OnExecMain(wxCommandEvent& event);
102 void OnExecThread(wxCommandEvent& event);
103
104 void OnShowCPUs(wxCommandEvent& event);
105 void OnAbout(wxCommandEvent& event);
106
107 void OnIdle(wxIdleEvent &event);
108
109 private:
110 // helper function - creates a new thread (but doesn't run it)
111 MyThread *CreateThread();
112
113 // just some place to put our messages in
114 wxTextCtrl *m_txtctrl;
115
116 // remember the number of running threads and total number of threads
117 size_t m_nRunning, m_nCount;
118
119 // the progress dialog which we show while worker thread is running
120 wxProgressDialog *m_dlgProgress;
121
122 // was the worker thread cancelled by user?
123 bool m_cancelled;
124
125 // protects m_cancelled
126 wxCriticalSection m_critsectWork;
127
128 DECLARE_EVENT_TABLE()
129 };
130
131 // ID for the menu commands
132 enum
133 {
134 THREAD_QUIT = 1,
135 THREAD_TEXT = 101,
136 THREAD_CLEAR,
137 THREAD_START_THREAD = 201,
138 THREAD_START_THREADS,
139 THREAD_STOP_THREAD,
140 THREAD_PAUSE_THREAD,
141 THREAD_RESUME_THREAD,
142 THREAD_START_WORKER,
143
144 THREAD_EXEC_MAIN,
145 THREAD_EXEC_THREAD,
146
147 THREAD_SHOWCPUS,
148 THREAD_ABOUT,
149
150 WORKER_EVENT // this one gets sent from the worker thread
151 };
152
153 // ----------------------------------------------------------------------------
154 // GUI thread
155 // ----------------------------------------------------------------------------
156
157 class MyThread : public wxThread
158 {
159 public:
160 MyThread(MyFrame *frame);
161
162 // thread execution starts here
163 virtual void *Entry();
164
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();
168
169 // write something to the text control
170 void WriteText(const wxString& text);
171
172 public:
173 size_t m_count;
174 MyFrame *m_frame;
175 };
176
177 MyThread::MyThread(MyFrame *frame)
178 : wxThread()
179 {
180 m_count = 0;
181 m_frame = frame;
182 }
183
184 void MyThread::WriteText(const wxString& text)
185 {
186 wxString msg;
187
188 // before doing any GUI calls we must ensure that this thread is the only
189 // one doing it!
190
191 wxMutexGuiEnter();
192
193 msg << text;
194 m_frame->WriteText(msg);
195
196 wxMutexGuiLeave();
197 }
198
199 void MyThread::OnExit()
200 {
201 wxCriticalSectionLocker locker(wxGetApp().m_critsect);
202
203 wxArrayThread& threads = wxGetApp().m_threads;
204 threads.Remove(this);
205
206 if ( threads.IsEmpty() )
207 {
208 // signal the main thread that there are no more threads left if it is
209 // waiting for us
210 if ( wxGetApp().m_waitingUntilAllDone )
211 {
212 wxGetApp().m_waitingUntilAllDone = FALSE;
213
214 wxGetApp().m_semAllDone.Post();
215 }
216 }
217 }
218
219 void *MyThread::Entry()
220 {
221 wxString text;
222
223 text.Printf(wxT("Thread 0x%lx started (priority = %u).\n"),
224 GetId(), GetPriority());
225 WriteText(text);
226 // wxLogMessage(text); -- test wxLog thread safeness
227
228 for ( m_count = 0; m_count < 10; m_count++ )
229 {
230 // check if we were asked to exit
231 if ( TestDestroy() )
232 break;
233
234 text.Printf(wxT("[%u] Thread 0x%lx here.\n"), m_count, GetId());
235 WriteText(text);
236
237 // wxSleep() can't be called from non-GUI thread!
238 wxThread::Sleep(1000);
239 }
240
241 text.Printf(wxT("Thread 0x%lx finished.\n"), GetId());
242 WriteText(text);
243 // wxLogMessage(text); -- test wxLog thread safeness
244
245 return NULL;
246 }
247
248 // ----------------------------------------------------------------------------
249 // worker thread
250 // ----------------------------------------------------------------------------
251
252 class MyWorkerThread : public wxThread
253 {
254 public:
255 MyWorkerThread(MyFrame *frame);
256
257 // thread execution starts here
258 virtual void *Entry();
259
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();
263
264 public:
265 MyFrame *m_frame;
266 size_t m_count;
267 };
268
269 MyWorkerThread::MyWorkerThread(MyFrame *frame)
270 : wxThread()
271 {
272 m_frame = frame;
273 m_count = 0;
274 }
275
276 void MyWorkerThread::OnExit()
277 {
278 }
279
280 void *MyWorkerThread::Entry()
281 {
282 for ( m_count = 0; !m_frame->Cancelled() && (m_count < 100); m_count++ )
283 {
284 // check if we were asked to exit
285 if ( TestDestroy() )
286 break;
287
288 // create any type of command event here
289 wxCommandEvent event( wxEVT_COMMAND_MENU_SELECTED, WORKER_EVENT );
290 event.SetInt( m_count );
291
292 // send in a thread-safe way
293 wxPostEvent( m_frame, event );
294
295 // wxSleep() can't be called from non-main thread!
296 wxThread::Sleep(200);
297 }
298
299 wxCommandEvent event( wxEVT_COMMAND_MENU_SELECTED, WORKER_EVENT );
300 event.SetInt(-1); // that's all
301 wxPostEvent( m_frame, event );
302
303 return NULL;
304 }
305
306 // ----------------------------------------------------------------------------
307 // a thread which simply calls wxExecute
308 // ----------------------------------------------------------------------------
309
310 class MyExecThread : public wxThread
311 {
312 public:
313 MyExecThread(const wxChar *command) : wxThread(wxTHREAD_JOINABLE),
314 m_command(command)
315 {
316 Create();
317 }
318
319 virtual ExitCode Entry()
320 {
321 return (ExitCode)EXEC(m_command);
322 }
323
324 private:
325 wxString m_command;
326 };
327
328 // ----------------------------------------------------------------------------
329 // implementation
330 // ----------------------------------------------------------------------------
331
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)
340
341 EVT_MENU(THREAD_EXEC_MAIN, MyFrame::OnExecMain)
342 EVT_MENU(THREAD_EXEC_THREAD, MyFrame::OnExecThread)
343
344 EVT_MENU(THREAD_SHOWCPUS, MyFrame::OnShowCPUs)
345 EVT_MENU(THREAD_ABOUT, MyFrame::OnAbout)
346
347 EVT_UPDATE_UI(THREAD_START_WORKER, MyFrame::OnUpdateWorker)
348 EVT_MENU(THREAD_START_WORKER, MyFrame::OnStartWorker)
349 EVT_MENU(WORKER_EVENT, MyFrame::OnWorkerEvent)
350
351 EVT_IDLE(MyFrame::OnIdle)
352 END_EVENT_TABLE()
353
354 MyApp::MyApp()
355 : m_semAllDone()
356 {
357 m_waitingUntilAllDone = FALSE;
358 }
359
360 MyApp::~MyApp()
361 {
362 }
363
364 // `Main program' equivalent, creating windows and returning main app frame
365 bool MyApp::OnInit()
366 {
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");
370
371 // Create the main frame window
372 MyFrame *frame = new MyFrame((wxFrame *)NULL, _T("wxWindows threads sample"),
373 50, 50, 450, 340);
374
375 // Make a menubar
376 wxMenuBar *menuBar = new wxMenuBar;
377
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"));
383
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"));
394
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"));
399
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"));
405
406 frame->SetMenuBar(menuBar);
407
408 // Show the frame
409 frame->Show(TRUE);
410
411 SetTopWindow(frame);
412
413 return TRUE;
414 }
415
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))
420 {
421 m_nRunning = m_nCount = 0;
422
423 m_dlgProgress = (wxProgressDialog *)NULL;
424
425 CreateStatusBar(2);
426
427 m_txtctrl = new wxTextCtrl(this, -1, _T(""), wxPoint(0, 0), wxSize(0, 0),
428 wxTE_MULTILINE | wxTE_READONLY);
429
430 }
431
432 MyFrame::~MyFrame()
433 {
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
437
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
441 wxThread *thread;
442
443 wxGetApp().m_critsect.Enter();
444
445 // check if we have any threads running first
446 const wxArrayThread& threads = wxGetApp().m_threads;
447 size_t count = threads.GetCount();
448
449 if ( count )
450 {
451 // set the flag for MyThread::OnExit()
452 wxGetApp().m_waitingUntilAllDone = TRUE;
453
454 // stop all threads
455 while ( ! threads.IsEmpty() )
456 {
457 thread = threads.Last();
458
459 wxGetApp().m_critsect.Leave();
460
461 thread->Delete();
462
463 wxGetApp().m_critsect.Enter();
464 }
465 }
466
467 wxGetApp().m_critsect.Leave();
468
469 if ( count )
470 {
471 // now wait for them to really terminate
472 wxGetApp().m_semAllDone.Wait();
473 }
474 //else: no threads to terminate, no condition to wait for
475 }
476
477 MyThread *MyFrame::CreateThread()
478 {
479 MyThread *thread = new MyThread(this);
480
481 if ( thread->Create() != wxTHREAD_NO_ERROR )
482 {
483 wxLogError(wxT("Can't create thread!"));
484 }
485
486 wxCriticalSectionLocker enter(wxGetApp().m_critsect);
487 wxGetApp().m_threads.Add(thread);
488
489 return thread;
490 }
491
492 void MyFrame::OnStartThreads(wxCommandEvent& WXUNUSED(event) )
493 {
494 static long s_num = 10;
495
496 s_num = wxGetNumberFromUser(_T("How many threads to start: "), _T(""),
497 _T("wxThread sample"), s_num, 1, 10000, this);
498 if ( s_num == -1 )
499 {
500 s_num = 10;
501
502 return;
503 }
504
505 size_t count = (size_t)s_num, n;
506
507 wxArrayThread threads;
508
509 // first create them all...
510 for ( n = 0; n < count; n++ )
511 {
512 wxThread *thr = CreateThread();
513
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
516 // the normal one
517 if ( n == 0 )
518 thr->SetPriority(WXTHREAD_MIN_PRIORITY);
519 else if ( n == 1 )
520 thr->SetPriority(WXTHREAD_MAX_PRIORITY);
521 else
522 thr->SetPriority(WXTHREAD_DEFAULT_PRIORITY);
523
524 threads.Add(thr);
525 }
526
527 wxString msg;
528 msg.Printf(wxT("%d new threads created."), count);
529 SetStatusText(msg, 1);
530
531 // ...and then start them
532 for ( n = 0; n < count; n++ )
533 {
534 threads[n]->Run();
535 }
536 }
537
538 void MyFrame::OnStartThread(wxCommandEvent& WXUNUSED(event) )
539 {
540 MyThread *thread = CreateThread();
541
542 if ( thread->Run() != wxTHREAD_NO_ERROR )
543 {
544 wxLogError(wxT("Can't start thread!"));
545 }
546
547 SetStatusText(_T("New thread started."), 1);
548 }
549
550 void MyFrame::OnStopThread(wxCommandEvent& WXUNUSED(event) )
551 {
552 wxGetApp().m_critsect.Enter();
553
554 // stop the last thread
555 if ( wxGetApp().m_threads.IsEmpty() )
556 {
557 wxLogError(wxT("No thread to stop!"));
558
559 wxGetApp().m_critsect.Leave();
560 }
561 else
562 {
563 wxThread *thread = wxGetApp().m_threads.Last();
564
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();
569
570 thread->Delete();
571
572 SetStatusText(_T("Thread stopped."), 1);
573 }
574 }
575
576 void MyFrame::OnResumeThread(wxCommandEvent& WXUNUSED(event) )
577 {
578 wxCriticalSectionLocker enter(wxGetApp().m_critsect);
579
580 // resume first suspended thread
581 size_t n = 0, count = wxGetApp().m_threads.Count();
582 while ( n < count && !wxGetApp().m_threads[n]->IsPaused() )
583 n++;
584
585 if ( n == count )
586 {
587 wxLogError(wxT("No thread to resume!"));
588 }
589 else
590 {
591 wxGetApp().m_threads[n]->Resume();
592
593 SetStatusText(_T("Thread resumed."), 1);
594 }
595 }
596
597 void MyFrame::OnPauseThread(wxCommandEvent& WXUNUSED(event) )
598 {
599 wxCriticalSectionLocker enter(wxGetApp().m_critsect);
600
601 // pause last running thread
602 int n = wxGetApp().m_threads.Count() - 1;
603 while ( n >= 0 && !wxGetApp().m_threads[n]->IsRunning() )
604 n--;
605
606 if ( n < 0 )
607 {
608 wxLogError(wxT("No thread to pause!"));
609 }
610 else
611 {
612 wxGetApp().m_threads[n]->Pause();
613
614 SetStatusText(_T("Thread paused."), 1);
615 }
616 }
617
618 // set the frame title indicating the current number of threads
619 void MyFrame::OnIdle(wxIdleEvent& event)
620 {
621 wxCriticalSectionLocker enter(wxGetApp().m_critsect);
622
623 // update the counts of running/total threads
624 size_t nRunning = 0,
625 nCount = wxGetApp().m_threads.Count();
626 for ( size_t n = 0; n < nCount; n++ )
627 {
628 if ( wxGetApp().m_threads[n]->IsRunning() )
629 nRunning++;
630 }
631
632 if ( nCount != m_nCount || nRunning != m_nRunning )
633 {
634 m_nRunning = nRunning;
635 m_nCount = nCount;
636
637 wxLogStatus(this, wxT("%u threads total, %u running."), nCount, nRunning);
638 }
639 //else: avoid flicker - don't print anything
640
641 event.Skip();
642 }
643
644 void MyFrame::OnQuit(wxCommandEvent& WXUNUSED(event) )
645 {
646 Close(TRUE);
647 }
648
649 void MyFrame::OnExecMain(wxCommandEvent& WXUNUSED(event))
650 {
651 wxLogMessage(wxT("The exit code from the main program is %ld"),
652 EXEC(_T("/bin/echo \"main program\"")));
653 }
654
655 void MyFrame::OnExecThread(wxCommandEvent& WXUNUSED(event))
656 {
657 MyExecThread thread(wxT("/bin/echo \"child thread\""));
658 thread.Run();
659
660 wxLogMessage(wxT("The exit code from a child thread is %ld"),
661 (long)thread.Wait());
662 }
663
664 void MyFrame::OnShowCPUs(wxCommandEvent& WXUNUSED(event))
665 {
666 wxString msg;
667
668 int nCPUs = wxThread::GetCPUCount();
669 switch ( nCPUs )
670 {
671 case -1:
672 msg = _T("Unknown number of CPUs");
673 break;
674
675 case 0:
676 msg = _T("WARNING: you're running without any CPUs!");
677 break;
678
679 case 1:
680 msg = _T("This system only has one CPU.");
681 break;
682
683 default:
684 msg.Printf(wxT("This system has %d CPUs"), nCPUs);
685 }
686
687 wxLogMessage(msg);
688 }
689
690 void MyFrame::OnAbout(wxCommandEvent& WXUNUSED(event) )
691 {
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);
699
700 dialog.ShowModal();
701 }
702
703 void MyFrame::OnClear(wxCommandEvent& WXUNUSED(event))
704 {
705 m_txtctrl->Clear();
706 }
707
708 void MyFrame::OnUpdateWorker(wxUpdateUIEvent& event)
709 {
710 event.Enable( m_dlgProgress == NULL );
711 }
712
713 void MyFrame::OnStartWorker(wxCommandEvent& WXUNUSED(event))
714 {
715 MyWorkerThread *thread = new MyWorkerThread(this);
716
717 if ( thread->Create() != wxTHREAD_NO_ERROR )
718 {
719 wxLogError(wxT("Can't create thread!"));
720 }
721
722 m_dlgProgress = new wxProgressDialog
723 (
724 _T("Progress dialog"),
725 _T("Wait until the thread terminates or press [Cancel]"),
726 100,
727 this,
728 wxPD_CAN_ABORT |
729 wxPD_APP_MODAL |
730 wxPD_ELAPSED_TIME |
731 wxPD_ESTIMATED_TIME |
732 wxPD_REMAINING_TIME
733 );
734
735 // thread is not running yet, no need for crit sect
736 m_cancelled = FALSE;
737
738 thread->Run();
739 }
740
741 void MyFrame::OnWorkerEvent(wxCommandEvent& event)
742 {
743 #if 0
744 WriteText( _T("Got message from worker thread: ") );
745 WriteText( event.GetString() );
746 WriteText( _T("\n") );
747 #else
748 int n = event.GetInt();
749 if ( n == -1 )
750 {
751 m_dlgProgress->Destroy();
752 m_dlgProgress = (wxProgressDialog *)NULL;
753
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
756 // really closed
757 wxWakeUpIdle();
758 }
759 else
760 {
761 if ( !m_dlgProgress->Update(n) )
762 {
763 wxCriticalSectionLocker lock(m_critsectWork);
764
765 m_cancelled = TRUE;
766 }
767 }
768 #endif
769 }
770
771 bool MyFrame::Cancelled()
772 {
773 wxCriticalSectionLocker lock(m_critsectWork);
774
775 return m_cancelled;
776 }