]> git.saurik.com Git - wxWidgets.git/blame - samples/thread/thread.cpp
compilation fix for wxOSX_USE_EXPERIMENTAL_FONTDIALOG==0 (closes #10834)
[wxWidgets.git] / samples / thread / thread.cpp
CommitLineData
82052aff 1/////////////////////////////////////////////////////////////////////////////
c4f02b1f 2// Name: thread.cpp
be5a51fb 3// Purpose: wxWidgets thread sample
ffc45b67 4// Author: Guilhem Lavaux, Vadim Zeitlin
82052aff
GL
5// Modified by:
6// Created: 06/16/98
7// RCS-ID: $Id$
be5a51fb 8// Copyright: (c) 1998-2002 wxWidgets team
3222fde2 9// Licence: wxWindows license
82052aff
GL
10/////////////////////////////////////////////////////////////////////////////
11
3186fa91
FM
12// ============================================================================
13// declarations
14// ============================================================================
15
16// ----------------------------------------------------------------------------
17// headers
18// ----------------------------------------------------------------------------
19
82052aff
GL
20// For compilers that support precompilation, includes "wx/wx.h".
21#include "wx/wxprec.h"
22
23#ifdef __BORLANDC__
3222fde2 24 #pragma hdrstop
82052aff
GL
25#endif
26
27#ifndef WX_PRECOMP
3222fde2 28 #include "wx/wx.h"
82052aff
GL
29#endif
30
3222fde2
VZ
31#if !wxUSE_THREADS
32 #error "This sample requires thread support!"
33#endif // wxUSE_THREADS
34
82052aff
GL
35#include "wx/thread.h"
36#include "wx/dynarray.h"
e4f3eb42 37#include "wx/numdlg.h"
b9de1315
VZ
38#include "wx/progdlg.h"
39
3186fa91
FM
40// ----------------------------------------------------------------------------
41// resources
42// ----------------------------------------------------------------------------
43
df315913
JS
44#include "../sample.xpm"
45
3186fa91
FM
46// ----------------------------------------------------------------------------
47// private classes
48// ----------------------------------------------------------------------------
49
ffc45b67 50// define this to use wxExecute in the exec tests, otherwise just use system
cd5e9298 51#define USE_EXECUTE
ffc45b67 52
a4b59324
VZ
53#ifdef USE_EXECUTE
54 #define EXEC(cmd) wxExecute((cmd), wxEXEC_SYNC)
55#else
56 #define EXEC(cmd) system(cmd)
57#endif
b8b9762a 58
1bd3e1ef 59class MyThread;
38d6b957 60WX_DEFINE_ARRAY_PTR(wxThread *, wxArrayThread);
1bd3e1ef 61
c1b8f155
FM
62// ----------------------------------------------------------------------------
63// the application object
64// ----------------------------------------------------------------------------
65
bf1852e1 66class MyApp : public wxApp
82052aff 67{
98f026a6 68public:
ffc45b67 69 MyApp();
925e9792 70 virtual ~MyApp(){};
ffc45b67 71
98f026a6
VZ
72 virtual bool OnInit();
73
abbcf16d
VZ
74 // critical section protects access to all of the fields below
75 wxCriticalSection m_critsect;
76
98f026a6
VZ
77 // all the threads currently alive - as soon as the thread terminates, it's
78 // removed from the array
79 wxArrayThread m_threads;
80
963ac8fb
VZ
81 // semaphore used to wait for the threads to exit, see MyFrame::OnQuit()
82 wxSemaphore m_semAllDone;
ffc45b67 83
abbcf16d
VZ
84 // indicates that we're shutting down and all threads should exit
85 bool m_shuttingDown;
82052aff
GL
86};
87
c1b8f155
FM
88// ----------------------------------------------------------------------------
89// the main application frame
90// ----------------------------------------------------------------------------
91
82052aff
GL
92class MyFrame: public wxFrame
93{
a6b0bd49
VZ
94public:
95 // ctor
bf1852e1 96 MyFrame(wxFrame *frame, const wxString& title, int x, int y, int w, int h);
4e5bbd40 97 virtual ~MyFrame();
3222fde2 98
3a105cbc
VZ
99 // this function is MT-safe, i.e. it can be called from worker threads
100 // safely without any additional locking
101 void LogThreadMessage(const wxString& text)
102 {
103 wxCriticalSectionLocker lock(m_csMessages);
104 m_messages.push_back(text);
105
106 // as we effectively log the messages from the idle event handler,
107 // ensure it's going to be called now that we have some messages to log
108 wxWakeUpIdle();
109 }
a6b0bd49 110
b9de1315
VZ
111 // accessors for MyWorkerThread (called in its context!)
112 bool Cancelled();
113
3a105cbc
VZ
114private:
115 // event handlers
ff8151c1
FM
116 // --------------
117
82052aff 118 void OnQuit(wxCommandEvent& event);
bee503b0 119 void OnClear(wxCommandEvent& event);
a6b0bd49 120
82052aff 121 void OnStartThread(wxCommandEvent& event);
7c3d7e2d 122 void OnStartThreads(wxCommandEvent& event);
82052aff
GL
123 void OnStopThread(wxCommandEvent& event);
124 void OnPauseThread(wxCommandEvent& event);
a6b0bd49 125 void OnResumeThread(wxCommandEvent& event);
b9de1315 126
ce6d2511 127 void OnStartWorker(wxCommandEvent& event);
a4b59324 128 void OnExecMain(wxCommandEvent& event);
c1b8f155 129 void OnStartGUIThread(wxCommandEvent& event);
a4b59324
VZ
130
131 void OnShowCPUs(wxCommandEvent& event);
132 void OnAbout(wxCommandEvent& event);
133
3222fde2 134 void OnIdle(wxIdleEvent &event);
ff8151c1
FM
135 void OnWorkerEvent(wxThreadEvent& event);
136 void OnUpdateWorker(wxUpdateUIEvent& event);
137
138
139 // thread helper functions
140 // -----------------------
3222fde2 141
7c3d7e2d
VZ
142 // helper function - creates a new thread (but doesn't run it)
143 MyThread *CreateThread();
88ac883a 144
3a105cbc
VZ
145 // update display in our status bar: called during idle handling
146 void UpdateThreadStatus();
147
148 // log the messages queued by LogThreadMessage()
149 void DoLogThreadMessages();
150
151
ff8151c1
FM
152 // internal variables
153 // ------------------
154
bf1852e1
VZ
155 // just some place to put our messages in
156 wxTextCtrl *m_txtctrl;
3222fde2 157
3a105cbc
VZ
158 // the array of pending messages to be displayed and the critical section
159 // protecting it
160 wxArrayString m_messages;
161 wxCriticalSection m_csMessages;
162
7c3d7e2d 163 // remember the number of running threads and total number of threads
3a105cbc
VZ
164 size_t m_nRunning,
165 m_nCount;
7c3d7e2d 166
b9de1315
VZ
167 // the progress dialog which we show while worker thread is running
168 wxProgressDialog *m_dlgProgress;
169
170 // was the worker thread cancelled by user?
171 bool m_cancelled;
c1b8f155 172 wxCriticalSection m_csCancelled; // protects m_cancelled
b9de1315 173
a6b0bd49 174 DECLARE_EVENT_TABLE()
82052aff
GL
175};
176
3186fa91
FM
177// ----------------------------------------------------------------------------
178// constants
179// ----------------------------------------------------------------------------
180
ce6d2511
RR
181// ID for the menu commands
182enum
183{
91b07357
JS
184 THREAD_QUIT = wxID_EXIT,
185 THREAD_ABOUT = wxID_ABOUT,
a4b59324
VZ
186 THREAD_TEXT = 101,
187 THREAD_CLEAR,
188 THREAD_START_THREAD = 201,
189 THREAD_START_THREADS,
190 THREAD_STOP_THREAD,
191 THREAD_PAUSE_THREAD,
192 THREAD_RESUME_THREAD,
a4b59324 193
c1b8f155 194 THREAD_START_WORKER,
a4b59324 195 THREAD_EXEC_MAIN,
c1b8f155 196 THREAD_START_GUI_THREAD,
a4b59324
VZ
197
198 THREAD_SHOWCPUS,
a4b59324 199
c1b8f155
FM
200 WORKER_EVENT = wxID_HIGHEST+1, // this one gets sent from MyWorkerThread
201 GUITHREAD_EVENT // this one gets sent from MyGUIThread
ce6d2511
RR
202};
203
a4b59324 204// ----------------------------------------------------------------------------
c1b8f155 205// a simple thread
a4b59324 206// ----------------------------------------------------------------------------
ce6d2511 207
bee503b0 208class MyThread : public wxThread
82052aff 209{
a6b0bd49 210public:
82052aff 211 MyThread(MyFrame *frame);
abbcf16d 212 virtual ~MyThread();
3222fde2
VZ
213
214 // thread execution starts here
215 virtual void *Entry();
216
abbcf16d
VZ
217 // write something to the text control in the main frame
218 void WriteText(const wxString& text)
219 {
220 m_frame->LogThreadMessage(text);
221 }
a6b0bd49
VZ
222
223public:
b143cf70 224 unsigned m_count;
82052aff
GL
225 MyFrame *m_frame;
226};
227
a4b59324 228// ----------------------------------------------------------------------------
c1b8f155 229// a worker thread
a4b59324 230// ----------------------------------------------------------------------------
ce6d2511
RR
231
232class MyWorkerThread : public wxThread
3222fde2 233{
ce6d2511
RR
234public:
235 MyWorkerThread(MyFrame *frame);
236
237 // thread execution starts here
238 virtual void *Entry();
239
240 // called when the thread exits - whether it terminates normally or is
241 // stopped with Delete() (but not when it is Kill()ed!)
242 virtual void OnExit();
243
244public:
245 MyFrame *m_frame;
b143cf70 246 unsigned m_count;
3222fde2 247};
82052aff 248
c1b8f155
FM
249// ----------------------------------------------------------------------------
250// a thread which executes GUI calls using wxMutexGuiEnter/Leave
251// ----------------------------------------------------------------------------
252
253#define GUITHREAD_BMP_SIZE 300
254#define GUITHREAD_NUM_UPDATES 50
255class MyImageDialog;
256
257class MyGUIThread : public wxThread
258{
259public:
260 MyGUIThread(MyImageDialog *dlg) : wxThread(wxTHREAD_JOINABLE)
261 {
262 m_dlg = dlg;
263 }
264
265 virtual ExitCode Entry();
266
267private:
268 MyImageDialog *m_dlg;
269};
270
271// ----------------------------------------------------------------------------
272// an helper dialog used by MyFrame::OnStartGUIThread
273// ----------------------------------------------------------------------------
274
275class MyImageDialog: public wxDialog
276{
277public:
278 // ctor
279 MyImageDialog(wxFrame *frame);
280 ~MyImageDialog();
281
282 // stuff used by MyGUIThread:
283 wxBitmap m_bmp; // the bitmap drawn by MyGUIThread
284 wxCriticalSection m_csBmp; // protects m_bmp
285
286private:
287 void OnGUIThreadEvent(wxThreadEvent& event);
288 void OnPaint(wxPaintEvent&);
289
290 MyGUIThread m_thread;
291 int m_nCurrentProgress;
292
293 DECLARE_EVENT_TABLE()
294};
295
3186fa91 296// ============================================================================
a4b59324 297// implementation
3186fa91
FM
298// ============================================================================
299
300// ----------------------------------------------------------------------------
301// the application class
302// ----------------------------------------------------------------------------
303
304// Create a new application object
305IMPLEMENT_APP(MyApp)
306
307MyApp::MyApp()
308{
309 m_shuttingDown = false;
310}
311
312// `Main program' equivalent, creating windows and returning main app frame
313bool MyApp::OnInit()
314{
315 if ( !wxApp::OnInit() )
316 return false;
317
318 // uncomment this to get some debugging messages from the trace code
319 // on the console (or just set WXTRACE env variable to include "thread")
ff8151c1 320 wxLog::AddTraceMask("thread");
3186fa91
FM
321
322 // Create the main frame window
323 MyFrame *frame = new MyFrame((wxFrame *)NULL, _T("wxWidgets threads sample"),
324 50, 50, 450, 340);
325 SetTopWindow(frame);
326
327 // Show the frame
328 frame->Show(true);
329
330 return true;
331}
332
333// ----------------------------------------------------------------------------
334// MyFrame
a4b59324 335// ----------------------------------------------------------------------------
ce6d2511 336
82052aff 337BEGIN_EVENT_TABLE(MyFrame, wxFrame)
a4b59324
VZ
338 EVT_MENU(THREAD_QUIT, MyFrame::OnQuit)
339 EVT_MENU(THREAD_CLEAR, MyFrame::OnClear)
340 EVT_MENU(THREAD_START_THREAD, MyFrame::OnStartThread)
341 EVT_MENU(THREAD_START_THREADS, MyFrame::OnStartThreads)
342 EVT_MENU(THREAD_STOP_THREAD, MyFrame::OnStopThread)
343 EVT_MENU(THREAD_PAUSE_THREAD, MyFrame::OnPauseThread)
344 EVT_MENU(THREAD_RESUME_THREAD, MyFrame::OnResumeThread)
c1b8f155 345
8bf30d0f 346 EVT_MENU(THREAD_START_WORKER, MyFrame::OnStartWorker)
a4b59324 347 EVT_MENU(THREAD_EXEC_MAIN, MyFrame::OnExecMain)
c1b8f155 348 EVT_MENU(THREAD_START_GUI_THREAD, MyFrame::OnStartGUIThread)
a4b59324
VZ
349
350 EVT_MENU(THREAD_SHOWCPUS, MyFrame::OnShowCPUs)
351 EVT_MENU(THREAD_ABOUT, MyFrame::OnAbout)
352
ff8151c1 353 EVT_UPDATE_UI(THREAD_START_WORKER, MyFrame::OnUpdateWorker)
d48b06bd 354 EVT_THREAD(WORKER_EVENT, MyFrame::OnWorkerEvent)
3222fde2 355 EVT_IDLE(MyFrame::OnIdle)
82052aff
GL
356END_EVENT_TABLE()
357
3186fa91
FM
358// My frame constructor
359MyFrame::MyFrame(wxFrame *frame, const wxString& title,
360 int x, int y, int w, int h)
361 : wxFrame(frame, wxID_ANY, title, wxPoint(x, y), wxSize(w, h))
82052aff 362{
3186fa91 363 SetIcon(wxIcon(sample_xpm));
3222fde2 364
a6b0bd49 365 // Make a menubar
a4b59324
VZ
366 wxMenuBar *menuBar = new wxMenuBar;
367
368 wxMenu *menuFile = new wxMenu;
ab1ca7b3 369 menuFile->Append(THREAD_CLEAR, _T("&Clear log\tCtrl-L"));
a4b59324 370 menuFile->AppendSeparator();
ab1ca7b3
MB
371 menuFile->Append(THREAD_QUIT, _T("E&xit\tAlt-X"));
372 menuBar->Append(menuFile, _T("&File"));
a4b59324
VZ
373
374 wxMenu *menuThread = new wxMenu;
ab1ca7b3
MB
375 menuThread->Append(THREAD_START_THREAD, _T("&Start a new thread\tCtrl-N"));
376 menuThread->Append(THREAD_START_THREADS, _T("Start &many threads at once"));
ff8151c1 377 menuThread->Append(THREAD_STOP_THREAD, _T("S&top the last spawned thread\tCtrl-S"));
a4b59324 378 menuThread->AppendSeparator();
ff8151c1
FM
379 menuThread->Append(THREAD_PAUSE_THREAD, _T("&Pause the last spawned running thread\tCtrl-P"));
380 menuThread->Append(THREAD_RESUME_THREAD, _T("&Resume the first suspended thread\tCtrl-R"));
a4b59324 381 menuThread->AppendSeparator();
ff8151c1 382 menuThread->Append(THREAD_START_WORKER, _T("Start a &worker thread\tCtrl-W"));
8bf30d0f 383 menuThread->Append(THREAD_EXEC_MAIN, _T("&Launch a program from main thread\tF5"));
c1b8f155 384 menuThread->Append(THREAD_START_GUI_THREAD, _T("Launch a &GUI thread\tF6"));
ab1ca7b3 385 menuBar->Append(menuThread, _T("&Thread"));
a4b59324 386
a4b59324 387 wxMenu *menuHelp = new wxMenu;
ab1ca7b3 388 menuHelp->Append(THREAD_SHOWCPUS, _T("&Show CPU count"));
a4b59324 389 menuHelp->AppendSeparator();
ab1ca7b3
MB
390 menuHelp->Append(THREAD_ABOUT, _T("&About..."));
391 menuBar->Append(menuHelp, _T("&Help"));
a4b59324 392
3186fa91 393 SetMenuBar(menuBar);
d1935bf6 394
7c3d7e2d
VZ
395 m_nRunning = m_nCount = 0;
396
b9de1315
VZ
397 m_dlgProgress = (wxProgressDialog *)NULL;
398
8520f137 399#if wxUSE_STATUSBAR
7c3d7e2d 400 CreateStatusBar(2);
8520f137 401#endif // wxUSE_STATUSBAR
3222fde2 402
bc2ec626 403 m_txtctrl = new wxTextCtrl(this, wxID_ANY, _T(""), wxPoint(0, 0), wxSize(0, 0),
bee503b0 404 wxTE_MULTILINE | wxTE_READONLY);
a6b0bd49 405}
82052aff 406
4e5bbd40
VZ
407MyFrame::~MyFrame()
408{
409 // NB: although the OS will terminate all the threads anyhow when the main
410 // one exits, it's good practice to do it ourselves -- even if it's not
411 // completely trivial in this example
412
413 // tell all the threads to terminate: note that they can't terminate while
414 // we're deleting them because they will block in their OnExit() -- this is
415 // important as otherwise we might access invalid array elements
4e5bbd40 416
4e5bbd40 417 {
abbcf16d 418 wxCriticalSectionLocker locker(wxGetApp().m_critsect);
4e5bbd40 419
abbcf16d
VZ
420 // check if we have any threads running first
421 const wxArrayThread& threads = wxGetApp().m_threads;
422 size_t count = threads.GetCount();
4e5bbd40 423
abbcf16d
VZ
424 if ( !count )
425 return;
4e5bbd40 426
abbcf16d
VZ
427 // set the flag indicating that all threads should exit
428 wxGetApp().m_shuttingDown = true;
4e5bbd40
VZ
429 }
430
abbcf16d
VZ
431 // now wait for them to really terminate
432 wxGetApp().m_semAllDone.Wait();
4e5bbd40
VZ
433}
434
7c3d7e2d 435MyThread *MyFrame::CreateThread()
a6b0bd49
VZ
436{
437 MyThread *thread = new MyThread(this);
3222fde2 438
bf1852e1
VZ
439 if ( thread->Create() != wxTHREAD_NO_ERROR )
440 {
4693b20c 441 wxLogError(wxT("Can't create thread!"));
bf1852e1 442 }
3222fde2 443
1bd3e1ef
GL
444 wxCriticalSectionLocker enter(wxGetApp().m_critsect);
445 wxGetApp().m_threads.Add(thread);
bf1852e1 446
7c3d7e2d
VZ
447 return thread;
448}
449
c1b8f155
FM
450void MyFrame::DoLogThreadMessages()
451{
452 wxCriticalSectionLocker lock(m_csMessages);
453
454 const size_t count = m_messages.size();
455 for ( size_t n = 0; n < count; n++ )
456 {
457 m_txtctrl->AppendText(m_messages[n]);
458 }
459
460 m_messages.clear();
461}
462
463void MyFrame::UpdateThreadStatus()
464{
465 wxCriticalSectionLocker enter(wxGetApp().m_critsect);
466
467 // update the counts of running/total threads
468 size_t nRunning = 0,
469 nCount = wxGetApp().m_threads.Count();
470 for ( size_t n = 0; n < nCount; n++ )
471 {
472 if ( wxGetApp().m_threads[n]->IsRunning() )
473 nRunning++;
474 }
475
476 if ( nCount != m_nCount || nRunning != m_nRunning )
477 {
478 m_nRunning = nRunning;
479 m_nCount = nCount;
480
481 wxLogStatus(this, wxT("%u threads total, %u running."), unsigned(nCount), unsigned(nRunning));
482 }
483 //else: avoid flicker - don't print anything
484}
485
486bool MyFrame::Cancelled()
487{
488 wxCriticalSectionLocker lock(m_csCancelled);
489
490 return m_cancelled;
491}
492
493// ----------------------------------------------------------------------------
494// MyFrame - event handlers
495// ----------------------------------------------------------------------------
496
7c3d7e2d
VZ
497void MyFrame::OnStartThreads(wxCommandEvent& WXUNUSED(event) )
498{
b143cf70 499 static long s_num;
b568d04f 500
ab1ca7b3
MB
501 s_num = wxGetNumberFromUser(_T("How many threads to start: "), _T(""),
502 _T("wxThread sample"), s_num, 1, 10000, this);
b568d04f
VZ
503 if ( s_num == -1 )
504 {
505 s_num = 10;
7c3d7e2d 506
7c3d7e2d 507 return;
b568d04f
VZ
508 }
509
b143cf70 510 unsigned count = unsigned(s_num), n;
7c3d7e2d
VZ
511
512 wxArrayThread threads;
513
514 // first create them all...
515 for ( n = 0; n < count; n++ )
516 {
98f026a6
VZ
517 wxThread *thr = CreateThread();
518
519 // we want to show the effect of SetPriority(): the first thread will
520 // have the lowest priority, the second - the highest, all the rest
521 // the normal one
522 if ( n == 0 )
523 thr->SetPriority(WXTHREAD_MIN_PRIORITY);
524 else if ( n == 1 )
525 thr->SetPriority(WXTHREAD_MAX_PRIORITY);
526 else
527 thr->SetPriority(WXTHREAD_DEFAULT_PRIORITY);
528
529 threads.Add(thr);
7c3d7e2d
VZ
530 }
531
8520f137 532#if wxUSE_STATUSBAR
7c3d7e2d 533 wxString msg;
4693b20c 534 msg.Printf(wxT("%d new threads created."), count);
7c3d7e2d 535 SetStatusText(msg, 1);
8520f137 536#endif // wxUSE_STATUSBAR
7c3d7e2d
VZ
537
538 // ...and then start them
539 for ( n = 0; n < count; n++ )
540 {
541 threads[n]->Run();
542 }
543}
544
545void MyFrame::OnStartThread(wxCommandEvent& WXUNUSED(event) )
546{
547 MyThread *thread = CreateThread();
548
bf1852e1
VZ
549 if ( thread->Run() != wxTHREAD_NO_ERROR )
550 {
4693b20c 551 wxLogError(wxT("Can't start thread!"));
bf1852e1 552 }
7c3d7e2d 553
8520f137 554#if wxUSE_STATUSBAR
ab1ca7b3 555 SetStatusText(_T("New thread started."), 1);
8520f137 556#endif // wxUSE_STATUSBAR
82052aff
GL
557}
558
e3e65dac 559void MyFrame::OnStopThread(wxCommandEvent& WXUNUSED(event) )
82052aff 560{
abbcf16d 561 wxCriticalSectionLocker enter(wxGetApp().m_critsect);
b8b9762a 562
bf1852e1 563 // stop the last thread
1bd3e1ef 564 if ( wxGetApp().m_threads.IsEmpty() )
bee503b0 565 {
4693b20c 566 wxLogError(wxT("No thread to stop!"));
bee503b0 567 }
bf1852e1
VZ
568 else
569 {
abbcf16d 570 wxGetApp().m_threads.Last()->Delete();
7c3d7e2d 571
8520f137 572#if wxUSE_STATUSBAR
abbcf16d 573 SetStatusText(_T("Last thread stopped."), 1);
8520f137 574#endif // wxUSE_STATUSBAR
bf1852e1 575 }
a6b0bd49 576}
82052aff 577
a6b0bd49
VZ
578void MyFrame::OnResumeThread(wxCommandEvent& WXUNUSED(event) )
579{
1bd3e1ef 580 wxCriticalSectionLocker enter(wxGetApp().m_critsect);
bee503b0 581
3222fde2 582 // resume first suspended thread
1bd3e1ef
GL
583 size_t n = 0, count = wxGetApp().m_threads.Count();
584 while ( n < count && !wxGetApp().m_threads[n]->IsPaused() )
bf1852e1 585 n++;
3222fde2 586
bf1852e1 587 if ( n == count )
7c3d7e2d 588 {
4693b20c 589 wxLogError(wxT("No thread to resume!"));
7c3d7e2d 590 }
a6b0bd49 591 else
7c3d7e2d 592 {
1bd3e1ef 593 wxGetApp().m_threads[n]->Resume();
7c3d7e2d 594
8520f137 595#if wxUSE_STATUSBAR
ab1ca7b3 596 SetStatusText(_T("Thread resumed."), 1);
8520f137 597#endif // wxUSE_STATUSBAR
7c3d7e2d 598 }
82052aff
GL
599}
600
e3e65dac 601void MyFrame::OnPauseThread(wxCommandEvent& WXUNUSED(event) )
f3855ef0 602{
1bd3e1ef 603 wxCriticalSectionLocker enter(wxGetApp().m_critsect);
bee503b0 604
3222fde2 605 // pause last running thread
1bd3e1ef
GL
606 int n = wxGetApp().m_threads.Count() - 1;
607 while ( n >= 0 && !wxGetApp().m_threads[n]->IsRunning() )
a6b0bd49 608 n--;
3222fde2 609
a6b0bd49 610 if ( n < 0 )
7c3d7e2d 611 {
4693b20c 612 wxLogError(wxT("No thread to pause!"));
7c3d7e2d 613 }
a6b0bd49 614 else
7c3d7e2d 615 {
1bd3e1ef 616 wxGetApp().m_threads[n]->Pause();
88ac883a 617
8520f137 618#if wxUSE_STATUSBAR
ab1ca7b3 619 SetStatusText(_T("Thread paused."), 1);
8520f137 620#endif // wxUSE_STATUSBAR
7c3d7e2d 621 }
f3855ef0
RR
622}
623
3ccae3ba 624void MyFrame::OnIdle(wxIdleEvent& event)
3a105cbc
VZ
625{
626 DoLogThreadMessages();
627
628 UpdateThreadStatus();
629
630 event.Skip();
631}
632
e3e65dac 633void MyFrame::OnQuit(wxCommandEvent& WXUNUSED(event) )
82052aff 634{
bc2ec626 635 Close(true);
82052aff
GL
636}
637
a4b59324
VZ
638void MyFrame::OnExecMain(wxCommandEvent& WXUNUSED(event))
639{
8bf30d0f
FM
640 wxString cmd = wxGetTextFromUser("Please enter the command to execute",
641 "Enter command",
642#ifdef __WXMSW__
643 "notepad",
644#else
645 "/bin/echo \"Message from another process\"",
646#endif
647 this);
648 if (cmd.IsEmpty())
649 return; // user clicked cancel
a4b59324 650
8bf30d0f
FM
651 wxLogMessage(wxT("The exit code from the main program is %ld"),
652 EXEC(cmd));
a4b59324
VZ
653}
654
655void MyFrame::OnShowCPUs(wxCommandEvent& WXUNUSED(event))
656{
657 wxString msg;
658
659 int nCPUs = wxThread::GetCPUCount();
660 switch ( nCPUs )
661 {
662 case -1:
ab1ca7b3 663 msg = _T("Unknown number of CPUs");
a4b59324
VZ
664 break;
665
666 case 0:
ab1ca7b3 667 msg = _T("WARNING: you're running without any CPUs!");
a4b59324
VZ
668 break;
669
670 case 1:
ab1ca7b3 671 msg = _T("This system only has one CPU.");
a4b59324
VZ
672 break;
673
674 default:
4fce73fc 675 msg.Printf(wxT("This system has %d CPUs"), nCPUs);
a4b59324 676 }
2ab25aca 677
a4b59324
VZ
678 wxLogMessage(msg);
679}
680
e3e65dac 681void MyFrame::OnAbout(wxCommandEvent& WXUNUSED(event) )
82052aff 682{
2ab25aca 683 wxMessageDialog dialog(this,
be5a51fb 684 _T("wxWidgets multithreaded application sample\n")
ab1ca7b3
MB
685 _T("(c) 1998 Julian Smart, Guilhem Lavaux\n")
686 _T("(c) 1999 Vadim Zeitlin\n")
687 _T("(c) 2000 Robert Roebling"),
688 _T("About wxThread sample"),
3222fde2
VZ
689 wxOK | wxICON_INFORMATION);
690
a6b0bd49 691 dialog.ShowModal();
82052aff
GL
692}
693
bee503b0
VZ
694void MyFrame::OnClear(wxCommandEvent& WXUNUSED(event))
695{
696 m_txtctrl->Clear();
697}
ce6d2511 698
b9de1315
VZ
699void MyFrame::OnUpdateWorker(wxUpdateUIEvent& event)
700{
701 event.Enable( m_dlgProgress == NULL );
702}
703
ce6d2511
RR
704void MyFrame::OnStartWorker(wxCommandEvent& WXUNUSED(event))
705{
706 MyWorkerThread *thread = new MyWorkerThread(this);
707
708 if ( thread->Create() != wxTHREAD_NO_ERROR )
709 {
4693b20c 710 wxLogError(wxT("Can't create thread!"));
5ac52b5d 711 return;
ce6d2511 712 }
b9de1315
VZ
713
714 m_dlgProgress = new wxProgressDialog
715 (
ab1ca7b3
MB
716 _T("Progress dialog"),
717 _T("Wait until the thread terminates or press [Cancel]"),
b9de1315
VZ
718 100,
719 this,
720 wxPD_CAN_ABORT |
721 wxPD_APP_MODAL |
722 wxPD_ELAPSED_TIME |
723 wxPD_ESTIMATED_TIME |
724 wxPD_REMAINING_TIME
725 );
726
727 // thread is not running yet, no need for crit sect
bc2ec626 728 m_cancelled = false;
b9de1315 729
ce6d2511
RR
730 thread->Run();
731}
732
d48b06bd 733void MyFrame::OnWorkerEvent(wxThreadEvent& event)
ce6d2511 734{
b9de1315
VZ
735 int n = event.GetInt();
736 if ( n == -1 )
737 {
738 m_dlgProgress->Destroy();
739 m_dlgProgress = (wxProgressDialog *)NULL;
740
741 // the dialog is aborted because the event came from another thread, so
742 // we may need to wake up the main event loop for the dialog to be
743 // really closed
744 wxWakeUpIdle();
745 }
746 else
747 {
748 if ( !m_dlgProgress->Update(n) )
749 {
c1b8f155 750 wxCriticalSectionLocker lock(m_csCancelled);
b9de1315 751
bc2ec626 752 m_cancelled = true;
b9de1315
VZ
753 }
754 }
ce6d2511
RR
755}
756
c1b8f155 757void MyFrame::OnStartGUIThread(wxCommandEvent& WXUNUSED(event))
b9de1315 758{
c1b8f155 759 MyImageDialog dlg(this);
b9de1315 760
c1b8f155
FM
761 dlg.ShowModal();
762}
763
764
765// ----------------------------------------------------------------------------
766// MyImageDialog
767// ----------------------------------------------------------------------------
768
769BEGIN_EVENT_TABLE(MyImageDialog, wxDialog)
770 EVT_THREAD(GUITHREAD_EVENT, MyImageDialog::OnGUIThreadEvent)
771 EVT_PAINT(MyImageDialog::OnPaint)
772END_EVENT_TABLE()
773
774MyImageDialog::MyImageDialog(wxFrame *parent)
775 : wxDialog(parent, wxID_ANY, "Image created by a secondary thread",
776 wxDefaultPosition, wxSize(GUITHREAD_BMP_SIZE,GUITHREAD_BMP_SIZE)*1.5, wxDEFAULT_DIALOG_STYLE),
777 m_thread(this)
778{
779 m_nCurrentProgress = 0;
780
781 CentreOnScreen();
782
783 // NOTE: no need to lock m_csBmp until the thread isn't started:
784
785 // create the bitmap
786 if (!m_bmp.Create(GUITHREAD_BMP_SIZE,GUITHREAD_BMP_SIZE) || !m_bmp.IsOk())
787 {
788 wxLogError("Couldn't create the bitmap!");
789 return;
790 }
791
792 // clean it
793 wxMemoryDC dc(m_bmp);
794 dc.SetBackground(*wxBLACK_BRUSH);
795 dc.Clear();
796
797 // draw the bitmap from a secondary thread
798 if ( m_thread.Create() != wxTHREAD_NO_ERROR ||
799 m_thread.Run() != wxTHREAD_NO_ERROR )
800 {
801 wxLogError(wxT("Can't create/run thread!"));
802 return;
803 }
804}
805
806MyImageDialog::~MyImageDialog()
807{
808 // in case our thread is still running and for some reason we are destroyed,
809 // do wait for the thread to complete as it assumes that its MyImageDialog
810 // pointer is always valid
811 m_thread.Delete();
b9de1315 812}
3186fa91 813
c1b8f155
FM
814void MyImageDialog::OnGUIThreadEvent(wxThreadEvent& event)
815{
816 m_nCurrentProgress = int(((float)event.GetInt()*100)/GUITHREAD_NUM_UPDATES);
817
818 Refresh();
819}
820
821void MyImageDialog::OnPaint(wxPaintEvent& WXUNUSED(evt))
822{
823 wxPaintDC dc(this);
824
825 const wxSize& sz = dc.GetSize();
826
827 {
828 // paint the bitmap
829 wxCriticalSectionLocker locker(m_csBmp);
830 dc.DrawBitmap(m_bmp, (sz.GetWidth()-GUITHREAD_BMP_SIZE)/2,
831 (sz.GetHeight()-GUITHREAD_BMP_SIZE)/2);
832 }
833
834 // paint a sort of progress bar with a 10px border:
835 dc.SetBrush(*wxRED_BRUSH);
9425b04c 836 dc.DrawRectangle(10,10, m_nCurrentProgress*(sz.GetWidth()-20)/100,30);
c1b8f155
FM
837 dc.SetTextForeground(*wxBLUE);
838 dc.DrawText(wxString::Format("%d%%", m_nCurrentProgress),
839 (sz.GetWidth()-dc.GetCharWidth()*2)/2,
840 25-dc.GetCharHeight()/2);
841}
3186fa91
FM
842
843// ----------------------------------------------------------------------------
844// MyThread
845// ----------------------------------------------------------------------------
846
847MyThread::MyThread(MyFrame *frame)
848 : wxThread()
849{
850 m_count = 0;
851 m_frame = frame;
852}
853
854MyThread::~MyThread()
855{
856 wxCriticalSectionLocker locker(wxGetApp().m_critsect);
857
858 wxArrayThread& threads = wxGetApp().m_threads;
859 threads.Remove(this);
860
861 if ( threads.IsEmpty() )
862 {
863 // signal the main thread that there are no more threads left if it is
864 // waiting for us
865 if ( wxGetApp().m_shuttingDown )
866 {
867 wxGetApp().m_shuttingDown = false;
868
869 wxGetApp().m_semAllDone.Post();
870 }
871 }
872}
873
c1b8f155 874wxThread::ExitCode MyThread::Entry()
3186fa91
FM
875{
876 wxString text;
877
ff8151c1 878 text.Printf(wxT("Thread %p started (priority = %u).\n"),
3186fa91
FM
879 GetId(), GetPriority());
880 WriteText(text);
881 // wxLogMessage(text); -- test wxLog thread safeness
882
883 for ( m_count = 0; m_count < 10; m_count++ )
884 {
885 // check if the application is shutting down: in this case all threads
886 // should stop a.s.a.p.
887 {
888 wxCriticalSectionLocker locker(wxGetApp().m_critsect);
889 if ( wxGetApp().m_shuttingDown )
890 return NULL;
891 }
892
893 // check if just this thread was asked to exit
894 if ( TestDestroy() )
895 break;
896
ff8151c1 897 text.Printf(wxT("[%u] Thread %p here.\n"), m_count, GetId());
3186fa91
FM
898 WriteText(text);
899
900 // wxSleep() can't be called from non-GUI thread!
901 wxThread::Sleep(1000);
902 }
903
ff8151c1 904 text.Printf(wxT("Thread %p finished.\n"), GetId());
3186fa91
FM
905 WriteText(text);
906 // wxLogMessage(text); -- test wxLog thread safeness
907
908 return NULL;
909}
910
911
912// ----------------------------------------------------------------------------
913// MyWorkerThread
914// ----------------------------------------------------------------------------
915
c1b8f155
FM
916// define this symbol to 1 to test if the YieldFor() call in the wxProgressDialog::Update
917// function provokes a race condition in which the second wxThreadEvent posted by
918// MyWorkerThread::Entry is processed by the YieldFor() call of wxProgressDialog::Update
919// and results in the destruction of the progress dialog itself, resulting in a crash later.
920#define TEST_YIELD_RACE_CONDITION 0
921
3186fa91
FM
922MyWorkerThread::MyWorkerThread(MyFrame *frame)
923 : wxThread()
924{
925 m_frame = frame;
926 m_count = 0;
927}
928
929void MyWorkerThread::OnExit()
930{
931}
932
c1b8f155 933wxThread::ExitCode MyWorkerThread::Entry()
3186fa91
FM
934{
935#if TEST_YIELD_RACE_CONDITION
936 if ( TestDestroy() )
937 return NULL;
938
939 wxThreadEvent event( wxEVT_COMMAND_THREAD, WORKER_EVENT );
940
941 event.SetInt( 50 );
942 wxQueueEvent( m_frame, event.Clone() );
943
944 event.SetInt(-1);
945 wxQueueEvent( m_frame, event.Clone() );
946#else
947 for ( m_count = 0; !m_frame->Cancelled() && (m_count < 100); m_count++ )
948 {
949 // check if we were asked to exit
950 if ( TestDestroy() )
951 break;
952
953 // create any type of command event here
954 wxThreadEvent event( wxEVT_COMMAND_THREAD, WORKER_EVENT );
955 event.SetInt( m_count );
956
957 // send in a thread-safe way
958 wxQueueEvent( m_frame, event.Clone() );
959
960 wxMilliSleep(200);
961 }
962
963 wxThreadEvent event( wxEVT_COMMAND_THREAD, WORKER_EVENT );
964 event.SetInt(-1); // that's all
965 wxQueueEvent( m_frame, event.Clone() );
966#endif
967
968 return NULL;
969}
970
c1b8f155
FM
971
972// ----------------------------------------------------------------------------
973// MyGUIThread
974// ----------------------------------------------------------------------------
975
976wxThread::ExitCode MyGUIThread::Entry()
977{
978 for (int i=0; i<GUITHREAD_NUM_UPDATES && !TestDestroy(); i++)
979 {
980 // inform the GUI toolkit that we're going to use GUI functions
981 // from a secondary thread:
982 wxMutexGuiEnter();
983
984 {
985 wxCriticalSectionLocker lock(m_dlg->m_csBmp);
986
987 // draw some more stuff on the bitmap
988 wxMemoryDC dc(m_dlg->m_bmp);
989 dc.SetBrush((i%2)==0 ? *wxBLUE_BRUSH : *wxGREEN_BRUSH);
990 dc.DrawRectangle(rand()%GUITHREAD_BMP_SIZE, rand()%GUITHREAD_BMP_SIZE, 30, 30);
991
992 // simulate long drawing time:
993 wxMilliSleep(200);
994 }
995
996 // if we don't release the GUI mutex the MyImageDialog won't be able to refresh
997 wxMutexGuiLeave();
998
999 // notify the dialog that another piece of our masterpiece is complete:
1000 wxThreadEvent event( wxEVT_COMMAND_THREAD, GUITHREAD_EVENT );
9425b04c 1001 event.SetInt(i+1);
c1b8f155
FM
1002 wxQueueEvent( m_dlg, event.Clone() );
1003
1004 // give the main thread the time to refresh before we lock the GUI mutex again
1005 // FIXME: find a better way to do this!
1006 wxMilliSleep(100);
1007 }
1008
1009 return (ExitCode)0;
1010}