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