]> git.saurik.com Git - wxWidgets.git/blame - samples/thread/test.cpp
Added missing SetGridCursor function
[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
VZ
191 WriteText(text);
192
bee503b0 193 for ( m_count = 0; m_count < 10; m_count++ )
3222fde2
VZ
194 {
195 // check if we were asked to exit
196 if ( TestDestroy() )
197 break;
198
b568d04f 199 text.Printf("[%u] Thread 0x%x here.\n", m_count, GetId());
3222fde2 200 WriteText(text);
a6b0bd49 201
bf1852e1
VZ
202 // wxSleep() can't be called from non-GUI thread!
203 wxThread::Sleep(1000);
a6b0bd49 204 }
3222fde2 205
b568d04f 206 text.Printf("Thread 0x%x finished.\n", GetId());
3222fde2
VZ
207 WriteText(text);
208
a6b0bd49 209 return NULL;
82052aff
GL
210}
211
ce6d2511
RR
212//--------------------------------------------------
213// worker thread
214//--------------------------------------------------
215
216class MyWorkerThread : public wxThread
3222fde2 217{
ce6d2511
RR
218public:
219 MyWorkerThread(MyFrame *frame);
220
221 // thread execution starts here
222 virtual void *Entry();
223
224 // called when the thread exits - whether it terminates normally or is
225 // stopped with Delete() (but not when it is Kill()ed!)
226 virtual void OnExit();
227
228public:
229 MyFrame *m_frame;
230 size_t m_count;
3222fde2 231};
82052aff 232
ce6d2511
RR
233MyWorkerThread::MyWorkerThread(MyFrame *frame)
234 : wxThread()
235{
236 m_frame = frame;
237 m_count = 0;
238}
239
240void MyWorkerThread::OnExit()
241{
242}
243
244void *MyWorkerThread::Entry()
245{
b9de1315 246 for ( m_count = 0; !m_frame->Cancelled() && (m_count < 100); m_count++ )
ce6d2511
RR
247 {
248 // check if we were asked to exit
249 if ( TestDestroy() )
250 break;
b9de1315 251
07f5b19a
RR
252 wxString text;
253 text.Printf("[%u] Thread 0x%x here!!", m_count, GetId());
254
b9de1315 255 // create any type of command event here
ce6d2511 256 wxCommandEvent event( wxEVT_COMMAND_MENU_SELECTED, WORKER_EVENT );
b9de1315 257 event.SetInt( m_count );
07f5b19a 258 event.SetString( text );
b9de1315 259
07f5b19a 260 // send in a thread-safe way
ce6d2511 261 wxPostEvent( m_frame, event );
b9de1315 262
07f5b19a
RR
263 // same as:
264 // m_frame->AddPendingEvent( event );
ce6d2511 265
07f5b19a 266 // wxSleep() can't be called from non-main thread!
b9de1315 267 wxThread::Sleep(200);
ce6d2511
RR
268 }
269
b9de1315
VZ
270 wxCommandEvent event( wxEVT_COMMAND_MENU_SELECTED, WORKER_EVENT );
271 event.SetInt(-1); // that's all
272 wxPostEvent( m_frame, event );
273
ce6d2511
RR
274 return NULL;
275}
276
277//--------------------------------------------------
278// main program
279//--------------------------------------------------
280
82052aff 281BEGIN_EVENT_TABLE(MyFrame, wxFrame)
a6b0bd49
VZ
282 EVT_MENU(TEST_QUIT, MyFrame::OnQuit)
283 EVT_MENU(TEST_ABOUT, MyFrame::OnAbout)
bee503b0 284 EVT_MENU(TEST_CLEAR, MyFrame::OnClear)
a6b0bd49 285 EVT_MENU(TEST_START_THREAD, MyFrame::OnStartThread)
7c3d7e2d 286 EVT_MENU(TEST_START_THREADS, MyFrame::OnStartThreads)
a6b0bd49
VZ
287 EVT_MENU(TEST_STOP_THREAD, MyFrame::OnStopThread)
288 EVT_MENU(TEST_PAUSE_THREAD, MyFrame::OnPauseThread)
289 EVT_MENU(TEST_RESUME_THREAD, MyFrame::OnResumeThread)
b9de1315
VZ
290
291 EVT_UPDATE_UI(TEST_START_WORKER, MyFrame::OnUpdateWorker)
ce6d2511
RR
292 EVT_MENU(TEST_START_WORKER, MyFrame::OnStartWorker)
293 EVT_MENU(WORKER_EVENT, MyFrame::OnWorkerEvent)
a6b0bd49 294
3222fde2 295 EVT_IDLE(MyFrame::OnIdle)
82052aff
GL
296END_EVENT_TABLE()
297
82052aff 298// `Main program' equivalent, creating windows and returning main app frame
3222fde2 299bool MyApp::OnInit()
82052aff 300{
a6b0bd49 301 // Create the main frame window
bee503b0
VZ
302 MyFrame *frame = new MyFrame((wxFrame *)NULL, "wxWindows threads sample",
303 50, 50, 450, 340);
3222fde2 304
a6b0bd49
VZ
305 // Make a menubar
306 wxMenu *file_menu = new wxMenu;
3222fde2 307
98f026a6 308 file_menu->Append(TEST_CLEAR, "&Clear log\tCtrl-L");
bee503b0 309 file_menu->AppendSeparator();
a6b0bd49 310 file_menu->Append(TEST_ABOUT, "&About");
bee503b0 311 file_menu->AppendSeparator();
98f026a6 312 file_menu->Append(TEST_QUIT, "E&xit\tAlt-X");
a6b0bd49
VZ
313 wxMenuBar *menu_bar = new wxMenuBar;
314 menu_bar->Append(file_menu, "&File");
3222fde2 315
a6b0bd49 316 wxMenu *thread_menu = new wxMenu;
98f026a6 317 thread_menu->Append(TEST_START_THREAD, "&Start a new thread\tCtrl-N");
7c3d7e2d 318 thread_menu->Append(TEST_START_THREADS, "Start &many threads at once");
98f026a6 319 thread_menu->Append(TEST_STOP_THREAD, "S&top a running thread\tCtrl-S");
a6b0bd49 320 thread_menu->AppendSeparator();
98f026a6
VZ
321 thread_menu->Append(TEST_PAUSE_THREAD, "&Pause a running thread\tCtrl-P");
322 thread_menu->Append(TEST_RESUME_THREAD, "&Resume suspended thread\tCtrl-R");
ce6d2511
RR
323 thread_menu->AppendSeparator();
324 thread_menu->Append(TEST_START_WORKER, "Start &worker thread\tCtrl-W");
b9de1315 325
3222fde2 326 menu_bar->Append(thread_menu, "&Thread");
a6b0bd49 327 frame->SetMenuBar(menu_bar);
3222fde2 328
a6b0bd49
VZ
329 // Show the frame
330 frame->Show(TRUE);
3222fde2 331
a6b0bd49 332 SetTopWindow(frame);
3222fde2 333
a6b0bd49 334 return TRUE;
82052aff
GL
335}
336
337// My frame constructor
bf1852e1
VZ
338MyFrame::MyFrame(wxFrame *frame, const wxString& title,
339 int x, int y, int w, int h)
a6b0bd49 340 : wxFrame(frame, -1, title, wxPoint(x, y), wxSize(w, h))
82052aff 341{
7c3d7e2d
VZ
342 m_nRunning = m_nCount = 0;
343
b9de1315
VZ
344 m_dlgProgress = (wxProgressDialog *)NULL;
345
7c3d7e2d 346 CreateStatusBar(2);
3222fde2 347
bee503b0
VZ
348 m_txtctrl = new wxTextCtrl(this, -1, "", wxPoint(0, 0), wxSize(0, 0),
349 wxTE_MULTILINE | wxTE_READONLY);
82052aff 350
a6b0bd49 351}
82052aff 352
7c3d7e2d 353MyThread *MyFrame::CreateThread()
a6b0bd49
VZ
354{
355 MyThread *thread = new MyThread(this);
3222fde2 356
bf1852e1
VZ
357 if ( thread->Create() != wxTHREAD_NO_ERROR )
358 {
359 wxLogError("Can't create thread!");
360 }
3222fde2 361
1bd3e1ef
GL
362 wxCriticalSectionLocker enter(wxGetApp().m_critsect);
363 wxGetApp().m_threads.Add(thread);
bf1852e1 364
7c3d7e2d
VZ
365 return thread;
366}
367
368void MyFrame::OnStartThreads(wxCommandEvent& WXUNUSED(event) )
369{
b568d04f
VZ
370 static long s_num = 10;
371
372 s_num = wxGetNumberFromUser("How many threads to start: ", "",
373 "wxThread sample", s_num, 1, 10000, this);
374 if ( s_num == -1 )
375 {
376 s_num = 10;
7c3d7e2d 377
7c3d7e2d 378 return;
b568d04f
VZ
379 }
380
381 size_t count = (size_t)s_num, n;
7c3d7e2d
VZ
382
383 wxArrayThread threads;
384
385 // first create them all...
386 for ( n = 0; n < count; n++ )
387 {
98f026a6
VZ
388 wxThread *thr = CreateThread();
389
390 // we want to show the effect of SetPriority(): the first thread will
391 // have the lowest priority, the second - the highest, all the rest
392 // the normal one
393 if ( n == 0 )
394 thr->SetPriority(WXTHREAD_MIN_PRIORITY);
395 else if ( n == 1 )
396 thr->SetPriority(WXTHREAD_MAX_PRIORITY);
397 else
398 thr->SetPriority(WXTHREAD_DEFAULT_PRIORITY);
399
400 threads.Add(thr);
7c3d7e2d
VZ
401 }
402
403 wxString msg;
404 msg.Printf("%d new threads created.", count);
405 SetStatusText(msg, 1);
406
407 // ...and then start them
408 for ( n = 0; n < count; n++ )
409 {
410 threads[n]->Run();
411 }
412}
413
414void MyFrame::OnStartThread(wxCommandEvent& WXUNUSED(event) )
415{
416 MyThread *thread = CreateThread();
417
bf1852e1
VZ
418 if ( thread->Run() != wxTHREAD_NO_ERROR )
419 {
420 wxLogError("Can't start thread!");
421 }
7c3d7e2d
VZ
422
423 SetStatusText("New thread started.", 1);
82052aff
GL
424}
425
e3e65dac 426void MyFrame::OnStopThread(wxCommandEvent& WXUNUSED(event) )
82052aff 427{
bf1852e1 428 // stop the last thread
1bd3e1ef 429 if ( wxGetApp().m_threads.IsEmpty() )
bee503b0
VZ
430 {
431 wxLogError("No thread to stop!");
bee503b0 432 }
bf1852e1
VZ
433 else
434 {
1bd3e1ef 435 wxGetApp().m_critsect.Enter();
7c3d7e2d 436
1bd3e1ef 437 wxThread *thread = wxGetApp().m_threads.Last();
7c3d7e2d
VZ
438
439 // it's important to leave critical section before calling Delete()
1bd3e1ef 440 // because delete will (implicitly) call OnExit() which also tries
7c3d7e2d 441 // to enter the same crit section - would dead lock.
1bd3e1ef 442 wxGetApp().m_critsect.Leave();
7c3d7e2d
VZ
443
444 thread->Delete();
445
446 SetStatusText("Thread stopped.", 1);
bf1852e1 447 }
a6b0bd49 448}
82052aff 449
a6b0bd49
VZ
450void MyFrame::OnResumeThread(wxCommandEvent& WXUNUSED(event) )
451{
1bd3e1ef 452 wxCriticalSectionLocker enter(wxGetApp().m_critsect);
bee503b0 453
3222fde2 454 // resume first suspended thread
1bd3e1ef
GL
455 size_t n = 0, count = wxGetApp().m_threads.Count();
456 while ( n < count && !wxGetApp().m_threads[n]->IsPaused() )
bf1852e1 457 n++;
3222fde2 458
bf1852e1 459 if ( n == count )
7c3d7e2d 460 {
bee503b0 461 wxLogError("No thread to resume!");
7c3d7e2d 462 }
a6b0bd49 463 else
7c3d7e2d 464 {
1bd3e1ef 465 wxGetApp().m_threads[n]->Resume();
7c3d7e2d
VZ
466
467 SetStatusText("Thread resumed.", 1);
468 }
82052aff
GL
469}
470
e3e65dac 471void MyFrame::OnPauseThread(wxCommandEvent& WXUNUSED(event) )
f3855ef0 472{
1bd3e1ef 473 wxCriticalSectionLocker enter(wxGetApp().m_critsect);
bee503b0 474
3222fde2 475 // pause last running thread
1bd3e1ef
GL
476 int n = wxGetApp().m_threads.Count() - 1;
477 while ( n >= 0 && !wxGetApp().m_threads[n]->IsRunning() )
a6b0bd49 478 n--;
3222fde2 479
a6b0bd49 480 if ( n < 0 )
7c3d7e2d 481 {
a6b0bd49 482 wxLogError("No thread to pause!");
7c3d7e2d 483 }
a6b0bd49 484 else
7c3d7e2d 485 {
1bd3e1ef 486 wxGetApp().m_threads[n]->Pause();
88ac883a 487
7c3d7e2d
VZ
488 SetStatusText("Thread paused.", 1);
489 }
f3855ef0
RR
490}
491
3222fde2
VZ
492// set the frame title indicating the current number of threads
493void MyFrame::OnIdle(wxIdleEvent &event)
494{
7fe4f500 495 // update the counts of running/total threads
3222fde2 496 size_t nRunning = 0,
1bd3e1ef 497 nCount = wxGetApp().m_threads.Count();
3222fde2
VZ
498 for ( size_t n = 0; n < nCount; n++ )
499 {
1bd3e1ef 500 if ( wxGetApp().m_threads[n]->IsRunning() )
3222fde2
VZ
501 nRunning++;
502 }
503
7c3d7e2d
VZ
504 if ( nCount != m_nCount || nRunning != m_nRunning )
505 {
506 m_nRunning = nRunning;
507 m_nCount = nCount;
508
509 wxLogStatus(this, "%u threads total, %u running.", nCount, nRunning);
510 }
511 //else: avoid flicker - don't print anything
f3855ef0 512}
82052aff 513
e3e65dac 514void MyFrame::OnQuit(wxCommandEvent& WXUNUSED(event) )
82052aff 515{
1bd3e1ef 516 size_t count = wxGetApp().m_threads.Count();
bf1852e1
VZ
517 for ( size_t i = 0; i < count; i++ )
518 {
1bd3e1ef 519 wxGetApp().m_threads[0]->Delete();
bf1852e1 520 }
3222fde2 521
a6b0bd49 522 Close(TRUE);
82052aff
GL
523}
524
e3e65dac 525void MyFrame::OnAbout(wxCommandEvent& WXUNUSED(event) )
82052aff 526{
bf1852e1
VZ
527 wxMessageDialog dialog(this, "wxWindows multithreaded application sample\n"
528 "(c) 1998 Julian Smart, Guilhem Lavaux\n"
07f5b19a
RR
529 "(c) 1999 Vadim Zeitlin\n"
530 "(c) 2000 Robert Roebling",
3222fde2
VZ
531 "About wxThread sample",
532 wxOK | wxICON_INFORMATION);
533
a6b0bd49 534 dialog.ShowModal();
82052aff
GL
535}
536
bee503b0
VZ
537void MyFrame::OnClear(wxCommandEvent& WXUNUSED(event))
538{
539 m_txtctrl->Clear();
540}
ce6d2511 541
b9de1315
VZ
542void MyFrame::OnUpdateWorker(wxUpdateUIEvent& event)
543{
544 event.Enable( m_dlgProgress == NULL );
545}
546
ce6d2511
RR
547void MyFrame::OnStartWorker(wxCommandEvent& WXUNUSED(event))
548{
549 MyWorkerThread *thread = new MyWorkerThread(this);
550
551 if ( thread->Create() != wxTHREAD_NO_ERROR )
552 {
553 wxLogError("Can't create thread!");
554 }
b9de1315
VZ
555
556 m_dlgProgress = new wxProgressDialog
557 (
558 "Progress dialog",
559 "Wait until the thread terminates or press [Cancel]",
560 100,
561 this,
562 wxPD_CAN_ABORT |
563 wxPD_APP_MODAL |
564 wxPD_ELAPSED_TIME |
565 wxPD_ESTIMATED_TIME |
566 wxPD_REMAINING_TIME
567 );
568
569 // thread is not running yet, no need for crit sect
570 m_cancelled = FALSE;
571
ce6d2511
RR
572 thread->Run();
573}
574
07f5b19a 575void MyFrame::OnWorkerEvent(wxCommandEvent& event)
ce6d2511 576{
b9de1315 577#if 0
07f5b19a
RR
578 WriteText( "Got message from worker thread: " );
579 WriteText( event.GetString() );
580 WriteText( "\n" );
b9de1315
VZ
581#else
582 int n = event.GetInt();
583 if ( n == -1 )
584 {
585 m_dlgProgress->Destroy();
586 m_dlgProgress = (wxProgressDialog *)NULL;
587
588 // the dialog is aborted because the event came from another thread, so
589 // we may need to wake up the main event loop for the dialog to be
590 // really closed
591 wxWakeUpIdle();
592 }
593 else
594 {
595 if ( !m_dlgProgress->Update(n) )
596 {
597 wxCriticalSectionLocker lock(m_critsectWork);
598
599 m_cancelled = TRUE;
600 }
601 }
602#endif
ce6d2511
RR
603}
604
b9de1315
VZ
605bool MyFrame::Cancelled()
606{
607 wxCriticalSectionLocker lock(m_critsectWork);
608
609 return m_cancelled;
610}