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