]> git.saurik.com Git - wxWidgets.git/blame_incremental - samples/thread/thread.cpp
compilation fix for wxOSX_USE_EXPERIMENTAL_FONTDIALOG==0 (closes #10834)
[wxWidgets.git] / samples / thread / thread.cpp
... / ...
CommitLineData
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
59class MyThread;
60WX_DEFINE_ARRAY_PTR(wxThread *, wxArrayThread);
61
62// ----------------------------------------------------------------------------
63// the application object
64// ----------------------------------------------------------------------------
65
66class MyApp : public wxApp
67{
68public:
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
92class MyFrame: public wxFrame
93{
94public:
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
114private:
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
182enum
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
208class MyThread : public wxThread
209{
210public:
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
223public:
224 unsigned m_count;
225 MyFrame *m_frame;
226};
227
228// ----------------------------------------------------------------------------
229// a worker thread
230// ----------------------------------------------------------------------------
231
232class MyWorkerThread : public wxThread
233{
234public:
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
244public:
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
255class MyImageDialog;
256
257class MyGUIThread : public wxThread
258{
259public:
260 MyGUIThread(MyImageDialog *dlg) : wxThread(wxTHREAD_JOINABLE)
261 {
262 m_dlg = dlg;
263 }
264
265 virtual ExitCode Entry();
266
267private:
268 MyImageDialog *m_dlg;
269};
270
271// ----------------------------------------------------------------------------
272// an helper dialog used by MyFrame::OnStartGUIThread
273// ----------------------------------------------------------------------------
274
275class MyImageDialog: public wxDialog
276{
277public:
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
286private:
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
305IMPLEMENT_APP(MyApp)
306
307MyApp::MyApp()
308{
309 m_shuttingDown = false;
310}
311
312// `Main program' equivalent, creating windows and returning main app frame
313bool 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
337BEGIN_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)
356END_EVENT_TABLE()
357
358// My frame constructor
359MyFrame::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
407MyFrame::~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
435MyThread *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
450void 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
463void 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
486bool MyFrame::Cancelled()
487{
488 wxCriticalSectionLocker lock(m_csCancelled);
489
490 return m_cancelled;
491}
492
493// ----------------------------------------------------------------------------
494// MyFrame - event handlers
495// ----------------------------------------------------------------------------
496
497void 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
545void 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
559void 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
578void 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
601void 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
624void MyFrame::OnIdle(wxIdleEvent& event)
625{
626 DoLogThreadMessages();
627
628 UpdateThreadStatus();
629
630 event.Skip();
631}
632
633void MyFrame::OnQuit(wxCommandEvent& WXUNUSED(event) )
634{
635 Close(true);
636}
637
638void 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
655void 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
681void 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
694void MyFrame::OnClear(wxCommandEvent& WXUNUSED(event))
695{
696 m_txtctrl->Clear();
697}
698
699void MyFrame::OnUpdateWorker(wxUpdateUIEvent& event)
700{
701 event.Enable( m_dlgProgress == NULL );
702}
703
704void 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
733void 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
757void MyFrame::OnStartGUIThread(wxCommandEvent& WXUNUSED(event))
758{
759 MyImageDialog dlg(this);
760
761 dlg.ShowModal();
762}
763
764
765// ----------------------------------------------------------------------------
766// MyImageDialog
767// ----------------------------------------------------------------------------
768
769BEGIN_EVENT_TABLE(MyImageDialog, wxDialog)
770 EVT_THREAD(GUITHREAD_EVENT, MyImageDialog::OnGUIThreadEvent)
771 EVT_PAINT(MyImageDialog::OnPaint)
772END_EVENT_TABLE()
773
774MyImageDialog::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
806MyImageDialog::~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
814void MyImageDialog::OnGUIThreadEvent(wxThreadEvent& event)
815{
816 m_nCurrentProgress = int(((float)event.GetInt()*100)/GUITHREAD_NUM_UPDATES);
817
818 Refresh();
819}
820
821void 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, m_nCurrentProgress*(sz.GetWidth()-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
847MyThread::MyThread(MyFrame *frame)
848 : wxThread()
849{
850 m_count = 0;
851 m_frame = frame;
852}
853
854MyThread::~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
874wxThread::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
922MyWorkerThread::MyWorkerThread(MyFrame *frame)
923 : wxThread()
924{
925 m_frame = frame;
926 m_count = 0;
927}
928
929void MyWorkerThread::OnExit()
930{
931}
932
933wxThread::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
976wxThread::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+1);
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}