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