Document domain parameter of wxTranslations::GetTranslatedString().
[wxWidgets.git] / samples / thread / thread.cpp
1 /////////////////////////////////////////////////////////////////////////////
2 // Name: thread.cpp
3 // Purpose: wxWidgets thread sample
4 // Author: Guilhem Lavaux, Vadim Zeitlin
5 // Modified by:
6 // Created: 06/16/98
7 // Copyright: (c) 1998-2009 wxWidgets team
8 // Licence: wxWindows licence
9 /////////////////////////////////////////////////////////////////////////////
10
11 // ============================================================================
12 // declarations
13 // ============================================================================
14
15 // ----------------------------------------------------------------------------
16 // headers
17 // ----------------------------------------------------------------------------
18
19 // For compilers that support precompilation, includes "wx/wx.h".
20 #include "wx/wxprec.h"
21
22 #ifdef __BORLANDC__
23 #pragma hdrstop
24 #endif
25
26 #ifndef WX_PRECOMP
27 #include "wx/wx.h"
28 #endif
29
30 #if !wxUSE_THREADS
31 #error "This sample requires thread support!"
32 #endif // wxUSE_THREADS
33
34 #include "wx/thread.h"
35 #include "wx/dynarray.h"
36 #include "wx/numdlg.h"
37 #include "wx/progdlg.h"
38
39 // ----------------------------------------------------------------------------
40 // resources
41 // ----------------------------------------------------------------------------
42
43 #include "../sample.xpm"
44
45 // ----------------------------------------------------------------------------
46 // private classes
47 // ----------------------------------------------------------------------------
48
49 // define this to use wxExecute in the exec tests, otherwise just use system
50 #define USE_EXECUTE
51
52 #ifdef USE_EXECUTE
53 #define EXEC(cmd) wxExecute((cmd), wxEXEC_SYNC)
54 #else
55 #define EXEC(cmd) system(cmd)
56 #endif
57
58 class MyThread;
59 WX_DEFINE_ARRAY_PTR(wxThread *, wxArrayThread);
60
61 // ----------------------------------------------------------------------------
62 // the application object
63 // ----------------------------------------------------------------------------
64
65 class MyApp : public wxApp
66 {
67 public:
68 MyApp();
69 virtual ~MyApp(){};
70
71 virtual bool OnInit();
72
73 // critical section protects access to all of the fields below
74 wxCriticalSection m_critsect;
75
76 // all the threads currently alive - as soon as the thread terminates, it's
77 // removed from the array
78 wxArrayThread m_threads;
79
80 // semaphore used to wait for the threads to exit, see MyFrame::OnQuit()
81 wxSemaphore m_semAllDone;
82
83 // indicates that we're shutting down and all threads should exit
84 bool m_shuttingDown;
85 };
86
87 // ----------------------------------------------------------------------------
88 // the main application frame
89 // ----------------------------------------------------------------------------
90
91 class MyFrame : public wxFrame,
92 private wxLog
93 {
94 public:
95 // ctor
96 MyFrame(const wxString& title);
97 virtual ~MyFrame();
98
99 // accessors for MyWorkerThread (called in its context!)
100 bool Cancelled();
101
102 protected:
103 virtual void DoLogRecord(wxLogLevel level,
104 const wxString& msg,
105 const wxLogRecordInfo& info);
106
107 private:
108 // event handlers
109 // --------------
110
111 void OnQuit(wxCommandEvent& event);
112 void OnClear(wxCommandEvent& event);
113
114 void OnStartThread(wxCommandEvent& event);
115 void OnStartThreads(wxCommandEvent& event);
116 void OnStopThread(wxCommandEvent& event);
117 void OnPauseThread(wxCommandEvent& event);
118 void OnResumeThread(wxCommandEvent& event);
119
120 void OnStartWorker(wxCommandEvent& event);
121 void OnExecMain(wxCommandEvent& event);
122 void OnStartGUIThread(wxCommandEvent& event);
123
124 void OnShowCPUs(wxCommandEvent& event);
125 void OnAbout(wxCommandEvent& event);
126
127 void OnIdle(wxIdleEvent &event);
128 void OnWorkerEvent(wxThreadEvent& event);
129 void OnUpdateWorker(wxUpdateUIEvent& event);
130
131
132 // logging helper
133 void DoLogLine(wxTextCtrl *text,
134 const wxString& timestr,
135 const wxString& threadstr,
136 const wxString& msg);
137
138
139 // thread helper functions
140 // -----------------------
141
142 // helper function - creates a new thread (but doesn't run it)
143 MyThread *CreateThread();
144
145 // update display in our status bar: called during idle handling
146 void UpdateThreadStatus();
147
148
149 // internal variables
150 // ------------------
151
152 // just some place to put our messages in
153 wxTextCtrl *m_txtctrl;
154
155 // old log target, we replace it with one using m_txtctrl during this
156 // frame life time
157 wxLog *m_oldLogger;
158
159 // the array of pending messages to be displayed and the critical section
160 // protecting it
161 wxArrayString m_messages;
162 wxCriticalSection m_csMessages;
163
164 // remember the number of running threads and total number of threads
165 size_t m_nRunning,
166 m_nCount;
167
168 // the progress dialog which we show while worker thread is running
169 wxProgressDialog *m_dlgProgress;
170
171 // was the worker thread cancelled by user?
172 bool m_cancelled;
173 wxCriticalSection m_csCancelled; // protects m_cancelled
174
175 DECLARE_EVENT_TABLE()
176 };
177
178 // ----------------------------------------------------------------------------
179 // constants
180 // ----------------------------------------------------------------------------
181
182 // ID for the menu commands
183 enum
184 {
185 THREAD_QUIT = wxID_EXIT,
186 THREAD_ABOUT = wxID_ABOUT,
187 THREAD_TEXT = 101,
188 THREAD_CLEAR,
189 THREAD_START_THREAD = 201,
190 THREAD_START_THREADS,
191 THREAD_STOP_THREAD,
192 THREAD_PAUSE_THREAD,
193 THREAD_RESUME_THREAD,
194
195 THREAD_START_WORKER,
196 THREAD_EXEC_MAIN,
197 THREAD_START_GUI_THREAD,
198
199 THREAD_SHOWCPUS,
200
201 WORKER_EVENT = wxID_HIGHEST+1, // this one gets sent from MyWorkerThread
202 GUITHREAD_EVENT // this one gets sent from MyGUIThread
203 };
204
205 // ----------------------------------------------------------------------------
206 // a simple thread
207 // ----------------------------------------------------------------------------
208
209 class MyThread : public wxThread
210 {
211 public:
212 MyThread();
213 virtual ~MyThread();
214
215 // thread execution starts here
216 virtual void *Entry();
217
218 public:
219 unsigned m_count;
220 };
221
222 // ----------------------------------------------------------------------------
223 // a worker thread
224 // ----------------------------------------------------------------------------
225
226 class MyWorkerThread : public wxThread
227 {
228 public:
229 MyWorkerThread(MyFrame *frame);
230
231 // thread execution starts here
232 virtual void *Entry();
233
234 // called when the thread exits - whether it terminates normally or is
235 // stopped with Delete() (but not when it is Kill()ed!)
236 virtual void OnExit();
237
238 public:
239 MyFrame *m_frame;
240 unsigned m_count;
241 };
242
243 // ----------------------------------------------------------------------------
244 // a thread which executes GUI calls using wxMutexGuiEnter/Leave
245 // ----------------------------------------------------------------------------
246
247 #define GUITHREAD_BMP_SIZE 300
248 #define GUITHREAD_NUM_UPDATES 50
249 class MyImageDialog;
250
251 class MyGUIThread : public wxThread
252 {
253 public:
254 MyGUIThread(MyImageDialog *dlg) : wxThread(wxTHREAD_JOINABLE)
255 {
256 m_dlg = dlg;
257 }
258
259 virtual ExitCode Entry();
260
261 private:
262 MyImageDialog *m_dlg;
263 };
264
265 // ----------------------------------------------------------------------------
266 // an helper dialog used by MyFrame::OnStartGUIThread
267 // ----------------------------------------------------------------------------
268
269 class MyImageDialog: public wxDialog
270 {
271 public:
272 // ctor
273 MyImageDialog(wxFrame *frame);
274 ~MyImageDialog();
275
276 // stuff used by MyGUIThread:
277 wxBitmap m_bmp; // the bitmap drawn by MyGUIThread
278 wxCriticalSection m_csBmp; // protects m_bmp
279
280 private:
281 void OnGUIThreadEvent(wxThreadEvent& event);
282 void OnPaint(wxPaintEvent&);
283
284 MyGUIThread m_thread;
285 int m_nCurrentProgress;
286
287 DECLARE_EVENT_TABLE()
288 };
289
290 // ============================================================================
291 // implementation
292 // ============================================================================
293
294 // ----------------------------------------------------------------------------
295 // the application class
296 // ----------------------------------------------------------------------------
297
298 // Create a new application object
299 IMPLEMENT_APP(MyApp)
300
301 MyApp::MyApp()
302 {
303 m_shuttingDown = false;
304 }
305
306 // `Main program' equivalent, creating windows and returning main app frame
307 bool MyApp::OnInit()
308 {
309 if ( !wxApp::OnInit() )
310 return false;
311
312 // uncomment this to get some debugging messages from the trace code
313 // on the console (or just set WXTRACE env variable to include "thread")
314 wxLog::AddTraceMask("thread");
315
316 // Create the main frame window
317 new MyFrame("wxWidgets threads sample");
318
319 return true;
320 }
321
322 // ----------------------------------------------------------------------------
323 // MyFrame
324 // ----------------------------------------------------------------------------
325
326 BEGIN_EVENT_TABLE(MyFrame, wxFrame)
327 EVT_MENU(THREAD_QUIT, MyFrame::OnQuit)
328 EVT_MENU(THREAD_CLEAR, MyFrame::OnClear)
329 EVT_MENU(THREAD_START_THREAD, MyFrame::OnStartThread)
330 EVT_MENU(THREAD_START_THREADS, MyFrame::OnStartThreads)
331 EVT_MENU(THREAD_STOP_THREAD, MyFrame::OnStopThread)
332 EVT_MENU(THREAD_PAUSE_THREAD, MyFrame::OnPauseThread)
333 EVT_MENU(THREAD_RESUME_THREAD, MyFrame::OnResumeThread)
334
335 EVT_MENU(THREAD_START_WORKER, MyFrame::OnStartWorker)
336 EVT_MENU(THREAD_EXEC_MAIN, MyFrame::OnExecMain)
337 EVT_MENU(THREAD_START_GUI_THREAD, MyFrame::OnStartGUIThread)
338
339 EVT_MENU(THREAD_SHOWCPUS, MyFrame::OnShowCPUs)
340 EVT_MENU(THREAD_ABOUT, MyFrame::OnAbout)
341
342 EVT_UPDATE_UI(THREAD_START_WORKER, MyFrame::OnUpdateWorker)
343 EVT_THREAD(WORKER_EVENT, MyFrame::OnWorkerEvent)
344 EVT_IDLE(MyFrame::OnIdle)
345 END_EVENT_TABLE()
346
347 // My frame constructor
348 MyFrame::MyFrame(const wxString& title)
349 : wxFrame(NULL, wxID_ANY, title)
350 {
351 m_oldLogger = wxLog::GetActiveTarget();
352
353 SetIcon(wxICON(sample));
354
355 // Make a menubar
356 wxMenuBar *menuBar = new wxMenuBar;
357
358 wxMenu *menuFile = new wxMenu;
359 menuFile->Append(THREAD_CLEAR, wxT("&Clear log\tCtrl-L"));
360 menuFile->AppendSeparator();
361 menuFile->Append(THREAD_QUIT, wxT("E&xit\tAlt-X"));
362 menuBar->Append(menuFile, wxT("&File"));
363
364 wxMenu *menuThread = new wxMenu;
365 menuThread->Append(THREAD_START_THREAD, wxT("&Start a new thread\tCtrl-N"));
366 menuThread->Append(THREAD_START_THREADS, wxT("Start &many threads at once"));
367 menuThread->Append(THREAD_STOP_THREAD, wxT("S&top the last spawned thread\tCtrl-S"));
368 menuThread->AppendSeparator();
369 menuThread->Append(THREAD_PAUSE_THREAD, wxT("&Pause the last spawned running thread\tCtrl-P"));
370 menuThread->Append(THREAD_RESUME_THREAD, wxT("&Resume the first suspended thread\tCtrl-R"));
371 menuThread->AppendSeparator();
372 menuThread->Append(THREAD_START_WORKER, wxT("Start a &worker thread\tCtrl-W"));
373 menuThread->Append(THREAD_EXEC_MAIN, wxT("&Launch a program from main thread\tF5"));
374 menuThread->Append(THREAD_START_GUI_THREAD, wxT("Launch a &GUI thread\tF6"));
375 menuBar->Append(menuThread, wxT("&Thread"));
376
377 wxMenu *menuHelp = new wxMenu;
378 menuHelp->Append(THREAD_SHOWCPUS, wxT("&Show CPU count"));
379 menuHelp->AppendSeparator();
380 menuHelp->Append(THREAD_ABOUT, wxT("&About"));
381 menuBar->Append(menuHelp, wxT("&Help"));
382
383 SetMenuBar(menuBar);
384
385 m_nRunning = m_nCount = 0;
386
387 m_dlgProgress = NULL;
388
389 #if wxUSE_STATUSBAR
390 CreateStatusBar(2);
391 #endif // wxUSE_STATUSBAR
392
393 // create the logging text control and a header showing the meaning of the
394 // different columns
395 wxTextCtrl *header = new wxTextCtrl(this, wxID_ANY, "",
396 wxDefaultPosition, wxDefaultSize,
397 wxTE_READONLY);
398 DoLogLine(header, " Time", " Thread", "Message");
399 m_txtctrl = new wxTextCtrl(this, wxID_ANY, "",
400 wxDefaultPosition, wxDefaultSize,
401 wxTE_MULTILINE | wxTE_READONLY);
402 wxLog::SetActiveTarget(this);
403
404 // use fixed width font to align output in nice columns
405 wxFont font(wxNORMAL_FONT->GetPointSize(), wxFONTFAMILY_TELETYPE,
406 wxFONTSTYLE_NORMAL, wxFONTWEIGHT_NORMAL);
407 header->SetFont(font);
408 m_txtctrl->SetFont(font);
409
410 m_txtctrl->SetFocus();
411
412 // layout and show the frame
413 wxBoxSizer *sizer = new wxBoxSizer(wxVERTICAL);
414 sizer->Add(header, wxSizerFlags().Expand());
415 sizer->Add(m_txtctrl, wxSizerFlags(1).Expand());
416 SetSizer(sizer);
417
418 SetSize(600, 350);
419 Show();
420 }
421
422 MyFrame::~MyFrame()
423 {
424 wxLog::SetActiveTarget(m_oldLogger);
425
426 // NB: although the OS will terminate all the threads anyhow when the main
427 // one exits, it's good practice to do it ourselves -- even if it's not
428 // completely trivial in this example
429
430 // tell all the threads to terminate: note that they can't terminate while
431 // we're deleting them because they will block in their OnExit() -- this is
432 // important as otherwise we might access invalid array elements
433
434 {
435 wxCriticalSectionLocker locker(wxGetApp().m_critsect);
436
437 // check if we have any threads running first
438 const wxArrayThread& threads = wxGetApp().m_threads;
439 size_t count = threads.GetCount();
440
441 if ( !count )
442 return;
443
444 // set the flag indicating that all threads should exit
445 wxGetApp().m_shuttingDown = true;
446 }
447
448 // now wait for them to really terminate
449 wxGetApp().m_semAllDone.Wait();
450 }
451
452 void
453 MyFrame::DoLogLine(wxTextCtrl *text,
454 const wxString& timestr,
455 const wxString& threadstr,
456 const wxString& msg)
457 {
458 text->AppendText(wxString::Format("%9s %10s %s", timestr, threadstr, msg));
459 }
460
461 void
462 MyFrame::DoLogRecord(wxLogLevel level,
463 const wxString& msg,
464 const wxLogRecordInfo& info)
465 {
466 // let the default GUI logger treat warnings and errors as they should be
467 // more noticeable than just another line in the log window and also trace
468 // messages as there may be too many of them
469 if ( level <= wxLOG_Warning || level == wxLOG_Trace )
470 {
471 m_oldLogger->LogRecord(level, msg, info);
472 return;
473 }
474
475 DoLogLine
476 (
477 m_txtctrl,
478 wxDateTime(info.timestamp).FormatISOTime(),
479 info.threadId == wxThread::GetMainId()
480 ? wxString("main")
481 : wxString::Format("%lx", info.threadId),
482 msg + "\n"
483 );
484 }
485
486 MyThread *MyFrame::CreateThread()
487 {
488 MyThread *thread = new MyThread;
489
490 if ( thread->Create() != wxTHREAD_NO_ERROR )
491 {
492 wxLogError(wxT("Can't create thread!"));
493 }
494
495 wxCriticalSectionLocker enter(wxGetApp().m_critsect);
496 wxGetApp().m_threads.Add(thread);
497
498 return thread;
499 }
500
501 void MyFrame::UpdateThreadStatus()
502 {
503 wxCriticalSectionLocker enter(wxGetApp().m_critsect);
504
505 // update the counts of running/total threads
506 size_t nRunning = 0,
507 nCount = wxGetApp().m_threads.Count();
508 for ( size_t n = 0; n < nCount; n++ )
509 {
510 if ( wxGetApp().m_threads[n]->IsRunning() )
511 nRunning++;
512 }
513
514 if ( nCount != m_nCount || nRunning != m_nRunning )
515 {
516 m_nRunning = nRunning;
517 m_nCount = nCount;
518
519 wxLogStatus(this, wxT("%u threads total, %u running."), unsigned(nCount), unsigned(nRunning));
520 }
521 //else: avoid flicker - don't print anything
522 }
523
524 bool MyFrame::Cancelled()
525 {
526 wxCriticalSectionLocker lock(m_csCancelled);
527
528 return m_cancelled;
529 }
530
531 // ----------------------------------------------------------------------------
532 // MyFrame - event handlers
533 // ----------------------------------------------------------------------------
534
535 void MyFrame::OnStartThreads(wxCommandEvent& WXUNUSED(event) )
536 {
537 static long s_num;
538
539 s_num = wxGetNumberFromUser(wxT("How many threads to start: "), wxT(""),
540 wxT("wxThread sample"), s_num, 1, 10000, this);
541 if ( s_num == -1 )
542 {
543 s_num = 10;
544
545 return;
546 }
547
548 unsigned count = unsigned(s_num), n;
549
550 wxArrayThread threads;
551
552 // first create them all...
553 for ( n = 0; n < count; n++ )
554 {
555 wxThread *thr = CreateThread();
556
557 // we want to show the effect of SetPriority(): the first thread will
558 // have the lowest priority, the second - the highest, all the rest
559 // the normal one
560 if ( n == 0 )
561 thr->SetPriority(wxPRIORITY_MIN);
562 else if ( n == 1 )
563 thr->SetPriority(wxPRIORITY_MAX);
564 else
565 thr->SetPriority(wxPRIORITY_DEFAULT);
566
567 threads.Add(thr);
568 }
569
570 #if wxUSE_STATUSBAR
571 wxString msg;
572 msg.Printf(wxT("%d new threads created."), count);
573 SetStatusText(msg, 1);
574 #endif // wxUSE_STATUSBAR
575
576 // ...and then start them
577 for ( n = 0; n < count; n++ )
578 {
579 threads[n]->Run();
580 }
581 }
582
583 void MyFrame::OnStartThread(wxCommandEvent& WXUNUSED(event) )
584 {
585 MyThread *thread = CreateThread();
586
587 if ( thread->Run() != wxTHREAD_NO_ERROR )
588 {
589 wxLogError(wxT("Can't start thread!"));
590 }
591
592 #if wxUSE_STATUSBAR
593 SetStatusText(wxT("New thread started."), 1);
594 #endif // wxUSE_STATUSBAR
595 }
596
597 void MyFrame::OnStopThread(wxCommandEvent& WXUNUSED(event) )
598 {
599 wxThread* toDelete = NULL;
600 {
601 wxCriticalSectionLocker enter(wxGetApp().m_critsect);
602
603 // stop the last thread
604 if ( wxGetApp().m_threads.IsEmpty() )
605 {
606 wxLogError(wxT("No thread to stop!"));
607 }
608 else
609 {
610 toDelete = wxGetApp().m_threads.Last();
611 }
612 }
613
614 if ( toDelete )
615 {
616 // This can still crash if the thread gets to delete itself
617 // in the mean time.
618 toDelete->Delete();
619
620 #if wxUSE_STATUSBAR
621 SetStatusText(wxT("Last thread stopped."), 1);
622 #endif // wxUSE_STATUSBAR
623 }
624 }
625
626 void MyFrame::OnResumeThread(wxCommandEvent& WXUNUSED(event) )
627 {
628 wxCriticalSectionLocker enter(wxGetApp().m_critsect);
629
630 // resume first suspended thread
631 size_t n = 0, count = wxGetApp().m_threads.Count();
632 while ( n < count && !wxGetApp().m_threads[n]->IsPaused() )
633 n++;
634
635 if ( n == count )
636 {
637 wxLogError(wxT("No thread to resume!"));
638 }
639 else
640 {
641 wxGetApp().m_threads[n]->Resume();
642
643 #if wxUSE_STATUSBAR
644 SetStatusText(wxT("Thread resumed."), 1);
645 #endif // wxUSE_STATUSBAR
646 }
647 }
648
649 void MyFrame::OnPauseThread(wxCommandEvent& WXUNUSED(event) )
650 {
651 wxCriticalSectionLocker enter(wxGetApp().m_critsect);
652
653 // pause last running thread
654 int n = wxGetApp().m_threads.Count() - 1;
655 while ( n >= 0 && !wxGetApp().m_threads[n]->IsRunning() )
656 n--;
657
658 if ( n < 0 )
659 {
660 wxLogError(wxT("No thread to pause!"));
661 }
662 else
663 {
664 wxGetApp().m_threads[n]->Pause();
665
666 #if wxUSE_STATUSBAR
667 SetStatusText(wxT("Thread paused."), 1);
668 #endif // wxUSE_STATUSBAR
669 }
670 }
671
672 void MyFrame::OnIdle(wxIdleEvent& event)
673 {
674 UpdateThreadStatus();
675
676 event.Skip();
677 }
678
679 void MyFrame::OnQuit(wxCommandEvent& WXUNUSED(event) )
680 {
681 Close(true);
682 }
683
684 void MyFrame::OnExecMain(wxCommandEvent& WXUNUSED(event))
685 {
686 wxString cmd = wxGetTextFromUser("Please enter the command to execute",
687 "Enter command",
688 #ifdef __WXMSW__
689 "notepad",
690 #else
691 "/bin/echo \"Message from another process\"",
692 #endif
693 this);
694 if (cmd.IsEmpty())
695 return; // user clicked cancel
696
697 wxLogMessage(wxT("The exit code from the main program is %ld"),
698 EXEC(cmd));
699 }
700
701 void MyFrame::OnShowCPUs(wxCommandEvent& WXUNUSED(event))
702 {
703 wxString msg;
704
705 int nCPUs = wxThread::GetCPUCount();
706 switch ( nCPUs )
707 {
708 case -1:
709 msg = wxT("Unknown number of CPUs");
710 break;
711
712 case 0:
713 msg = wxT("WARNING: you're running without any CPUs!");
714 break;
715
716 case 1:
717 msg = wxT("This system only has one CPU.");
718 break;
719
720 default:
721 msg.Printf(wxT("This system has %d CPUs"), nCPUs);
722 }
723
724 wxLogMessage(msg);
725 }
726
727 void MyFrame::OnAbout(wxCommandEvent& WXUNUSED(event) )
728 {
729 wxMessageDialog dialog(this,
730 wxT("wxWidgets multithreaded application sample\n")
731 wxT("(c) 1998 Julian Smart, Guilhem Lavaux\n")
732 wxT("(c) 2000 Robert Roebling\n")
733 wxT("(c) 1999,2009 Vadim Zeitlin"),
734 wxT("About wxThread sample"),
735 wxOK | wxICON_INFORMATION);
736
737 dialog.ShowModal();
738 }
739
740 void MyFrame::OnClear(wxCommandEvent& WXUNUSED(event))
741 {
742 m_txtctrl->Clear();
743 }
744
745 void MyFrame::OnUpdateWorker(wxUpdateUIEvent& event)
746 {
747 event.Enable( m_dlgProgress == NULL );
748 }
749
750 void MyFrame::OnStartWorker(wxCommandEvent& WXUNUSED(event))
751 {
752 MyWorkerThread *thread = new MyWorkerThread(this);
753
754 if ( thread->Create() != wxTHREAD_NO_ERROR )
755 {
756 wxLogError(wxT("Can't create thread!"));
757 return;
758 }
759
760 m_dlgProgress = new wxProgressDialog
761 (
762 wxT("Progress dialog"),
763 wxT("Wait until the thread terminates or press [Cancel]"),
764 100,
765 this,
766 wxPD_CAN_ABORT |
767 wxPD_APP_MODAL |
768 wxPD_ELAPSED_TIME |
769 wxPD_ESTIMATED_TIME |
770 wxPD_REMAINING_TIME
771 );
772
773 // thread is not running yet, no need for crit sect
774 m_cancelled = false;
775
776 thread->Run();
777 }
778
779 void MyFrame::OnWorkerEvent(wxThreadEvent& event)
780 {
781 int n = event.GetInt();
782 if ( n == -1 )
783 {
784 m_dlgProgress->Destroy();
785 m_dlgProgress = (wxProgressDialog *)NULL;
786
787 // the dialog is aborted because the event came from another thread, so
788 // we may need to wake up the main event loop for the dialog to be
789 // really closed
790 wxWakeUpIdle();
791 }
792 else
793 {
794 if ( !m_dlgProgress->Update(n) )
795 {
796 wxCriticalSectionLocker lock(m_csCancelled);
797
798 m_cancelled = true;
799 }
800 }
801 }
802
803 void MyFrame::OnStartGUIThread(wxCommandEvent& WXUNUSED(event))
804 {
805 // we use this to check that disabling logging only affects the main thread
806 // but the messages from the worker thread will still be logged
807 wxLogNull noLog;
808 wxLogMessage("You shouldn't see this message because of wxLogNull");
809
810 MyImageDialog dlg(this);
811
812 dlg.ShowModal();
813 }
814
815
816 // ----------------------------------------------------------------------------
817 // MyImageDialog
818 // ----------------------------------------------------------------------------
819
820 BEGIN_EVENT_TABLE(MyImageDialog, wxDialog)
821 EVT_THREAD(GUITHREAD_EVENT, MyImageDialog::OnGUIThreadEvent)
822 EVT_PAINT(MyImageDialog::OnPaint)
823 END_EVENT_TABLE()
824
825 MyImageDialog::MyImageDialog(wxFrame *parent)
826 : wxDialog(parent, wxID_ANY, "Image created by a secondary thread",
827 wxDefaultPosition, wxSize(GUITHREAD_BMP_SIZE,GUITHREAD_BMP_SIZE)*1.5, wxDEFAULT_DIALOG_STYLE),
828 m_thread(this)
829 {
830 m_nCurrentProgress = 0;
831
832 CentreOnScreen();
833
834 // NOTE: no need to lock m_csBmp until the thread isn't started:
835
836 // create the bitmap
837 if (!m_bmp.Create(GUITHREAD_BMP_SIZE,GUITHREAD_BMP_SIZE) || !m_bmp.IsOk())
838 {
839 wxLogError("Couldn't create the bitmap!");
840 return;
841 }
842
843 // clean it
844 wxMemoryDC dc(m_bmp);
845 dc.SetBackground(*wxBLACK_BRUSH);
846 dc.Clear();
847
848 // draw the bitmap from a secondary thread
849 if ( m_thread.Create() != wxTHREAD_NO_ERROR ||
850 m_thread.Run() != wxTHREAD_NO_ERROR )
851 {
852 wxLogError(wxT("Can't create/run thread!"));
853 return;
854 }
855 }
856
857 MyImageDialog::~MyImageDialog()
858 {
859 // in case our thread is still running and for some reason we are destroyed,
860 // do wait for the thread to complete as it assumes that its MyImageDialog
861 // pointer is always valid
862 m_thread.Delete();
863 }
864
865 void MyImageDialog::OnGUIThreadEvent(wxThreadEvent& event)
866 {
867 m_nCurrentProgress = int(((float)event.GetInt()*100)/GUITHREAD_NUM_UPDATES);
868
869 Refresh();
870 }
871
872 void MyImageDialog::OnPaint(wxPaintEvent& WXUNUSED(evt))
873 {
874 wxPaintDC dc(this);
875
876 const wxSize& sz = dc.GetSize();
877
878 {
879 // paint the bitmap
880 wxCriticalSectionLocker locker(m_csBmp);
881 dc.DrawBitmap(m_bmp, (sz.GetWidth()-GUITHREAD_BMP_SIZE)/2,
882 (sz.GetHeight()-GUITHREAD_BMP_SIZE)/2);
883 }
884
885 // paint a sort of progress bar with a 10px border:
886 dc.SetBrush(*wxRED_BRUSH);
887 dc.DrawRectangle(10,10, m_nCurrentProgress*(sz.GetWidth()-20)/100,30);
888 dc.SetTextForeground(*wxBLUE);
889 dc.DrawText(wxString::Format("%d%%", m_nCurrentProgress),
890 (sz.GetWidth()-dc.GetCharWidth()*2)/2,
891 25-dc.GetCharHeight()/2);
892 }
893
894 // ----------------------------------------------------------------------------
895 // MyThread
896 // ----------------------------------------------------------------------------
897
898 MyThread::MyThread()
899 : wxThread()
900 {
901 m_count = 0;
902 }
903
904 MyThread::~MyThread()
905 {
906 wxCriticalSectionLocker locker(wxGetApp().m_critsect);
907
908 wxArrayThread& threads = wxGetApp().m_threads;
909 threads.Remove(this);
910
911 if ( threads.IsEmpty() )
912 {
913 // signal the main thread that there are no more threads left if it is
914 // waiting for us
915 if ( wxGetApp().m_shuttingDown )
916 {
917 wxGetApp().m_shuttingDown = false;
918
919 wxGetApp().m_semAllDone.Post();
920 }
921 }
922 }
923
924 wxThread::ExitCode MyThread::Entry()
925 {
926 wxLogMessage("Thread started (priority = %u).", GetPriority());
927
928 for ( m_count = 0; m_count < 10; m_count++ )
929 {
930 // check if the application is shutting down: in this case all threads
931 // should stop a.s.a.p.
932 {
933 wxCriticalSectionLocker locker(wxGetApp().m_critsect);
934 if ( wxGetApp().m_shuttingDown )
935 return NULL;
936 }
937
938 // check if just this thread was asked to exit
939 if ( TestDestroy() )
940 break;
941
942 wxLogMessage("Thread progress: %u", m_count);
943
944 // wxSleep() can't be called from non-GUI thread!
945 wxThread::Sleep(1000);
946 }
947
948 wxLogMessage("Thread finished.");
949
950 return NULL;
951 }
952
953
954 // ----------------------------------------------------------------------------
955 // MyWorkerThread
956 // ----------------------------------------------------------------------------
957
958 // define this symbol to 1 to test if the YieldFor() call in the wxProgressDialog::Update
959 // function provokes a race condition in which the second wxThreadEvent posted by
960 // MyWorkerThread::Entry is processed by the YieldFor() call of wxProgressDialog::Update
961 // and results in the destruction of the progress dialog itself, resulting in a crash later.
962 #define TEST_YIELD_RACE_CONDITION 0
963
964 MyWorkerThread::MyWorkerThread(MyFrame *frame)
965 : wxThread()
966 {
967 m_frame = frame;
968 m_count = 0;
969 }
970
971 void MyWorkerThread::OnExit()
972 {
973 }
974
975 wxThread::ExitCode MyWorkerThread::Entry()
976 {
977 #if TEST_YIELD_RACE_CONDITION
978 if ( TestDestroy() )
979 return NULL;
980
981 wxThreadEvent event( wxEVT_THREAD, WORKER_EVENT );
982
983 event.SetInt( 50 );
984 wxQueueEvent( m_frame, event.Clone() );
985
986 event.SetInt(-1);
987 wxQueueEvent( m_frame, event.Clone() );
988 #else
989 for ( m_count = 0; !m_frame->Cancelled() && (m_count < 100); m_count++ )
990 {
991 // check if we were asked to exit
992 if ( TestDestroy() )
993 break;
994
995 // create any type of command event here
996 wxThreadEvent event( wxEVT_THREAD, WORKER_EVENT );
997 event.SetInt( m_count );
998
999 // send in a thread-safe way
1000 wxQueueEvent( m_frame, event.Clone() );
1001
1002 wxMilliSleep(200);
1003 }
1004
1005 wxThreadEvent event( wxEVT_THREAD, WORKER_EVENT );
1006 event.SetInt(-1); // that's all
1007 wxQueueEvent( m_frame, event.Clone() );
1008 #endif
1009
1010 return NULL;
1011 }
1012
1013
1014 // ----------------------------------------------------------------------------
1015 // MyGUIThread
1016 // ----------------------------------------------------------------------------
1017
1018 wxThread::ExitCode MyGUIThread::Entry()
1019 {
1020 // uncomment this to check that disabling logging here does disable it for
1021 // this thread -- but not the main one if you also comment out wxLogNull
1022 // line in MyFrame::OnStartGUIThread()
1023 //wxLogNull noLog;
1024
1025 // this goes to the main window
1026 wxLogMessage("GUI thread starting");
1027
1028 // use a thread-specific log target for this thread to show that its
1029 // messages don't appear in the main window while it runs
1030 wxLogBuffer logBuf;
1031 wxLog::SetThreadActiveTarget(&logBuf);
1032
1033 for (int i=0; i<GUITHREAD_NUM_UPDATES && !TestDestroy(); i++)
1034 {
1035 // inform the GUI toolkit that we're going to use GUI functions
1036 // from a secondary thread:
1037 wxMutexGuiEnter();
1038
1039 {
1040 wxCriticalSectionLocker lock(m_dlg->m_csBmp);
1041
1042 // draw some more stuff on the bitmap
1043 wxMemoryDC dc(m_dlg->m_bmp);
1044 dc.SetBrush((i%2)==0 ? *wxBLUE_BRUSH : *wxGREEN_BRUSH);
1045 dc.DrawRectangle(rand()%GUITHREAD_BMP_SIZE, rand()%GUITHREAD_BMP_SIZE, 30, 30);
1046
1047 // simulate long drawing time:
1048 wxMilliSleep(200);
1049 }
1050
1051 // if we don't release the GUI mutex the MyImageDialog won't be able to refresh
1052 wxMutexGuiLeave();
1053
1054 // notify the dialog that another piece of our masterpiece is complete:
1055 wxThreadEvent event( wxEVT_THREAD, GUITHREAD_EVENT );
1056 event.SetInt(i+1);
1057 wxQueueEvent( m_dlg, event.Clone() );
1058
1059 if ( !((i + 1) % 10) )
1060 {
1061 // this message will go to the buffer
1062 wxLogMessage("Step #%d.", i + 1);
1063 }
1064
1065 // give the main thread the time to refresh before we lock the GUI mutex again
1066 // FIXME: find a better way to do this!
1067 wxMilliSleep(100);
1068 }
1069
1070 // now remove the thread-specific thread target
1071 wxLog::SetThreadActiveTarget(NULL);
1072
1073 // so that this goes to the main window again
1074 wxLogMessage("GUI thread finished.");
1075
1076 return (ExitCode)0;
1077 }