minor cleaning: use %p instead of 0x%lx; clear in the menu name which is the thread...
[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 // Define a new application type
63 class MyApp : public wxApp
64 {
65 public:
66 MyApp();
67 virtual ~MyApp(){};
68
69 virtual bool OnInit();
70
71 // critical section protects access to all of the fields below
72 wxCriticalSection m_critsect;
73
74 // all the threads currently alive - as soon as the thread terminates, it's
75 // removed from the array
76 wxArrayThread m_threads;
77
78 // semaphore used to wait for the threads to exit, see MyFrame::OnQuit()
79 wxSemaphore m_semAllDone;
80
81 // indicates that we're shutting down and all threads should exit
82 bool m_shuttingDown;
83 };
84
85 // Define a new frame type
86 class MyFrame: public wxFrame
87 {
88 public:
89 // ctor
90 MyFrame(wxFrame *frame, const wxString& title, int x, int y, int w, int h);
91 virtual ~MyFrame();
92
93 // this function is MT-safe, i.e. it can be called from worker threads
94 // safely without any additional locking
95 void LogThreadMessage(const wxString& text)
96 {
97 wxCriticalSectionLocker lock(m_csMessages);
98 m_messages.push_back(text);
99
100 // as we effectively log the messages from the idle event handler,
101 // ensure it's going to be called now that we have some messages to log
102 wxWakeUpIdle();
103 }
104
105 // accessors for MyWorkerThread (called in its context!)
106 bool Cancelled();
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
123 void OnExecMain(wxCommandEvent& event);
124 void OnExecThread(wxCommandEvent& event);
125
126 void OnShowCPUs(wxCommandEvent& event);
127 void OnAbout(wxCommandEvent& event);
128
129 void OnIdle(wxIdleEvent &event);
130 void OnWorkerEvent(wxThreadEvent& event);
131 void OnUpdateWorker(wxUpdateUIEvent& event);
132
133
134 // thread helper functions
135 // -----------------------
136
137 // helper function - creates a new thread (but doesn't run it)
138 MyThread *CreateThread();
139
140 // update display in our status bar: called during idle handling
141 void UpdateThreadStatus();
142
143 // log the messages queued by LogThreadMessage()
144 void DoLogThreadMessages();
145
146
147 // internal variables
148 // ------------------
149
150 // just some place to put our messages in
151 wxTextCtrl *m_txtctrl;
152
153 // the array of pending messages to be displayed and the critical section
154 // protecting it
155 wxArrayString m_messages;
156 wxCriticalSection m_csMessages;
157
158 // remember the number of running threads and total number of threads
159 size_t m_nRunning,
160 m_nCount;
161
162 // the progress dialog which we show while worker thread is running
163 wxProgressDialog *m_dlgProgress;
164
165 // was the worker thread cancelled by user?
166 bool m_cancelled;
167
168 // protects m_cancelled
169 wxCriticalSection m_critsectWork;
170
171 DECLARE_EVENT_TABLE()
172 };
173
174 // ----------------------------------------------------------------------------
175 // constants
176 // ----------------------------------------------------------------------------
177
178 // ID for the menu commands
179 enum
180 {
181 THREAD_QUIT = wxID_EXIT,
182 THREAD_ABOUT = wxID_ABOUT,
183 THREAD_TEXT = 101,
184 THREAD_CLEAR,
185 THREAD_START_THREAD = 201,
186 THREAD_START_THREADS,
187 THREAD_STOP_THREAD,
188 THREAD_PAUSE_THREAD,
189 THREAD_RESUME_THREAD,
190 THREAD_START_WORKER,
191
192 THREAD_EXEC_MAIN,
193 THREAD_EXEC_THREAD,
194
195 THREAD_SHOWCPUS,
196
197 WORKER_EVENT = wxID_HIGHEST+1 // this one gets sent from the worker thread
198 };
199
200 // ----------------------------------------------------------------------------
201 // GUI thread
202 // ----------------------------------------------------------------------------
203
204 class MyThread : public wxThread
205 {
206 public:
207 MyThread(MyFrame *frame);
208 virtual ~MyThread();
209
210 // thread execution starts here
211 virtual void *Entry();
212
213 // write something to the text control in the main frame
214 void WriteText(const wxString& text)
215 {
216 m_frame->LogThreadMessage(text);
217 }
218
219 public:
220 unsigned m_count;
221 MyFrame *m_frame;
222 };
223
224 // ----------------------------------------------------------------------------
225 // worker thread
226 // ----------------------------------------------------------------------------
227
228 class MyWorkerThread : public wxThread
229 {
230 public:
231 MyWorkerThread(MyFrame *frame);
232
233 // thread execution starts here
234 virtual void *Entry();
235
236 // called when the thread exits - whether it terminates normally or is
237 // stopped with Delete() (but not when it is Kill()ed!)
238 virtual void OnExit();
239
240 public:
241 MyFrame *m_frame;
242 unsigned m_count;
243 };
244
245 // ----------------------------------------------------------------------------
246 // a thread which simply calls wxExecute
247 // ----------------------------------------------------------------------------
248
249 class MyExecThread : public wxThread
250 {
251 public:
252 MyExecThread(const wxChar *command) : wxThread(wxTHREAD_JOINABLE),
253 m_command(command)
254 {
255 Create();
256 }
257
258 virtual ExitCode Entry()
259 {
260 return wxUIntToPtr(EXEC(m_command));
261 }
262
263 private:
264 wxString m_command;
265 };
266
267 // ============================================================================
268 // implementation
269 // ============================================================================
270
271 // ----------------------------------------------------------------------------
272 // the application class
273 // ----------------------------------------------------------------------------
274
275 // Create a new application object
276 IMPLEMENT_APP(MyApp)
277
278 MyApp::MyApp()
279 {
280 m_shuttingDown = false;
281 }
282
283 // `Main program' equivalent, creating windows and returning main app frame
284 bool MyApp::OnInit()
285 {
286 if ( !wxApp::OnInit() )
287 return false;
288
289 // uncomment this to get some debugging messages from the trace code
290 // on the console (or just set WXTRACE env variable to include "thread")
291 wxLog::AddTraceMask("thread");
292
293 // Create the main frame window
294 MyFrame *frame = new MyFrame((wxFrame *)NULL, _T("wxWidgets threads sample"),
295 50, 50, 450, 340);
296 SetTopWindow(frame);
297
298 // Show the frame
299 frame->Show(true);
300
301 return true;
302 }
303
304 // ----------------------------------------------------------------------------
305 // MyFrame
306 // ----------------------------------------------------------------------------
307
308 BEGIN_EVENT_TABLE(MyFrame, wxFrame)
309 EVT_MENU(THREAD_QUIT, MyFrame::OnQuit)
310 EVT_MENU(THREAD_CLEAR, MyFrame::OnClear)
311 EVT_MENU(THREAD_START_THREAD, MyFrame::OnStartThread)
312 EVT_MENU(THREAD_START_THREADS, MyFrame::OnStartThreads)
313 EVT_MENU(THREAD_STOP_THREAD, MyFrame::OnStopThread)
314 EVT_MENU(THREAD_PAUSE_THREAD, MyFrame::OnPauseThread)
315 EVT_MENU(THREAD_RESUME_THREAD, MyFrame::OnResumeThread)
316
317 EVT_MENU(THREAD_EXEC_MAIN, MyFrame::OnExecMain)
318 EVT_MENU(THREAD_EXEC_THREAD, MyFrame::OnExecThread)
319
320 EVT_MENU(THREAD_SHOWCPUS, MyFrame::OnShowCPUs)
321 EVT_MENU(THREAD_ABOUT, MyFrame::OnAbout)
322
323 EVT_MENU(THREAD_START_WORKER, MyFrame::OnStartWorker)
324
325 EVT_UPDATE_UI(THREAD_START_WORKER, MyFrame::OnUpdateWorker)
326 EVT_THREAD(WORKER_EVENT, MyFrame::OnWorkerEvent)
327 EVT_IDLE(MyFrame::OnIdle)
328 END_EVENT_TABLE()
329
330 // My frame constructor
331 MyFrame::MyFrame(wxFrame *frame, const wxString& title,
332 int x, int y, int w, int h)
333 : wxFrame(frame, wxID_ANY, title, wxPoint(x, y), wxSize(w, h))
334 {
335 SetIcon(wxIcon(sample_xpm));
336
337 // Make a menubar
338 wxMenuBar *menuBar = new wxMenuBar;
339
340 wxMenu *menuFile = new wxMenu;
341 menuFile->Append(THREAD_CLEAR, _T("&Clear log\tCtrl-L"));
342 menuFile->AppendSeparator();
343 menuFile->Append(THREAD_QUIT, _T("E&xit\tAlt-X"));
344 menuBar->Append(menuFile, _T("&File"));
345
346 wxMenu *menuThread = new wxMenu;
347 menuThread->Append(THREAD_START_THREAD, _T("&Start a new thread\tCtrl-N"));
348 menuThread->Append(THREAD_START_THREADS, _T("Start &many threads at once"));
349 menuThread->Append(THREAD_STOP_THREAD, _T("S&top the last spawned thread\tCtrl-S"));
350 menuThread->AppendSeparator();
351 menuThread->Append(THREAD_PAUSE_THREAD, _T("&Pause the last spawned running thread\tCtrl-P"));
352 menuThread->Append(THREAD_RESUME_THREAD, _T("&Resume the first suspended thread\tCtrl-R"));
353 menuThread->AppendSeparator();
354 menuThread->Append(THREAD_START_WORKER, _T("Start a &worker thread\tCtrl-W"));
355 menuBar->Append(menuThread, _T("&Thread"));
356
357 wxMenu *menuExec = new wxMenu;
358 menuExec->Append(THREAD_EXEC_MAIN, _T("&Launch a program from main thread\tF5"));
359 menuExec->Append(THREAD_EXEC_THREAD, _T("L&aunch a program from a thread\tCtrl-F5"));
360 menuBar->Append(menuExec, _T("&Execute"));
361
362 wxMenu *menuHelp = new wxMenu;
363 menuHelp->Append(THREAD_SHOWCPUS, _T("&Show CPU count"));
364 menuHelp->AppendSeparator();
365 menuHelp->Append(THREAD_ABOUT, _T("&About..."));
366 menuBar->Append(menuHelp, _T("&Help"));
367
368 SetMenuBar(menuBar);
369
370 m_nRunning = m_nCount = 0;
371
372 m_dlgProgress = (wxProgressDialog *)NULL;
373
374 #if wxUSE_STATUSBAR
375 CreateStatusBar(2);
376 #endif // wxUSE_STATUSBAR
377
378 m_txtctrl = new wxTextCtrl(this, wxID_ANY, _T(""), wxPoint(0, 0), wxSize(0, 0),
379 wxTE_MULTILINE | wxTE_READONLY);
380 }
381
382 MyFrame::~MyFrame()
383 {
384 // NB: although the OS will terminate all the threads anyhow when the main
385 // one exits, it's good practice to do it ourselves -- even if it's not
386 // completely trivial in this example
387
388 // tell all the threads to terminate: note that they can't terminate while
389 // we're deleting them because they will block in their OnExit() -- this is
390 // important as otherwise we might access invalid array elements
391
392 {
393 wxCriticalSectionLocker locker(wxGetApp().m_critsect);
394
395 // check if we have any threads running first
396 const wxArrayThread& threads = wxGetApp().m_threads;
397 size_t count = threads.GetCount();
398
399 if ( !count )
400 return;
401
402 // set the flag indicating that all threads should exit
403 wxGetApp().m_shuttingDown = true;
404 }
405
406 // now wait for them to really terminate
407 wxGetApp().m_semAllDone.Wait();
408 }
409
410 MyThread *MyFrame::CreateThread()
411 {
412 MyThread *thread = new MyThread(this);
413
414 if ( thread->Create() != wxTHREAD_NO_ERROR )
415 {
416 wxLogError(wxT("Can't create thread!"));
417 }
418
419 wxCriticalSectionLocker enter(wxGetApp().m_critsect);
420 wxGetApp().m_threads.Add(thread);
421
422 return thread;
423 }
424
425 void MyFrame::OnStartThreads(wxCommandEvent& WXUNUSED(event) )
426 {
427 static long s_num;
428
429 s_num = wxGetNumberFromUser(_T("How many threads to start: "), _T(""),
430 _T("wxThread sample"), s_num, 1, 10000, this);
431 if ( s_num == -1 )
432 {
433 s_num = 10;
434
435 return;
436 }
437
438 unsigned count = unsigned(s_num), n;
439
440 wxArrayThread threads;
441
442 // first create them all...
443 for ( n = 0; n < count; n++ )
444 {
445 wxThread *thr = CreateThread();
446
447 // we want to show the effect of SetPriority(): the first thread will
448 // have the lowest priority, the second - the highest, all the rest
449 // the normal one
450 if ( n == 0 )
451 thr->SetPriority(WXTHREAD_MIN_PRIORITY);
452 else if ( n == 1 )
453 thr->SetPriority(WXTHREAD_MAX_PRIORITY);
454 else
455 thr->SetPriority(WXTHREAD_DEFAULT_PRIORITY);
456
457 threads.Add(thr);
458 }
459
460 #if wxUSE_STATUSBAR
461 wxString msg;
462 msg.Printf(wxT("%d new threads created."), count);
463 SetStatusText(msg, 1);
464 #endif // wxUSE_STATUSBAR
465
466 // ...and then start them
467 for ( n = 0; n < count; n++ )
468 {
469 threads[n]->Run();
470 }
471 }
472
473 void MyFrame::OnStartThread(wxCommandEvent& WXUNUSED(event) )
474 {
475 MyThread *thread = CreateThread();
476
477 if ( thread->Run() != wxTHREAD_NO_ERROR )
478 {
479 wxLogError(wxT("Can't start thread!"));
480 }
481
482 #if wxUSE_STATUSBAR
483 SetStatusText(_T("New thread started."), 1);
484 #endif // wxUSE_STATUSBAR
485 }
486
487 void MyFrame::OnStopThread(wxCommandEvent& WXUNUSED(event) )
488 {
489 wxCriticalSectionLocker enter(wxGetApp().m_critsect);
490
491 // stop the last thread
492 if ( wxGetApp().m_threads.IsEmpty() )
493 {
494 wxLogError(wxT("No thread to stop!"));
495 }
496 else
497 {
498 wxGetApp().m_threads.Last()->Delete();
499
500 #if wxUSE_STATUSBAR
501 SetStatusText(_T("Last thread stopped."), 1);
502 #endif // wxUSE_STATUSBAR
503 }
504 }
505
506 void MyFrame::OnResumeThread(wxCommandEvent& WXUNUSED(event) )
507 {
508 wxCriticalSectionLocker enter(wxGetApp().m_critsect);
509
510 // resume first suspended thread
511 size_t n = 0, count = wxGetApp().m_threads.Count();
512 while ( n < count && !wxGetApp().m_threads[n]->IsPaused() )
513 n++;
514
515 if ( n == count )
516 {
517 wxLogError(wxT("No thread to resume!"));
518 }
519 else
520 {
521 wxGetApp().m_threads[n]->Resume();
522
523 #if wxUSE_STATUSBAR
524 SetStatusText(_T("Thread resumed."), 1);
525 #endif // wxUSE_STATUSBAR
526 }
527 }
528
529 void MyFrame::OnPauseThread(wxCommandEvent& WXUNUSED(event) )
530 {
531 wxCriticalSectionLocker enter(wxGetApp().m_critsect);
532
533 // pause last running thread
534 int n = wxGetApp().m_threads.Count() - 1;
535 while ( n >= 0 && !wxGetApp().m_threads[n]->IsRunning() )
536 n--;
537
538 if ( n < 0 )
539 {
540 wxLogError(wxT("No thread to pause!"));
541 }
542 else
543 {
544 wxGetApp().m_threads[n]->Pause();
545
546 #if wxUSE_STATUSBAR
547 SetStatusText(_T("Thread paused."), 1);
548 #endif // wxUSE_STATUSBAR
549 }
550 }
551
552 void MyFrame::OnIdle(wxIdleEvent& event)
553 {
554 DoLogThreadMessages();
555
556 UpdateThreadStatus();
557
558 event.Skip();
559 }
560
561 void MyFrame::DoLogThreadMessages()
562 {
563 wxCriticalSectionLocker lock(m_csMessages);
564
565 const size_t count = m_messages.size();
566 for ( size_t n = 0; n < count; n++ )
567 {
568 m_txtctrl->AppendText(m_messages[n]);
569 }
570
571 m_messages.clear();
572 }
573
574 void MyFrame::UpdateThreadStatus()
575 {
576 wxCriticalSectionLocker enter(wxGetApp().m_critsect);
577
578 // update the counts of running/total threads
579 size_t nRunning = 0,
580 nCount = wxGetApp().m_threads.Count();
581 for ( size_t n = 0; n < nCount; n++ )
582 {
583 if ( wxGetApp().m_threads[n]->IsRunning() )
584 nRunning++;
585 }
586
587 if ( nCount != m_nCount || nRunning != m_nRunning )
588 {
589 m_nRunning = nRunning;
590 m_nCount = nCount;
591
592 wxLogStatus(this, wxT("%u threads total, %u running."), unsigned(nCount), unsigned(nRunning));
593 }
594 //else: avoid flicker - don't print anything
595 }
596
597 void MyFrame::OnQuit(wxCommandEvent& WXUNUSED(event) )
598 {
599 Close(true);
600 }
601
602 void MyFrame::OnExecMain(wxCommandEvent& WXUNUSED(event))
603 {
604 wxLogMessage(wxT("The exit code from the main program is %ld"),
605 EXEC(_T("/bin/echo \"main program\"")));
606 }
607
608 void MyFrame::OnExecThread(wxCommandEvent& WXUNUSED(event))
609 {
610 MyExecThread thread(wxT("/bin/echo \"child thread\""));
611 thread.Run();
612
613 wxLogMessage(wxT("The exit code from a child thread is %ld"),
614 (long)wxPtrToUInt(thread.Wait()));
615 }
616
617 void MyFrame::OnShowCPUs(wxCommandEvent& WXUNUSED(event))
618 {
619 wxString msg;
620
621 int nCPUs = wxThread::GetCPUCount();
622 switch ( nCPUs )
623 {
624 case -1:
625 msg = _T("Unknown number of CPUs");
626 break;
627
628 case 0:
629 msg = _T("WARNING: you're running without any CPUs!");
630 break;
631
632 case 1:
633 msg = _T("This system only has one CPU.");
634 break;
635
636 default:
637 msg.Printf(wxT("This system has %d CPUs"), nCPUs);
638 }
639
640 wxLogMessage(msg);
641 }
642
643 void MyFrame::OnAbout(wxCommandEvent& WXUNUSED(event) )
644 {
645 wxMessageDialog dialog(this,
646 _T("wxWidgets multithreaded application sample\n")
647 _T("(c) 1998 Julian Smart, Guilhem Lavaux\n")
648 _T("(c) 1999 Vadim Zeitlin\n")
649 _T("(c) 2000 Robert Roebling"),
650 _T("About wxThread sample"),
651 wxOK | wxICON_INFORMATION);
652
653 dialog.ShowModal();
654 }
655
656 void MyFrame::OnClear(wxCommandEvent& WXUNUSED(event))
657 {
658 m_txtctrl->Clear();
659 }
660
661 void MyFrame::OnUpdateWorker(wxUpdateUIEvent& event)
662 {
663 event.Enable( m_dlgProgress == NULL );
664 }
665
666 void MyFrame::OnStartWorker(wxCommandEvent& WXUNUSED(event))
667 {
668 MyWorkerThread *thread = new MyWorkerThread(this);
669
670 if ( thread->Create() != wxTHREAD_NO_ERROR )
671 {
672 wxLogError(wxT("Can't create thread!"));
673 return;
674 }
675
676 m_dlgProgress = new wxProgressDialog
677 (
678 _T("Progress dialog"),
679 _T("Wait until the thread terminates or press [Cancel]"),
680 100,
681 this,
682 wxPD_CAN_ABORT |
683 wxPD_APP_MODAL |
684 wxPD_ELAPSED_TIME |
685 wxPD_ESTIMATED_TIME |
686 wxPD_REMAINING_TIME
687 );
688
689 // thread is not running yet, no need for crit sect
690 m_cancelled = false;
691
692 thread->Run();
693 }
694
695 void MyFrame::OnWorkerEvent(wxThreadEvent& event)
696 {
697 int n = event.GetInt();
698 if ( n == -1 )
699 {
700 m_dlgProgress->Destroy();
701 m_dlgProgress = (wxProgressDialog *)NULL;
702
703 // the dialog is aborted because the event came from another thread, so
704 // we may need to wake up the main event loop for the dialog to be
705 // really closed
706 wxWakeUpIdle();
707 }
708 else
709 {
710 if ( !m_dlgProgress->Update(n) )
711 {
712 wxCriticalSectionLocker lock(m_critsectWork);
713
714 m_cancelled = true;
715 }
716 }
717 }
718
719 bool MyFrame::Cancelled()
720 {
721 wxCriticalSectionLocker lock(m_critsectWork);
722
723 return m_cancelled;
724 }
725
726
727 // ----------------------------------------------------------------------------
728 // MyThread
729 // ----------------------------------------------------------------------------
730
731 MyThread::MyThread(MyFrame *frame)
732 : wxThread()
733 {
734 m_count = 0;
735 m_frame = frame;
736 }
737
738 MyThread::~MyThread()
739 {
740 wxCriticalSectionLocker locker(wxGetApp().m_critsect);
741
742 wxArrayThread& threads = wxGetApp().m_threads;
743 threads.Remove(this);
744
745 if ( threads.IsEmpty() )
746 {
747 // signal the main thread that there are no more threads left if it is
748 // waiting for us
749 if ( wxGetApp().m_shuttingDown )
750 {
751 wxGetApp().m_shuttingDown = false;
752
753 wxGetApp().m_semAllDone.Post();
754 }
755 }
756 }
757
758 void *MyThread::Entry()
759 {
760 wxString text;
761
762 text.Printf(wxT("Thread %p started (priority = %u).\n"),
763 GetId(), GetPriority());
764 WriteText(text);
765 // wxLogMessage(text); -- test wxLog thread safeness
766
767 for ( m_count = 0; m_count < 10; m_count++ )
768 {
769 // check if the application is shutting down: in this case all threads
770 // should stop a.s.a.p.
771 {
772 wxCriticalSectionLocker locker(wxGetApp().m_critsect);
773 if ( wxGetApp().m_shuttingDown )
774 return NULL;
775 }
776
777 // check if just this thread was asked to exit
778 if ( TestDestroy() )
779 break;
780
781 text.Printf(wxT("[%u] Thread %p here.\n"), m_count, GetId());
782 WriteText(text);
783
784 // wxSleep() can't be called from non-GUI thread!
785 wxThread::Sleep(1000);
786 }
787
788 text.Printf(wxT("Thread %p finished.\n"), GetId());
789 WriteText(text);
790 // wxLogMessage(text); -- test wxLog thread safeness
791
792 return NULL;
793 }
794
795
796 // ----------------------------------------------------------------------------
797 // MyWorkerThread
798 // ----------------------------------------------------------------------------
799
800 MyWorkerThread::MyWorkerThread(MyFrame *frame)
801 : wxThread()
802 {
803 m_frame = frame;
804 m_count = 0;
805 }
806
807 void MyWorkerThread::OnExit()
808 {
809 }
810
811 // define this symbol to 1 to test if the YieldFor() call in the wxProgressDialog::Update
812 // function provokes a race condition in which the second wxThreadEvent posted by
813 // MyWorkerThread::Entry is processed by the YieldFor() call of wxProgressDialog::Update
814 // and results in the destruction of the progress dialog itself, resulting in a crash later.
815 #define TEST_YIELD_RACE_CONDITION 0
816
817 void *MyWorkerThread::Entry()
818 {
819 #if TEST_YIELD_RACE_CONDITION
820 if ( TestDestroy() )
821 return NULL;
822
823 wxThreadEvent event( wxEVT_COMMAND_THREAD, WORKER_EVENT );
824
825 event.SetInt( 50 );
826 wxQueueEvent( m_frame, event.Clone() );
827
828 event.SetInt(-1);
829 wxQueueEvent( m_frame, event.Clone() );
830 #else
831 for ( m_count = 0; !m_frame->Cancelled() && (m_count < 100); m_count++ )
832 {
833 // check if we were asked to exit
834 if ( TestDestroy() )
835 break;
836
837 // create any type of command event here
838 wxThreadEvent event( wxEVT_COMMAND_THREAD, WORKER_EVENT );
839 event.SetInt( m_count );
840
841 // send in a thread-safe way
842 wxQueueEvent( m_frame, event.Clone() );
843
844 wxMilliSleep(200);
845 }
846
847 wxThreadEvent event( wxEVT_COMMAND_THREAD, WORKER_EVENT );
848 event.SetInt(-1); // that's all
849 wxQueueEvent( m_frame, event.Clone() );
850 #endif
851
852 return NULL;
853 }
854