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