]> git.saurik.com Git - wxWidgets.git/blob - samples/thread/thread.cpp
forgot to check-in wxGTK wxDC changes
[wxWidgets.git] / samples / thread / thread.cpp
1 /////////////////////////////////////////////////////////////////////////////
2 // Name: thread.cpp
3 // Purpose: wxWindows thread sample
4 // Author: Julian Smart(minimal)/Guilhem Lavaux(thread test)
5 // Modified by:
6 // Created: 06/16/98
7 // RCS-ID: $Id$
8 // Copyright: (c) Julian Smart, Markus Holzem, Guilhem Lavaux
9 // Licence: wxWindows license
10 /////////////////////////////////////////////////////////////////////////////
11
12 /*
13 TODO: use worker threads to update progress controls instead of writing
14 messages - it will be more visual
15 */
16
17 // For compilers that support precompilation, includes "wx/wx.h".
18 #include "wx/wxprec.h"
19
20 #ifdef __BORLANDC__
21 #pragma hdrstop
22 #endif
23
24 #ifndef WX_PRECOMP
25 #include "wx/wx.h"
26 #endif
27
28 #if !wxUSE_THREADS
29 #error "This sample requires thread support!"
30 #endif // wxUSE_THREADS
31
32 #include "wx/thread.h"
33 #include "wx/dynarray.h"
34 #include "wx/time.h"
35
36 #include "wx/progdlg.h"
37
38 #ifdef USE_EXECUTE
39 #define EXEC(cmd) wxExecute((cmd), wxEXEC_SYNC)
40 #else
41 #define EXEC(cmd) system(cmd)
42 #endif
43
44 class MyThread;
45 WX_DEFINE_ARRAY(wxThread *, wxArrayThread);
46
47 // Define a new application type
48 class MyApp : public wxApp
49 {
50 public:
51 virtual bool OnInit();
52
53 public:
54 // all the threads currently alive - as soon as the thread terminates, it's
55 // removed from the array
56 wxArrayThread m_threads;
57
58 // crit section protects access to all of the arrays below
59 wxCriticalSection m_critsect;
60 };
61
62 // Create a new application object
63 IMPLEMENT_APP(MyApp)
64
65 // Define a new frame type
66 class MyFrame: public wxFrame
67 {
68 public:
69 // ctor
70 MyFrame(wxFrame *frame, const wxString& title, int x, int y, int w, int h);
71
72 // operations
73 void WriteText(const wxString& text) { m_txtctrl->WriteText(text); }
74
75 // accessors for MyWorkerThread (called in its context!)
76 bool Cancelled();
77
78 // callbacks
79 void OnQuit(wxCommandEvent& event);
80 void OnClear(wxCommandEvent& event);
81
82 void OnStartThread(wxCommandEvent& event);
83 void OnStartThreads(wxCommandEvent& event);
84 void OnStopThread(wxCommandEvent& event);
85 void OnPauseThread(wxCommandEvent& event);
86 void OnResumeThread(wxCommandEvent& event);
87
88 void OnStartWorker(wxCommandEvent& event);
89 void OnWorkerEvent(wxCommandEvent& event);
90 void OnUpdateWorker(wxUpdateUIEvent& event);
91
92 void OnExecMain(wxCommandEvent& event);
93 void OnExecThread(wxCommandEvent& event);
94
95 void OnShowCPUs(wxCommandEvent& event);
96 void OnAbout(wxCommandEvent& event);
97
98 void OnIdle(wxIdleEvent &event);
99
100 private:
101 // helper function - creates a new thread (but doesn't run it)
102 MyThread *CreateThread();
103
104 // just some place to put our messages in
105 wxTextCtrl *m_txtctrl;
106
107 // remember the number of running threads and total number of threads
108 size_t m_nRunning, m_nCount;
109
110 // the progress dialog which we show while worker thread is running
111 wxProgressDialog *m_dlgProgress;
112
113 // was the worker thread cancelled by user?
114 bool m_cancelled;
115
116 // protects m_cancelled
117 wxCriticalSection m_critsectWork;
118
119 DECLARE_EVENT_TABLE()
120 };
121
122 // ID for the menu commands
123 enum
124 {
125 THREAD_QUIT = 1,
126 THREAD_TEXT = 101,
127 THREAD_CLEAR,
128 THREAD_START_THREAD = 201,
129 THREAD_START_THREADS,
130 THREAD_STOP_THREAD,
131 THREAD_PAUSE_THREAD,
132 THREAD_RESUME_THREAD,
133 THREAD_START_WORKER,
134
135 THREAD_EXEC_MAIN,
136 THREAD_EXEC_THREAD,
137
138 THREAD_SHOWCPUS,
139 THREAD_ABOUT,
140
141 WORKER_EVENT // this one gets sent from the worker thread
142 };
143
144 // ----------------------------------------------------------------------------
145 // GUI thread
146 // ----------------------------------------------------------------------------
147
148 class MyThread : public wxThread
149 {
150 public:
151 MyThread(MyFrame *frame);
152
153 // thread execution starts here
154 virtual void *Entry();
155
156 // called when the thread exits - whether it terminates normally or is
157 // stopped with Delete() (but not when it is Kill()ed!)
158 virtual void OnExit();
159
160 // write something to the text control
161 void WriteText(const wxString& text);
162
163 public:
164 size_t m_count;
165 MyFrame *m_frame;
166 };
167
168 MyThread::MyThread(MyFrame *frame)
169 : wxThread()
170 {
171 m_count = 0;
172 m_frame = frame;
173 }
174
175 void MyThread::WriteText(const wxString& text)
176 {
177 wxString msg;
178
179 // before doing any GUI calls we must ensure that this thread is the only
180 // one doing it!
181
182 wxMutexGuiEnter();
183
184 msg << text;
185 m_frame->WriteText(msg);
186
187 wxMutexGuiLeave();
188 }
189
190 void MyThread::OnExit()
191 {
192 wxCriticalSectionLocker locker(wxGetApp().m_critsect);
193
194 wxGetApp().m_threads.Remove(this);
195 }
196
197 void *MyThread::Entry()
198 {
199 wxString text;
200
201 text.Printf(wxT("Thread 0x%x started (priority = %d).\n"),
202 GetId(), GetPriority());
203 WriteText(text);
204 // wxLogMessage(text); -- test wxLog thread safeness
205
206 for ( m_count = 0; m_count < 10; m_count++ )
207 {
208 // check if we were asked to exit
209 if ( TestDestroy() )
210 break;
211
212 text.Printf(wxT("[%u] Thread 0x%x here.\n"), m_count, GetId());
213 WriteText(text);
214
215 // wxSleep() can't be called from non-GUI thread!
216 wxThread::Sleep(1000);
217 }
218
219 text.Printf(wxT("Thread 0x%x finished.\n"), GetId());
220 WriteText(text);
221 // wxLogMessage(text); -- test wxLog thread safeness
222
223 return NULL;
224 }
225
226 // ----------------------------------------------------------------------------
227 // worker thread
228 // ----------------------------------------------------------------------------
229
230 class MyWorkerThread : public wxThread
231 {
232 public:
233 MyWorkerThread(MyFrame *frame);
234
235 // thread execution starts here
236 virtual void *Entry();
237
238 // called when the thread exits - whether it terminates normally or is
239 // stopped with Delete() (but not when it is Kill()ed!)
240 virtual void OnExit();
241
242 public:
243 MyFrame *m_frame;
244 size_t m_count;
245 };
246
247 MyWorkerThread::MyWorkerThread(MyFrame *frame)
248 : wxThread()
249 {
250 m_frame = frame;
251 m_count = 0;
252 }
253
254 void MyWorkerThread::OnExit()
255 {
256 }
257
258 void *MyWorkerThread::Entry()
259 {
260 for ( m_count = 0; !m_frame->Cancelled() && (m_count < 100); m_count++ )
261 {
262 // check if we were asked to exit
263 if ( TestDestroy() )
264 break;
265
266 // create any type of command event here
267 wxCommandEvent event( wxEVT_COMMAND_MENU_SELECTED, WORKER_EVENT );
268 event.SetInt( m_count );
269
270 // send in a thread-safe way
271 wxPostEvent( m_frame, event );
272
273 // wxSleep() can't be called from non-main thread!
274 wxThread::Sleep(200);
275 }
276
277 wxCommandEvent event( wxEVT_COMMAND_MENU_SELECTED, WORKER_EVENT );
278 event.SetInt(-1); // that's all
279 wxPostEvent( m_frame, event );
280
281 return NULL;
282 }
283
284 // ----------------------------------------------------------------------------
285 // a thread which simply calls wxExecute
286 // ----------------------------------------------------------------------------
287
288 class MyExecThread : public wxThread
289 {
290 public:
291 MyExecThread(const wxChar *command) : wxThread(wxTHREAD_JOINABLE),
292 m_command(command)
293 {
294 Create();
295 }
296
297 virtual ExitCode Entry()
298 {
299 return (ExitCode)EXEC(m_command);
300 }
301
302 private:
303 wxString m_command;
304 };
305
306 // ----------------------------------------------------------------------------
307 // implementation
308 // ----------------------------------------------------------------------------
309
310 BEGIN_EVENT_TABLE(MyFrame, wxFrame)
311 EVT_MENU(THREAD_QUIT, MyFrame::OnQuit)
312 EVT_MENU(THREAD_CLEAR, MyFrame::OnClear)
313 EVT_MENU(THREAD_START_THREAD, MyFrame::OnStartThread)
314 EVT_MENU(THREAD_START_THREADS, MyFrame::OnStartThreads)
315 EVT_MENU(THREAD_STOP_THREAD, MyFrame::OnStopThread)
316 EVT_MENU(THREAD_PAUSE_THREAD, MyFrame::OnPauseThread)
317 EVT_MENU(THREAD_RESUME_THREAD, MyFrame::OnResumeThread)
318
319 EVT_MENU(THREAD_EXEC_MAIN, MyFrame::OnExecMain)
320 EVT_MENU(THREAD_EXEC_THREAD, MyFrame::OnExecThread)
321
322 EVT_MENU(THREAD_SHOWCPUS, MyFrame::OnShowCPUs)
323 EVT_MENU(THREAD_ABOUT, MyFrame::OnAbout)
324
325 EVT_UPDATE_UI(THREAD_START_WORKER, MyFrame::OnUpdateWorker)
326 EVT_MENU(THREAD_START_WORKER, MyFrame::OnStartWorker)
327 EVT_MENU(WORKER_EVENT, MyFrame::OnWorkerEvent)
328
329 EVT_IDLE(MyFrame::OnIdle)
330 END_EVENT_TABLE()
331
332 // `Main program' equivalent, creating windows and returning main app frame
333 bool MyApp::OnInit()
334 {
335 // uncomment this to get some debugging messages from the trace code
336 // on the console (or just set WXTRACE env variable to include "thread")
337 //wxLog::AddTraceMask("thread");
338
339 // Create the main frame window
340 MyFrame *frame = new MyFrame((wxFrame *)NULL, "wxWindows threads sample",
341 50, 50, 450, 340);
342
343 // Make a menubar
344 wxMenuBar *menuBar = new wxMenuBar;
345
346 wxMenu *menuFile = new wxMenu;
347 menuFile->Append(THREAD_CLEAR, "&Clear log\tCtrl-L");
348 menuFile->AppendSeparator();
349 menuFile->Append(THREAD_QUIT, "E&xit\tAlt-X");
350 menuBar->Append(menuFile, "&File");
351
352 wxMenu *menuThread = new wxMenu;
353 menuThread->Append(THREAD_START_THREAD, "&Start a new thread\tCtrl-N");
354 menuThread->Append(THREAD_START_THREADS, "Start &many threads at once");
355 menuThread->Append(THREAD_STOP_THREAD, "S&top a running thread\tCtrl-S");
356 menuThread->AppendSeparator();
357 menuThread->Append(THREAD_PAUSE_THREAD, "&Pause a running thread\tCtrl-P");
358 menuThread->Append(THREAD_RESUME_THREAD, "&Resume suspended thread\tCtrl-R");
359 menuThread->AppendSeparator();
360 menuThread->Append(THREAD_START_WORKER, "Start &worker thread\tCtrl-W");
361 menuBar->Append(menuThread, "&Thread");
362
363 wxMenu *menuExec = new wxMenu;
364 menuExec->Append(THREAD_EXEC_MAIN, "&Launch a program from main thread\tF5");
365 menuExec->Append(THREAD_EXEC_THREAD, "L&aunch a program from a thread\tCtrl-F5");
366 menuBar->Append(menuExec, "&Execute");
367
368 wxMenu *menuHelp = new wxMenu;
369 menuHelp->Append(THREAD_SHOWCPUS, "&Show CPU count");
370 menuHelp->AppendSeparator();
371 menuHelp->Append(THREAD_ABOUT, "&About...");
372 menuBar->Append(menuHelp, "&Help");
373
374 frame->SetMenuBar(menuBar);
375
376 // Show the frame
377 frame->Show(TRUE);
378
379 SetTopWindow(frame);
380
381 return TRUE;
382 }
383
384 // My frame constructor
385 MyFrame::MyFrame(wxFrame *frame, const wxString& title,
386 int x, int y, int w, int h)
387 : wxFrame(frame, -1, title, wxPoint(x, y), wxSize(w, h))
388 {
389 m_nRunning = m_nCount = 0;
390
391 m_dlgProgress = (wxProgressDialog *)NULL;
392
393 CreateStatusBar(2);
394
395 m_txtctrl = new wxTextCtrl(this, -1, "", wxPoint(0, 0), wxSize(0, 0),
396 wxTE_MULTILINE | wxTE_READONLY);
397
398 }
399
400 MyThread *MyFrame::CreateThread()
401 {
402 MyThread *thread = new MyThread(this);
403
404 if ( thread->Create() != wxTHREAD_NO_ERROR )
405 {
406 wxLogError(wxT("Can't create thread!"));
407 }
408
409 wxCriticalSectionLocker enter(wxGetApp().m_critsect);
410 wxGetApp().m_threads.Add(thread);
411
412 return thread;
413 }
414
415 void MyFrame::OnStartThreads(wxCommandEvent& WXUNUSED(event) )
416 {
417 static long s_num = 10;
418
419 s_num = wxGetNumberFromUser("How many threads to start: ", "",
420 "wxThread sample", s_num, 1, 10000, this);
421 if ( s_num == -1 )
422 {
423 s_num = 10;
424
425 return;
426 }
427
428 size_t count = (size_t)s_num, n;
429
430 wxArrayThread threads;
431
432 // first create them all...
433 for ( n = 0; n < count; n++ )
434 {
435 wxThread *thr = CreateThread();
436
437 // we want to show the effect of SetPriority(): the first thread will
438 // have the lowest priority, the second - the highest, all the rest
439 // the normal one
440 if ( n == 0 )
441 thr->SetPriority(WXTHREAD_MIN_PRIORITY);
442 else if ( n == 1 )
443 thr->SetPriority(WXTHREAD_MAX_PRIORITY);
444 else
445 thr->SetPriority(WXTHREAD_DEFAULT_PRIORITY);
446
447 threads.Add(thr);
448 }
449
450 wxString msg;
451 msg.Printf(wxT("%d new threads created."), count);
452 SetStatusText(msg, 1);
453
454 // ...and then start them
455 for ( n = 0; n < count; n++ )
456 {
457 threads[n]->Run();
458 }
459 }
460
461 void MyFrame::OnStartThread(wxCommandEvent& WXUNUSED(event) )
462 {
463 MyThread *thread = CreateThread();
464
465 if ( thread->Run() != wxTHREAD_NO_ERROR )
466 {
467 wxLogError(wxT("Can't start thread!"));
468 }
469
470 SetStatusText("New thread started.", 1);
471 }
472
473 void MyFrame::OnStopThread(wxCommandEvent& WXUNUSED(event) )
474 {
475 wxGetApp().m_critsect.Enter();
476
477 // stop the last thread
478 if ( wxGetApp().m_threads.IsEmpty() )
479 {
480 wxLogError(wxT("No thread to stop!"));
481
482 wxGetApp().m_critsect.Leave();
483 }
484 else
485 {
486 wxThread *thread = wxGetApp().m_threads.Last();
487
488 // it's important to leave critical section before calling Delete()
489 // because delete will (implicitly) call OnExit() which also tries
490 // to enter the same crit section - would dead lock.
491 wxGetApp().m_critsect.Leave();
492
493 thread->Delete();
494
495 SetStatusText("Thread stopped.", 1);
496 }
497 }
498
499 void MyFrame::OnResumeThread(wxCommandEvent& WXUNUSED(event) )
500 {
501 wxCriticalSectionLocker enter(wxGetApp().m_critsect);
502
503 // resume first suspended thread
504 size_t n = 0, count = wxGetApp().m_threads.Count();
505 while ( n < count && !wxGetApp().m_threads[n]->IsPaused() )
506 n++;
507
508 if ( n == count )
509 {
510 wxLogError(wxT("No thread to resume!"));
511 }
512 else
513 {
514 wxGetApp().m_threads[n]->Resume();
515
516 SetStatusText("Thread resumed.", 1);
517 }
518 }
519
520 void MyFrame::OnPauseThread(wxCommandEvent& WXUNUSED(event) )
521 {
522 wxCriticalSectionLocker enter(wxGetApp().m_critsect);
523
524 // pause last running thread
525 int n = wxGetApp().m_threads.Count() - 1;
526 while ( n >= 0 && !wxGetApp().m_threads[n]->IsRunning() )
527 n--;
528
529 if ( n < 0 )
530 {
531 wxLogError(wxT("No thread to pause!"));
532 }
533 else
534 {
535 wxGetApp().m_threads[n]->Pause();
536
537 SetStatusText("Thread paused.", 1);
538 }
539 }
540
541 // set the frame title indicating the current number of threads
542 void MyFrame::OnIdle(wxIdleEvent &event)
543 {
544 // update the counts of running/total threads
545 size_t nRunning = 0,
546 nCount = wxGetApp().m_threads.Count();
547 for ( size_t n = 0; n < nCount; n++ )
548 {
549 if ( wxGetApp().m_threads[n]->IsRunning() )
550 nRunning++;
551 }
552
553 if ( nCount != m_nCount || nRunning != m_nRunning )
554 {
555 m_nRunning = nRunning;
556 m_nCount = nCount;
557
558 wxLogStatus(this, wxT("%u threads total, %u running."), nCount, nRunning);
559 }
560 //else: avoid flicker - don't print anything
561 }
562
563 void MyFrame::OnQuit(wxCommandEvent& WXUNUSED(event) )
564 {
565 size_t count = wxGetApp().m_threads.Count();
566 for ( size_t i = 0; i < count; i++ )
567 {
568 wxGetApp().m_threads[0]->Delete();
569 }
570
571 Close(TRUE);
572 }
573
574 void MyFrame::OnExecMain(wxCommandEvent& WXUNUSED(event))
575 {
576 wxLogMessage("The exit code from the main program is %ld",
577 EXEC("/bin/echo \"main program\""));
578 }
579
580 void MyFrame::OnExecThread(wxCommandEvent& WXUNUSED(event))
581 {
582 MyExecThread thread("/bin/echo \"child thread\"");
583 thread.Run();
584
585 wxLogMessage("The exit code from a child thread is %ld",
586 (long)thread.Wait());
587 }
588
589 void MyFrame::OnShowCPUs(wxCommandEvent& WXUNUSED(event))
590 {
591 wxString msg;
592
593 int nCPUs = wxThread::GetCPUCount();
594 switch ( nCPUs )
595 {
596 case -1:
597 msg = "Unknown number of CPUs";
598 break;
599
600 case 0:
601 msg = "WARNING: you're running without any CPUs!";
602 break;
603
604 case 1:
605 msg = "This system only has one CPU.";
606 break;
607
608 default:
609 msg.Printf("This system has %d CPUs", nCPUs);
610 }
611
612 wxLogMessage(msg);
613 }
614
615 void MyFrame::OnAbout(wxCommandEvent& WXUNUSED(event) )
616 {
617 wxMessageDialog dialog(this, "wxWindows multithreaded application sample\n"
618 "(c) 1998 Julian Smart, Guilhem Lavaux\n"
619 "(c) 1999 Vadim Zeitlin\n"
620 "(c) 2000 Robert Roebling",
621 "About wxThread sample",
622 wxOK | wxICON_INFORMATION);
623
624 dialog.ShowModal();
625 }
626
627 void MyFrame::OnClear(wxCommandEvent& WXUNUSED(event))
628 {
629 m_txtctrl->Clear();
630 }
631
632 void MyFrame::OnUpdateWorker(wxUpdateUIEvent& event)
633 {
634 event.Enable( m_dlgProgress == NULL );
635 }
636
637 void MyFrame::OnStartWorker(wxCommandEvent& WXUNUSED(event))
638 {
639 MyWorkerThread *thread = new MyWorkerThread(this);
640
641 if ( thread->Create() != wxTHREAD_NO_ERROR )
642 {
643 wxLogError(wxT("Can't create thread!"));
644 }
645
646 m_dlgProgress = new wxProgressDialog
647 (
648 "Progress dialog",
649 "Wait until the thread terminates or press [Cancel]",
650 100,
651 this,
652 wxPD_CAN_ABORT |
653 wxPD_APP_MODAL |
654 wxPD_ELAPSED_TIME |
655 wxPD_ESTIMATED_TIME |
656 wxPD_REMAINING_TIME
657 );
658
659 // thread is not running yet, no need for crit sect
660 m_cancelled = FALSE;
661
662 thread->Run();
663 }
664
665 void MyFrame::OnWorkerEvent(wxCommandEvent& event)
666 {
667 #if 0
668 WriteText( "Got message from worker thread: " );
669 WriteText( event.GetString() );
670 WriteText( "\n" );
671 #else
672 int n = event.GetInt();
673 if ( n == -1 )
674 {
675 m_dlgProgress->Destroy();
676 m_dlgProgress = (wxProgressDialog *)NULL;
677
678 // the dialog is aborted because the event came from another thread, so
679 // we may need to wake up the main event loop for the dialog to be
680 // really closed
681 wxWakeUpIdle();
682 }
683 else
684 {
685 if ( !m_dlgProgress->Update(n) )
686 {
687 wxCriticalSectionLocker lock(m_critsectWork);
688
689 m_cancelled = TRUE;
690 }
691 }
692 #endif
693 }
694
695 bool MyFrame::Cancelled()
696 {
697 wxCriticalSectionLocker lock(m_critsectWork);
698
699 return m_cancelled;
700 }