]> git.saurik.com Git - wxWidgets.git/blob - samples/thread/thread.cpp
Extract wxFDIOEventLoopSourceHandler in its own header.
[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(wxPRIORITY_MIN);
563 else if ( n == 1 )
564 thr->SetPriority(wxPRIORITY_MAX);
565 else
566 thr->SetPriority(wxPRIORITY_DEFAULT);
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 wxThread* toDelete = NULL;
601 {
602 wxCriticalSectionLocker enter(wxGetApp().m_critsect);
603
604 // stop the last thread
605 if ( wxGetApp().m_threads.IsEmpty() )
606 {
607 wxLogError(wxT("No thread to stop!"));
608 }
609 else
610 {
611 toDelete = wxGetApp().m_threads.Last();
612 }
613 }
614
615 if ( toDelete )
616 {
617 // This can still crash if the thread gets to delete itself
618 // in the mean time.
619 toDelete->Delete();
620
621 #if wxUSE_STATUSBAR
622 SetStatusText(wxT("Last thread stopped."), 1);
623 #endif // wxUSE_STATUSBAR
624 }
625 }
626
627 void MyFrame::OnResumeThread(wxCommandEvent& WXUNUSED(event) )
628 {
629 wxCriticalSectionLocker enter(wxGetApp().m_critsect);
630
631 // resume first suspended thread
632 size_t n = 0, count = wxGetApp().m_threads.Count();
633 while ( n < count && !wxGetApp().m_threads[n]->IsPaused() )
634 n++;
635
636 if ( n == count )
637 {
638 wxLogError(wxT("No thread to resume!"));
639 }
640 else
641 {
642 wxGetApp().m_threads[n]->Resume();
643
644 #if wxUSE_STATUSBAR
645 SetStatusText(wxT("Thread resumed."), 1);
646 #endif // wxUSE_STATUSBAR
647 }
648 }
649
650 void MyFrame::OnPauseThread(wxCommandEvent& WXUNUSED(event) )
651 {
652 wxCriticalSectionLocker enter(wxGetApp().m_critsect);
653
654 // pause last running thread
655 int n = wxGetApp().m_threads.Count() - 1;
656 while ( n >= 0 && !wxGetApp().m_threads[n]->IsRunning() )
657 n--;
658
659 if ( n < 0 )
660 {
661 wxLogError(wxT("No thread to pause!"));
662 }
663 else
664 {
665 wxGetApp().m_threads[n]->Pause();
666
667 #if wxUSE_STATUSBAR
668 SetStatusText(wxT("Thread paused."), 1);
669 #endif // wxUSE_STATUSBAR
670 }
671 }
672
673 void MyFrame::OnIdle(wxIdleEvent& event)
674 {
675 UpdateThreadStatus();
676
677 event.Skip();
678 }
679
680 void MyFrame::OnQuit(wxCommandEvent& WXUNUSED(event) )
681 {
682 Close(true);
683 }
684
685 void MyFrame::OnExecMain(wxCommandEvent& WXUNUSED(event))
686 {
687 wxString cmd = wxGetTextFromUser("Please enter the command to execute",
688 "Enter command",
689 #ifdef __WXMSW__
690 "notepad",
691 #else
692 "/bin/echo \"Message from another process\"",
693 #endif
694 this);
695 if (cmd.IsEmpty())
696 return; // user clicked cancel
697
698 wxLogMessage(wxT("The exit code from the main program is %ld"),
699 EXEC(cmd));
700 }
701
702 void MyFrame::OnShowCPUs(wxCommandEvent& WXUNUSED(event))
703 {
704 wxString msg;
705
706 int nCPUs = wxThread::GetCPUCount();
707 switch ( nCPUs )
708 {
709 case -1:
710 msg = wxT("Unknown number of CPUs");
711 break;
712
713 case 0:
714 msg = wxT("WARNING: you're running without any CPUs!");
715 break;
716
717 case 1:
718 msg = wxT("This system only has one CPU.");
719 break;
720
721 default:
722 msg.Printf(wxT("This system has %d CPUs"), nCPUs);
723 }
724
725 wxLogMessage(msg);
726 }
727
728 void MyFrame::OnAbout(wxCommandEvent& WXUNUSED(event) )
729 {
730 wxMessageDialog dialog(this,
731 wxT("wxWidgets multithreaded application sample\n")
732 wxT("(c) 1998 Julian Smart, Guilhem Lavaux\n")
733 wxT("(c) 2000 Robert Roebling\n")
734 wxT("(c) 1999,2009 Vadim Zeitlin"),
735 wxT("About wxThread sample"),
736 wxOK | wxICON_INFORMATION);
737
738 dialog.ShowModal();
739 }
740
741 void MyFrame::OnClear(wxCommandEvent& WXUNUSED(event))
742 {
743 m_txtctrl->Clear();
744 }
745
746 void MyFrame::OnUpdateWorker(wxUpdateUIEvent& event)
747 {
748 event.Enable( m_dlgProgress == NULL );
749 }
750
751 void MyFrame::OnStartWorker(wxCommandEvent& WXUNUSED(event))
752 {
753 MyWorkerThread *thread = new MyWorkerThread(this);
754
755 if ( thread->Create() != wxTHREAD_NO_ERROR )
756 {
757 wxLogError(wxT("Can't create thread!"));
758 return;
759 }
760
761 m_dlgProgress = new wxProgressDialog
762 (
763 wxT("Progress dialog"),
764 wxT("Wait until the thread terminates or press [Cancel]"),
765 100,
766 this,
767 wxPD_CAN_ABORT |
768 wxPD_APP_MODAL |
769 wxPD_ELAPSED_TIME |
770 wxPD_ESTIMATED_TIME |
771 wxPD_REMAINING_TIME
772 );
773
774 // thread is not running yet, no need for crit sect
775 m_cancelled = false;
776
777 thread->Run();
778 }
779
780 void MyFrame::OnWorkerEvent(wxThreadEvent& event)
781 {
782 int n = event.GetInt();
783 if ( n == -1 )
784 {
785 m_dlgProgress->Destroy();
786 m_dlgProgress = (wxProgressDialog *)NULL;
787
788 // the dialog is aborted because the event came from another thread, so
789 // we may need to wake up the main event loop for the dialog to be
790 // really closed
791 wxWakeUpIdle();
792 }
793 else
794 {
795 if ( !m_dlgProgress->Update(n) )
796 {
797 wxCriticalSectionLocker lock(m_csCancelled);
798
799 m_cancelled = true;
800 }
801 }
802 }
803
804 void MyFrame::OnStartGUIThread(wxCommandEvent& WXUNUSED(event))
805 {
806 // we use this to check that disabling logging only affects the main thread
807 // but the messages from the worker thread will still be logged
808 wxLogNull noLog;
809 wxLogMessage("You shouldn't see this message because of wxLogNull");
810
811 MyImageDialog dlg(this);
812
813 dlg.ShowModal();
814 }
815
816
817 // ----------------------------------------------------------------------------
818 // MyImageDialog
819 // ----------------------------------------------------------------------------
820
821 BEGIN_EVENT_TABLE(MyImageDialog, wxDialog)
822 EVT_THREAD(GUITHREAD_EVENT, MyImageDialog::OnGUIThreadEvent)
823 EVT_PAINT(MyImageDialog::OnPaint)
824 END_EVENT_TABLE()
825
826 MyImageDialog::MyImageDialog(wxFrame *parent)
827 : wxDialog(parent, wxID_ANY, "Image created by a secondary thread",
828 wxDefaultPosition, wxSize(GUITHREAD_BMP_SIZE,GUITHREAD_BMP_SIZE)*1.5, wxDEFAULT_DIALOG_STYLE),
829 m_thread(this)
830 {
831 m_nCurrentProgress = 0;
832
833 CentreOnScreen();
834
835 // NOTE: no need to lock m_csBmp until the thread isn't started:
836
837 // create the bitmap
838 if (!m_bmp.Create(GUITHREAD_BMP_SIZE,GUITHREAD_BMP_SIZE) || !m_bmp.IsOk())
839 {
840 wxLogError("Couldn't create the bitmap!");
841 return;
842 }
843
844 // clean it
845 wxMemoryDC dc(m_bmp);
846 dc.SetBackground(*wxBLACK_BRUSH);
847 dc.Clear();
848
849 // draw the bitmap from a secondary thread
850 if ( m_thread.Create() != wxTHREAD_NO_ERROR ||
851 m_thread.Run() != wxTHREAD_NO_ERROR )
852 {
853 wxLogError(wxT("Can't create/run thread!"));
854 return;
855 }
856 }
857
858 MyImageDialog::~MyImageDialog()
859 {
860 // in case our thread is still running and for some reason we are destroyed,
861 // do wait for the thread to complete as it assumes that its MyImageDialog
862 // pointer is always valid
863 m_thread.Delete();
864 }
865
866 void MyImageDialog::OnGUIThreadEvent(wxThreadEvent& event)
867 {
868 m_nCurrentProgress = int(((float)event.GetInt()*100)/GUITHREAD_NUM_UPDATES);
869
870 Refresh();
871 }
872
873 void MyImageDialog::OnPaint(wxPaintEvent& WXUNUSED(evt))
874 {
875 wxPaintDC dc(this);
876
877 const wxSize& sz = dc.GetSize();
878
879 {
880 // paint the bitmap
881 wxCriticalSectionLocker locker(m_csBmp);
882 dc.DrawBitmap(m_bmp, (sz.GetWidth()-GUITHREAD_BMP_SIZE)/2,
883 (sz.GetHeight()-GUITHREAD_BMP_SIZE)/2);
884 }
885
886 // paint a sort of progress bar with a 10px border:
887 dc.SetBrush(*wxRED_BRUSH);
888 dc.DrawRectangle(10,10, m_nCurrentProgress*(sz.GetWidth()-20)/100,30);
889 dc.SetTextForeground(*wxBLUE);
890 dc.DrawText(wxString::Format("%d%%", m_nCurrentProgress),
891 (sz.GetWidth()-dc.GetCharWidth()*2)/2,
892 25-dc.GetCharHeight()/2);
893 }
894
895 // ----------------------------------------------------------------------------
896 // MyThread
897 // ----------------------------------------------------------------------------
898
899 MyThread::MyThread()
900 : wxThread()
901 {
902 m_count = 0;
903 }
904
905 MyThread::~MyThread()
906 {
907 wxCriticalSectionLocker locker(wxGetApp().m_critsect);
908
909 wxArrayThread& threads = wxGetApp().m_threads;
910 threads.Remove(this);
911
912 if ( threads.IsEmpty() )
913 {
914 // signal the main thread that there are no more threads left if it is
915 // waiting for us
916 if ( wxGetApp().m_shuttingDown )
917 {
918 wxGetApp().m_shuttingDown = false;
919
920 wxGetApp().m_semAllDone.Post();
921 }
922 }
923 }
924
925 wxThread::ExitCode MyThread::Entry()
926 {
927 wxLogMessage("Thread started (priority = %u).", GetPriority());
928
929 for ( m_count = 0; m_count < 10; m_count++ )
930 {
931 // check if the application is shutting down: in this case all threads
932 // should stop a.s.a.p.
933 {
934 wxCriticalSectionLocker locker(wxGetApp().m_critsect);
935 if ( wxGetApp().m_shuttingDown )
936 return NULL;
937 }
938
939 // check if just this thread was asked to exit
940 if ( TestDestroy() )
941 break;
942
943 wxLogMessage("Thread progress: %u", m_count);
944
945 // wxSleep() can't be called from non-GUI thread!
946 wxThread::Sleep(1000);
947 }
948
949 wxLogMessage("Thread finished.");
950
951 return NULL;
952 }
953
954
955 // ----------------------------------------------------------------------------
956 // MyWorkerThread
957 // ----------------------------------------------------------------------------
958
959 // define this symbol to 1 to test if the YieldFor() call in the wxProgressDialog::Update
960 // function provokes a race condition in which the second wxThreadEvent posted by
961 // MyWorkerThread::Entry is processed by the YieldFor() call of wxProgressDialog::Update
962 // and results in the destruction of the progress dialog itself, resulting in a crash later.
963 #define TEST_YIELD_RACE_CONDITION 0
964
965 MyWorkerThread::MyWorkerThread(MyFrame *frame)
966 : wxThread()
967 {
968 m_frame = frame;
969 m_count = 0;
970 }
971
972 void MyWorkerThread::OnExit()
973 {
974 }
975
976 wxThread::ExitCode MyWorkerThread::Entry()
977 {
978 #if TEST_YIELD_RACE_CONDITION
979 if ( TestDestroy() )
980 return NULL;
981
982 wxThreadEvent event( wxEVT_THREAD, WORKER_EVENT );
983
984 event.SetInt( 50 );
985 wxQueueEvent( m_frame, event.Clone() );
986
987 event.SetInt(-1);
988 wxQueueEvent( m_frame, event.Clone() );
989 #else
990 for ( m_count = 0; !m_frame->Cancelled() && (m_count < 100); m_count++ )
991 {
992 // check if we were asked to exit
993 if ( TestDestroy() )
994 break;
995
996 // create any type of command event here
997 wxThreadEvent event( wxEVT_THREAD, WORKER_EVENT );
998 event.SetInt( m_count );
999
1000 // send in a thread-safe way
1001 wxQueueEvent( m_frame, event.Clone() );
1002
1003 wxMilliSleep(200);
1004 }
1005
1006 wxThreadEvent event( wxEVT_THREAD, WORKER_EVENT );
1007 event.SetInt(-1); // that's all
1008 wxQueueEvent( m_frame, event.Clone() );
1009 #endif
1010
1011 return NULL;
1012 }
1013
1014
1015 // ----------------------------------------------------------------------------
1016 // MyGUIThread
1017 // ----------------------------------------------------------------------------
1018
1019 wxThread::ExitCode MyGUIThread::Entry()
1020 {
1021 // uncomment this to check that disabling logging here does disable it for
1022 // this thread -- but not the main one if you also comment out wxLogNull
1023 // line in MyFrame::OnStartGUIThread()
1024 //wxLogNull noLog;
1025
1026 // this goes to the main window
1027 wxLogMessage("GUI thread starting");
1028
1029 // use a thread-specific log target for this thread to show that its
1030 // messages don't appear in the main window while it runs
1031 wxLogBuffer logBuf;
1032 wxLog::SetThreadActiveTarget(&logBuf);
1033
1034 for (int i=0; i<GUITHREAD_NUM_UPDATES && !TestDestroy(); i++)
1035 {
1036 // inform the GUI toolkit that we're going to use GUI functions
1037 // from a secondary thread:
1038 wxMutexGuiEnter();
1039
1040 {
1041 wxCriticalSectionLocker lock(m_dlg->m_csBmp);
1042
1043 // draw some more stuff on the bitmap
1044 wxMemoryDC dc(m_dlg->m_bmp);
1045 dc.SetBrush((i%2)==0 ? *wxBLUE_BRUSH : *wxGREEN_BRUSH);
1046 dc.DrawRectangle(rand()%GUITHREAD_BMP_SIZE, rand()%GUITHREAD_BMP_SIZE, 30, 30);
1047
1048 // simulate long drawing time:
1049 wxMilliSleep(200);
1050 }
1051
1052 // if we don't release the GUI mutex the MyImageDialog won't be able to refresh
1053 wxMutexGuiLeave();
1054
1055 // notify the dialog that another piece of our masterpiece is complete:
1056 wxThreadEvent event( wxEVT_THREAD, GUITHREAD_EVENT );
1057 event.SetInt(i+1);
1058 wxQueueEvent( m_dlg, event.Clone() );
1059
1060 if ( !((i + 1) % 10) )
1061 {
1062 // this message will go to the buffer
1063 wxLogMessage("Step #%d.", i + 1);
1064 }
1065
1066 // give the main thread the time to refresh before we lock the GUI mutex again
1067 // FIXME: find a better way to do this!
1068 wxMilliSleep(100);
1069 }
1070
1071 // now remove the thread-specific thread target
1072 wxLog::SetThreadActiveTarget(NULL);
1073
1074 // so that this goes to the main window again
1075 wxLogMessage("GUI thread finished.");
1076
1077 return (ExitCode)0;
1078 }