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