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