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