]> git.saurik.com Git - wxWidgets.git/blame - samples/thread/thread.cpp
fixing incorrect scrolling - which happened at least under OSX ...
[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 )
562 thr->SetPriority(WXTHREAD_MIN_PRIORITY);
563 else if ( n == 1 )
564 thr->SetPriority(WXTHREAD_MAX_PRIORITY);
565 else
566 thr->SetPriority(WXTHREAD_DEFAULT_PRIORITY);
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{
abbcf16d 600 wxCriticalSectionLocker enter(wxGetApp().m_critsect);
b8b9762a 601
bf1852e1 602 // stop the last thread
1bd3e1ef 603 if ( wxGetApp().m_threads.IsEmpty() )
bee503b0 604 {
4693b20c 605 wxLogError(wxT("No thread to stop!"));
bee503b0 606 }
bf1852e1
VZ
607 else
608 {
abbcf16d 609 wxGetApp().m_threads.Last()->Delete();
7c3d7e2d 610
8520f137 611#if wxUSE_STATUSBAR
9a83f860 612 SetStatusText(wxT("Last thread stopped."), 1);
8520f137 613#endif // wxUSE_STATUSBAR
bf1852e1 614 }
a6b0bd49 615}
82052aff 616
a6b0bd49
VZ
617void MyFrame::OnResumeThread(wxCommandEvent& WXUNUSED(event) )
618{
1bd3e1ef 619 wxCriticalSectionLocker enter(wxGetApp().m_critsect);
bee503b0 620
3222fde2 621 // resume first suspended thread
1bd3e1ef
GL
622 size_t n = 0, count = wxGetApp().m_threads.Count();
623 while ( n < count && !wxGetApp().m_threads[n]->IsPaused() )
bf1852e1 624 n++;
3222fde2 625
bf1852e1 626 if ( n == count )
7c3d7e2d 627 {
4693b20c 628 wxLogError(wxT("No thread to resume!"));
7c3d7e2d 629 }
a6b0bd49 630 else
7c3d7e2d 631 {
1bd3e1ef 632 wxGetApp().m_threads[n]->Resume();
7c3d7e2d 633
8520f137 634#if wxUSE_STATUSBAR
9a83f860 635 SetStatusText(wxT("Thread resumed."), 1);
8520f137 636#endif // wxUSE_STATUSBAR
7c3d7e2d 637 }
82052aff
GL
638}
639
e3e65dac 640void MyFrame::OnPauseThread(wxCommandEvent& WXUNUSED(event) )
f3855ef0 641{
1bd3e1ef 642 wxCriticalSectionLocker enter(wxGetApp().m_critsect);
bee503b0 643
3222fde2 644 // pause last running thread
1bd3e1ef
GL
645 int n = wxGetApp().m_threads.Count() - 1;
646 while ( n >= 0 && !wxGetApp().m_threads[n]->IsRunning() )
a6b0bd49 647 n--;
3222fde2 648
a6b0bd49 649 if ( n < 0 )
7c3d7e2d 650 {
4693b20c 651 wxLogError(wxT("No thread to pause!"));
7c3d7e2d 652 }
a6b0bd49 653 else
7c3d7e2d 654 {
1bd3e1ef 655 wxGetApp().m_threads[n]->Pause();
88ac883a 656
8520f137 657#if wxUSE_STATUSBAR
9a83f860 658 SetStatusText(wxT("Thread paused."), 1);
8520f137 659#endif // wxUSE_STATUSBAR
7c3d7e2d 660 }
f3855ef0
RR
661}
662
3ccae3ba 663void MyFrame::OnIdle(wxIdleEvent& event)
3a105cbc 664{
3a105cbc
VZ
665 UpdateThreadStatus();
666
667 event.Skip();
668}
669
e3e65dac 670void MyFrame::OnQuit(wxCommandEvent& WXUNUSED(event) )
82052aff 671{
bc2ec626 672 Close(true);
82052aff
GL
673}
674
a4b59324
VZ
675void MyFrame::OnExecMain(wxCommandEvent& WXUNUSED(event))
676{
8bf30d0f
FM
677 wxString cmd = wxGetTextFromUser("Please enter the command to execute",
678 "Enter command",
679#ifdef __WXMSW__
680 "notepad",
681#else
682 "/bin/echo \"Message from another process\"",
683#endif
684 this);
685 if (cmd.IsEmpty())
686 return; // user clicked cancel
a4b59324 687
8bf30d0f
FM
688 wxLogMessage(wxT("The exit code from the main program is %ld"),
689 EXEC(cmd));
a4b59324
VZ
690}
691
692void MyFrame::OnShowCPUs(wxCommandEvent& WXUNUSED(event))
693{
694 wxString msg;
695
696 int nCPUs = wxThread::GetCPUCount();
697 switch ( nCPUs )
698 {
699 case -1:
9a83f860 700 msg = wxT("Unknown number of CPUs");
a4b59324
VZ
701 break;
702
703 case 0:
9a83f860 704 msg = wxT("WARNING: you're running without any CPUs!");
a4b59324
VZ
705 break;
706
707 case 1:
9a83f860 708 msg = wxT("This system only has one CPU.");
a4b59324
VZ
709 break;
710
711 default:
4fce73fc 712 msg.Printf(wxT("This system has %d CPUs"), nCPUs);
a4b59324 713 }
2ab25aca 714
a4b59324
VZ
715 wxLogMessage(msg);
716}
717
e3e65dac 718void MyFrame::OnAbout(wxCommandEvent& WXUNUSED(event) )
82052aff 719{
2ab25aca 720 wxMessageDialog dialog(this,
9a83f860
VZ
721 wxT("wxWidgets multithreaded application sample\n")
722 wxT("(c) 1998 Julian Smart, Guilhem Lavaux\n")
723 wxT("(c) 2000 Robert Roebling\n")
724 wxT("(c) 1999,2009 Vadim Zeitlin"),
725 wxT("About wxThread sample"),
3222fde2
VZ
726 wxOK | wxICON_INFORMATION);
727
a6b0bd49 728 dialog.ShowModal();
82052aff
GL
729}
730
bee503b0
VZ
731void MyFrame::OnClear(wxCommandEvent& WXUNUSED(event))
732{
733 m_txtctrl->Clear();
734}
ce6d2511 735
b9de1315
VZ
736void MyFrame::OnUpdateWorker(wxUpdateUIEvent& event)
737{
738 event.Enable( m_dlgProgress == NULL );
739}
740
ce6d2511
RR
741void MyFrame::OnStartWorker(wxCommandEvent& WXUNUSED(event))
742{
743 MyWorkerThread *thread = new MyWorkerThread(this);
744
745 if ( thread->Create() != wxTHREAD_NO_ERROR )
746 {
4693b20c 747 wxLogError(wxT("Can't create thread!"));
5ac52b5d 748 return;
ce6d2511 749 }
b9de1315
VZ
750
751 m_dlgProgress = new wxProgressDialog
752 (
9a83f860
VZ
753 wxT("Progress dialog"),
754 wxT("Wait until the thread terminates or press [Cancel]"),
b9de1315
VZ
755 100,
756 this,
757 wxPD_CAN_ABORT |
758 wxPD_APP_MODAL |
759 wxPD_ELAPSED_TIME |
760 wxPD_ESTIMATED_TIME |
761 wxPD_REMAINING_TIME
762 );
763
764 // thread is not running yet, no need for crit sect
bc2ec626 765 m_cancelled = false;
b9de1315 766
ce6d2511
RR
767 thread->Run();
768}
769
d48b06bd 770void MyFrame::OnWorkerEvent(wxThreadEvent& event)
ce6d2511 771{
b9de1315
VZ
772 int n = event.GetInt();
773 if ( n == -1 )
774 {
775 m_dlgProgress->Destroy();
776 m_dlgProgress = (wxProgressDialog *)NULL;
777
778 // the dialog is aborted because the event came from another thread, so
779 // we may need to wake up the main event loop for the dialog to be
780 // really closed
781 wxWakeUpIdle();
782 }
783 else
784 {
785 if ( !m_dlgProgress->Update(n) )
786 {
c1b8f155 787 wxCriticalSectionLocker lock(m_csCancelled);
b9de1315 788
bc2ec626 789 m_cancelled = true;
b9de1315
VZ
790 }
791 }
ce6d2511
RR
792}
793
c1b8f155 794void MyFrame::OnStartGUIThread(wxCommandEvent& WXUNUSED(event))
b9de1315 795{
53ff8df7
VZ
796 // we use this to check that disabling logging only affects the main thread
797 // but the messages from the worker thread will still be logged
798 wxLogNull noLog;
799 wxLogMessage("You shouldn't see this message because of wxLogNull");
800
c1b8f155 801 MyImageDialog dlg(this);
b9de1315 802
c1b8f155
FM
803 dlg.ShowModal();
804}
805
806
807// ----------------------------------------------------------------------------
808// MyImageDialog
809// ----------------------------------------------------------------------------
810
811BEGIN_EVENT_TABLE(MyImageDialog, wxDialog)
812 EVT_THREAD(GUITHREAD_EVENT, MyImageDialog::OnGUIThreadEvent)
813 EVT_PAINT(MyImageDialog::OnPaint)
814END_EVENT_TABLE()
815
816MyImageDialog::MyImageDialog(wxFrame *parent)
817 : wxDialog(parent, wxID_ANY, "Image created by a secondary thread",
818 wxDefaultPosition, wxSize(GUITHREAD_BMP_SIZE,GUITHREAD_BMP_SIZE)*1.5, wxDEFAULT_DIALOG_STYLE),
819 m_thread(this)
820{
821 m_nCurrentProgress = 0;
822
823 CentreOnScreen();
824
825 // NOTE: no need to lock m_csBmp until the thread isn't started:
826
827 // create the bitmap
828 if (!m_bmp.Create(GUITHREAD_BMP_SIZE,GUITHREAD_BMP_SIZE) || !m_bmp.IsOk())
829 {
830 wxLogError("Couldn't create the bitmap!");
831 return;
832 }
833
834 // clean it
835 wxMemoryDC dc(m_bmp);
836 dc.SetBackground(*wxBLACK_BRUSH);
837 dc.Clear();
838
839 // draw the bitmap from a secondary thread
840 if ( m_thread.Create() != wxTHREAD_NO_ERROR ||
841 m_thread.Run() != wxTHREAD_NO_ERROR )
842 {
843 wxLogError(wxT("Can't create/run thread!"));
844 return;
845 }
846}
847
848MyImageDialog::~MyImageDialog()
849{
850 // in case our thread is still running and for some reason we are destroyed,
851 // do wait for the thread to complete as it assumes that its MyImageDialog
852 // pointer is always valid
853 m_thread.Delete();
b9de1315 854}
3186fa91 855
c1b8f155
FM
856void MyImageDialog::OnGUIThreadEvent(wxThreadEvent& event)
857{
858 m_nCurrentProgress = int(((float)event.GetInt()*100)/GUITHREAD_NUM_UPDATES);
859
860 Refresh();
861}
862
863void MyImageDialog::OnPaint(wxPaintEvent& WXUNUSED(evt))
864{
865 wxPaintDC dc(this);
866
867 const wxSize& sz = dc.GetSize();
868
869 {
870 // paint the bitmap
871 wxCriticalSectionLocker locker(m_csBmp);
872 dc.DrawBitmap(m_bmp, (sz.GetWidth()-GUITHREAD_BMP_SIZE)/2,
873 (sz.GetHeight()-GUITHREAD_BMP_SIZE)/2);
874 }
875
876 // paint a sort of progress bar with a 10px border:
877 dc.SetBrush(*wxRED_BRUSH);
9425b04c 878 dc.DrawRectangle(10,10, m_nCurrentProgress*(sz.GetWidth()-20)/100,30);
c1b8f155
FM
879 dc.SetTextForeground(*wxBLUE);
880 dc.DrawText(wxString::Format("%d%%", m_nCurrentProgress),
881 (sz.GetWidth()-dc.GetCharWidth()*2)/2,
882 25-dc.GetCharHeight()/2);
883}
3186fa91
FM
884
885// ----------------------------------------------------------------------------
886// MyThread
887// ----------------------------------------------------------------------------
888
232addd1 889MyThread::MyThread()
3186fa91
FM
890 : wxThread()
891{
892 m_count = 0;
3186fa91
FM
893}
894
895MyThread::~MyThread()
896{
897 wxCriticalSectionLocker locker(wxGetApp().m_critsect);
898
899 wxArrayThread& threads = wxGetApp().m_threads;
900 threads.Remove(this);
901
902 if ( threads.IsEmpty() )
903 {
904 // signal the main thread that there are no more threads left if it is
905 // waiting for us
906 if ( wxGetApp().m_shuttingDown )
907 {
908 wxGetApp().m_shuttingDown = false;
909
910 wxGetApp().m_semAllDone.Post();
911 }
912 }
913}
914
c1b8f155 915wxThread::ExitCode MyThread::Entry()
3186fa91 916{
232addd1 917 wxLogMessage("Thread started (priority = %u).", GetPriority());
3186fa91
FM
918
919 for ( m_count = 0; m_count < 10; m_count++ )
920 {
921 // check if the application is shutting down: in this case all threads
922 // should stop a.s.a.p.
923 {
924 wxCriticalSectionLocker locker(wxGetApp().m_critsect);
925 if ( wxGetApp().m_shuttingDown )
926 return NULL;
927 }
928
929 // check if just this thread was asked to exit
930 if ( TestDestroy() )
931 break;
932
232addd1 933 wxLogMessage("Thread progress: %u", m_count);
3186fa91
FM
934
935 // wxSleep() can't be called from non-GUI thread!
936 wxThread::Sleep(1000);
937 }
938
232addd1 939 wxLogMessage("Thread finished.");
3186fa91
FM
940
941 return NULL;
942}
943
944
945// ----------------------------------------------------------------------------
946// MyWorkerThread
947// ----------------------------------------------------------------------------
948
c1b8f155
FM
949// define this symbol to 1 to test if the YieldFor() call in the wxProgressDialog::Update
950// function provokes a race condition in which the second wxThreadEvent posted by
951// MyWorkerThread::Entry is processed by the YieldFor() call of wxProgressDialog::Update
952// and results in the destruction of the progress dialog itself, resulting in a crash later.
953#define TEST_YIELD_RACE_CONDITION 0
954
3186fa91
FM
955MyWorkerThread::MyWorkerThread(MyFrame *frame)
956 : wxThread()
957{
958 m_frame = frame;
959 m_count = 0;
960}
961
962void MyWorkerThread::OnExit()
963{
964}
965
c1b8f155 966wxThread::ExitCode MyWorkerThread::Entry()
3186fa91
FM
967{
968#if TEST_YIELD_RACE_CONDITION
969 if ( TestDestroy() )
970 return NULL;
971
c1b293bb 972 wxThreadEvent event( wxEVT_THREAD, WORKER_EVENT );
3186fa91
FM
973
974 event.SetInt( 50 );
975 wxQueueEvent( m_frame, event.Clone() );
976
977 event.SetInt(-1);
978 wxQueueEvent( m_frame, event.Clone() );
979#else
980 for ( m_count = 0; !m_frame->Cancelled() && (m_count < 100); m_count++ )
981 {
982 // check if we were asked to exit
983 if ( TestDestroy() )
984 break;
985
986 // create any type of command event here
c1b293bb 987 wxThreadEvent event( wxEVT_THREAD, WORKER_EVENT );
3186fa91
FM
988 event.SetInt( m_count );
989
990 // send in a thread-safe way
991 wxQueueEvent( m_frame, event.Clone() );
992
993 wxMilliSleep(200);
994 }
995
c1b293bb 996 wxThreadEvent event( wxEVT_THREAD, WORKER_EVENT );
3186fa91
FM
997 event.SetInt(-1); // that's all
998 wxQueueEvent( m_frame, event.Clone() );
999#endif
1000
1001 return NULL;
1002}
1003
c1b8f155
FM
1004
1005// ----------------------------------------------------------------------------
1006// MyGUIThread
1007// ----------------------------------------------------------------------------
1008
1009wxThread::ExitCode MyGUIThread::Entry()
1010{
53ff8df7
VZ
1011 // uncomment this to check that disabling logging here does disable it for
1012 // this thread -- but not the main one if you also comment out wxLogNull
1013 // line in MyFrame::OnStartGUIThread()
1014 //wxLogNull noLog;
1015
acad886c
VZ
1016 // this goes to the main window
1017 wxLogMessage("GUI thread starting");
1018
1019 // use a thread-specific log target for this thread to show that its
1020 // messages don't appear in the main window while it runs
1021 wxLogBuffer logBuf;
1022 wxLog::SetThreadActiveTarget(&logBuf);
1023
c1b8f155
FM
1024 for (int i=0; i<GUITHREAD_NUM_UPDATES && !TestDestroy(); i++)
1025 {
1026 // inform the GUI toolkit that we're going to use GUI functions
1027 // from a secondary thread:
1028 wxMutexGuiEnter();
1029
1030 {
1031 wxCriticalSectionLocker lock(m_dlg->m_csBmp);
1032
1033 // draw some more stuff on the bitmap
1034 wxMemoryDC dc(m_dlg->m_bmp);
1035 dc.SetBrush((i%2)==0 ? *wxBLUE_BRUSH : *wxGREEN_BRUSH);
1036 dc.DrawRectangle(rand()%GUITHREAD_BMP_SIZE, rand()%GUITHREAD_BMP_SIZE, 30, 30);
1037
1038 // simulate long drawing time:
1039 wxMilliSleep(200);
1040 }
1041
1042 // if we don't release the GUI mutex the MyImageDialog won't be able to refresh
1043 wxMutexGuiLeave();
1044
1045 // notify the dialog that another piece of our masterpiece is complete:
c1b293bb 1046 wxThreadEvent event( wxEVT_THREAD, GUITHREAD_EVENT );
9425b04c 1047 event.SetInt(i+1);
c1b8f155
FM
1048 wxQueueEvent( m_dlg, event.Clone() );
1049
acad886c
VZ
1050 if ( !((i + 1) % 10) )
1051 {
1052 // this message will go to the buffer
1053 wxLogMessage("Step #%d.", i + 1);
1054 }
1055
c1b8f155
FM
1056 // give the main thread the time to refresh before we lock the GUI mutex again
1057 // FIXME: find a better way to do this!
1058 wxMilliSleep(100);
1059 }
1060
acad886c
VZ
1061 // now remove the thread-specific thread target
1062 wxLog::SetThreadActiveTarget(NULL);
1063
1064 // so that this goes to the main window again
1065 wxLogMessage("GUI thread finished.");
1066
c1b8f155
FM
1067 return (ExitCode)0;
1068}