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