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