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