thread.cpp 30 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077
  1. /////////////////////////////////////////////////////////////////////////////
  2. // Name: thread.cpp
  3. // Purpose: wxWidgets thread sample
  4. // Author: Guilhem Lavaux, Vadim Zeitlin
  5. // Modified by:
  6. // Created: 06/16/98
  7. // Copyright: (c) 1998-2009 wxWidgets team
  8. // Licence: wxWindows licence
  9. /////////////////////////////////////////////////////////////////////////////
  10. // ============================================================================
  11. // declarations
  12. // ============================================================================
  13. // ----------------------------------------------------------------------------
  14. // headers
  15. // ----------------------------------------------------------------------------
  16. // For compilers that support precompilation, includes "wx/wx.h".
  17. #include "wx/wxprec.h"
  18. #ifdef __BORLANDC__
  19. #pragma hdrstop
  20. #endif
  21. #ifndef WX_PRECOMP
  22. #include "wx/wx.h"
  23. #endif
  24. #if !wxUSE_THREADS
  25. #error "This sample requires thread support!"
  26. #endif // wxUSE_THREADS
  27. #include "wx/thread.h"
  28. #include "wx/dynarray.h"
  29. #include "wx/numdlg.h"
  30. #include "wx/progdlg.h"
  31. // ----------------------------------------------------------------------------
  32. // resources
  33. // ----------------------------------------------------------------------------
  34. #include "../sample.xpm"
  35. // ----------------------------------------------------------------------------
  36. // private classes
  37. // ----------------------------------------------------------------------------
  38. // define this to use wxExecute in the exec tests, otherwise just use system
  39. #define USE_EXECUTE
  40. #ifdef USE_EXECUTE
  41. #define EXEC(cmd) wxExecute((cmd), wxEXEC_SYNC)
  42. #else
  43. #define EXEC(cmd) system(cmd)
  44. #endif
  45. class MyThread;
  46. WX_DEFINE_ARRAY_PTR(wxThread *, wxArrayThread);
  47. // ----------------------------------------------------------------------------
  48. // the application object
  49. // ----------------------------------------------------------------------------
  50. class MyApp : public wxApp
  51. {
  52. public:
  53. MyApp();
  54. virtual ~MyApp(){};
  55. virtual bool OnInit();
  56. // critical section protects access to all of the fields below
  57. wxCriticalSection m_critsect;
  58. // all the threads currently alive - as soon as the thread terminates, it's
  59. // removed from the array
  60. wxArrayThread m_threads;
  61. // semaphore used to wait for the threads to exit, see MyFrame::OnQuit()
  62. wxSemaphore m_semAllDone;
  63. // indicates that we're shutting down and all threads should exit
  64. bool m_shuttingDown;
  65. };
  66. // ----------------------------------------------------------------------------
  67. // the main application frame
  68. // ----------------------------------------------------------------------------
  69. class MyFrame : public wxFrame,
  70. private wxLog
  71. {
  72. public:
  73. // ctor
  74. MyFrame(const wxString& title);
  75. virtual ~MyFrame();
  76. // accessors for MyWorkerThread (called in its context!)
  77. bool Cancelled();
  78. protected:
  79. virtual void DoLogRecord(wxLogLevel level,
  80. const wxString& msg,
  81. const wxLogRecordInfo& info);
  82. private:
  83. // event handlers
  84. // --------------
  85. void OnQuit(wxCommandEvent& event);
  86. void OnClear(wxCommandEvent& event);
  87. void OnStartThread(wxCommandEvent& event);
  88. void OnStartThreads(wxCommandEvent& event);
  89. void OnStopThread(wxCommandEvent& event);
  90. void OnPauseThread(wxCommandEvent& event);
  91. void OnResumeThread(wxCommandEvent& event);
  92. void OnStartWorker(wxCommandEvent& event);
  93. void OnExecMain(wxCommandEvent& event);
  94. void OnStartGUIThread(wxCommandEvent& event);
  95. void OnShowCPUs(wxCommandEvent& event);
  96. void OnAbout(wxCommandEvent& event);
  97. void OnIdle(wxIdleEvent &event);
  98. void OnWorkerEvent(wxThreadEvent& event);
  99. void OnUpdateWorker(wxUpdateUIEvent& event);
  100. // logging helper
  101. void DoLogLine(wxTextCtrl *text,
  102. const wxString& timestr,
  103. const wxString& threadstr,
  104. const wxString& msg);
  105. // thread helper functions
  106. // -----------------------
  107. // helper function - creates a new thread (but doesn't run it)
  108. MyThread *CreateThread();
  109. // update display in our status bar: called during idle handling
  110. void UpdateThreadStatus();
  111. // internal variables
  112. // ------------------
  113. // just some place to put our messages in
  114. wxTextCtrl *m_txtctrl;
  115. // old log target, we replace it with one using m_txtctrl during this
  116. // frame life time
  117. wxLog *m_oldLogger;
  118. // the array of pending messages to be displayed and the critical section
  119. // protecting it
  120. wxArrayString m_messages;
  121. wxCriticalSection m_csMessages;
  122. // remember the number of running threads and total number of threads
  123. size_t m_nRunning,
  124. m_nCount;
  125. // the progress dialog which we show while worker thread is running
  126. wxProgressDialog *m_dlgProgress;
  127. // was the worker thread cancelled by user?
  128. bool m_cancelled;
  129. wxCriticalSection m_csCancelled; // protects m_cancelled
  130. wxDECLARE_EVENT_TABLE();
  131. };
  132. // ----------------------------------------------------------------------------
  133. // constants
  134. // ----------------------------------------------------------------------------
  135. // ID for the menu commands
  136. enum
  137. {
  138. THREAD_QUIT = wxID_EXIT,
  139. THREAD_ABOUT = wxID_ABOUT,
  140. THREAD_TEXT = 101,
  141. THREAD_CLEAR,
  142. THREAD_START_THREAD = 201,
  143. THREAD_START_THREADS,
  144. THREAD_STOP_THREAD,
  145. THREAD_PAUSE_THREAD,
  146. THREAD_RESUME_THREAD,
  147. THREAD_START_WORKER,
  148. THREAD_EXEC_MAIN,
  149. THREAD_START_GUI_THREAD,
  150. THREAD_SHOWCPUS,
  151. WORKER_EVENT = wxID_HIGHEST+1, // this one gets sent from MyWorkerThread
  152. GUITHREAD_EVENT // this one gets sent from MyGUIThread
  153. };
  154. // ----------------------------------------------------------------------------
  155. // a simple thread
  156. // ----------------------------------------------------------------------------
  157. class MyThread : public wxThread
  158. {
  159. public:
  160. MyThread();
  161. virtual ~MyThread();
  162. // thread execution starts here
  163. virtual void *Entry();
  164. public:
  165. unsigned m_count;
  166. };
  167. // ----------------------------------------------------------------------------
  168. // a worker thread
  169. // ----------------------------------------------------------------------------
  170. class MyWorkerThread : public wxThread
  171. {
  172. public:
  173. MyWorkerThread(MyFrame *frame);
  174. // thread execution starts here
  175. virtual void *Entry();
  176. // called when the thread exits - whether it terminates normally or is
  177. // stopped with Delete() (but not when it is Kill()ed!)
  178. virtual void OnExit();
  179. public:
  180. MyFrame *m_frame;
  181. unsigned m_count;
  182. };
  183. // ----------------------------------------------------------------------------
  184. // a thread which executes GUI calls using wxMutexGuiEnter/Leave
  185. // ----------------------------------------------------------------------------
  186. #define GUITHREAD_BMP_SIZE 300
  187. #define GUITHREAD_NUM_UPDATES 50
  188. class MyImageDialog;
  189. class MyGUIThread : public wxThread
  190. {
  191. public:
  192. MyGUIThread(MyImageDialog *dlg) : wxThread(wxTHREAD_JOINABLE)
  193. {
  194. m_dlg = dlg;
  195. }
  196. virtual ExitCode Entry();
  197. private:
  198. MyImageDialog *m_dlg;
  199. };
  200. // ----------------------------------------------------------------------------
  201. // an helper dialog used by MyFrame::OnStartGUIThread
  202. // ----------------------------------------------------------------------------
  203. class MyImageDialog: public wxDialog
  204. {
  205. public:
  206. // ctor
  207. MyImageDialog(wxFrame *frame);
  208. ~MyImageDialog();
  209. // stuff used by MyGUIThread:
  210. wxBitmap m_bmp; // the bitmap drawn by MyGUIThread
  211. wxCriticalSection m_csBmp; // protects m_bmp
  212. private:
  213. void OnGUIThreadEvent(wxThreadEvent& event);
  214. void OnPaint(wxPaintEvent&);
  215. MyGUIThread m_thread;
  216. int m_nCurrentProgress;
  217. wxDECLARE_EVENT_TABLE();
  218. };
  219. // ============================================================================
  220. // implementation
  221. // ============================================================================
  222. // ----------------------------------------------------------------------------
  223. // the application class
  224. // ----------------------------------------------------------------------------
  225. // Create a new application object
  226. IMPLEMENT_APP(MyApp)
  227. MyApp::MyApp()
  228. {
  229. m_shuttingDown = false;
  230. }
  231. // `Main program' equivalent, creating windows and returning main app frame
  232. bool MyApp::OnInit()
  233. {
  234. if ( !wxApp::OnInit() )
  235. return false;
  236. // uncomment this to get some debugging messages from the trace code
  237. // on the console (or just set WXTRACE env variable to include "thread")
  238. wxLog::AddTraceMask("thread");
  239. // Create the main frame window
  240. new MyFrame("wxWidgets threads sample");
  241. return true;
  242. }
  243. // ----------------------------------------------------------------------------
  244. // MyFrame
  245. // ----------------------------------------------------------------------------
  246. wxBEGIN_EVENT_TABLE(MyFrame, wxFrame)
  247. EVT_MENU(THREAD_QUIT, MyFrame::OnQuit)
  248. EVT_MENU(THREAD_CLEAR, MyFrame::OnClear)
  249. EVT_MENU(THREAD_START_THREAD, MyFrame::OnStartThread)
  250. EVT_MENU(THREAD_START_THREADS, MyFrame::OnStartThreads)
  251. EVT_MENU(THREAD_STOP_THREAD, MyFrame::OnStopThread)
  252. EVT_MENU(THREAD_PAUSE_THREAD, MyFrame::OnPauseThread)
  253. EVT_MENU(THREAD_RESUME_THREAD, MyFrame::OnResumeThread)
  254. EVT_MENU(THREAD_START_WORKER, MyFrame::OnStartWorker)
  255. EVT_MENU(THREAD_EXEC_MAIN, MyFrame::OnExecMain)
  256. EVT_MENU(THREAD_START_GUI_THREAD, MyFrame::OnStartGUIThread)
  257. EVT_MENU(THREAD_SHOWCPUS, MyFrame::OnShowCPUs)
  258. EVT_MENU(THREAD_ABOUT, MyFrame::OnAbout)
  259. EVT_UPDATE_UI(THREAD_START_WORKER, MyFrame::OnUpdateWorker)
  260. EVT_THREAD(WORKER_EVENT, MyFrame::OnWorkerEvent)
  261. EVT_IDLE(MyFrame::OnIdle)
  262. wxEND_EVENT_TABLE()
  263. // My frame constructor
  264. MyFrame::MyFrame(const wxString& title)
  265. : wxFrame(NULL, wxID_ANY, title)
  266. {
  267. m_oldLogger = wxLog::GetActiveTarget();
  268. SetIcon(wxICON(sample));
  269. // Make a menubar
  270. wxMenuBar *menuBar = new wxMenuBar;
  271. wxMenu *menuFile = new wxMenu;
  272. menuFile->Append(THREAD_CLEAR, wxT("&Clear log\tCtrl-L"));
  273. menuFile->AppendSeparator();
  274. menuFile->Append(THREAD_QUIT, wxT("E&xit\tAlt-X"));
  275. menuBar->Append(menuFile, wxT("&File"));
  276. wxMenu *menuThread = new wxMenu;
  277. menuThread->Append(THREAD_START_THREAD, wxT("&Start a new thread\tCtrl-N"));
  278. menuThread->Append(THREAD_START_THREADS, wxT("Start &many threads at once"));
  279. menuThread->Append(THREAD_STOP_THREAD, wxT("S&top the last spawned thread\tCtrl-S"));
  280. menuThread->AppendSeparator();
  281. menuThread->Append(THREAD_PAUSE_THREAD, wxT("&Pause the last spawned running thread\tCtrl-P"));
  282. menuThread->Append(THREAD_RESUME_THREAD, wxT("&Resume the first suspended thread\tCtrl-R"));
  283. menuThread->AppendSeparator();
  284. menuThread->Append(THREAD_START_WORKER, wxT("Start a &worker thread\tCtrl-W"));
  285. menuThread->Append(THREAD_EXEC_MAIN, wxT("&Launch a program from main thread\tF5"));
  286. menuThread->Append(THREAD_START_GUI_THREAD, wxT("Launch a &GUI thread\tF6"));
  287. menuBar->Append(menuThread, wxT("&Thread"));
  288. wxMenu *menuHelp = new wxMenu;
  289. menuHelp->Append(THREAD_SHOWCPUS, wxT("&Show CPU count"));
  290. menuHelp->AppendSeparator();
  291. menuHelp->Append(THREAD_ABOUT, wxT("&About"));
  292. menuBar->Append(menuHelp, wxT("&Help"));
  293. SetMenuBar(menuBar);
  294. m_nRunning = m_nCount = 0;
  295. m_dlgProgress = NULL;
  296. #if wxUSE_STATUSBAR
  297. CreateStatusBar(2);
  298. #endif // wxUSE_STATUSBAR
  299. // create the logging text control and a header showing the meaning of the
  300. // different columns
  301. wxTextCtrl *header = new wxTextCtrl(this, wxID_ANY, "",
  302. wxDefaultPosition, wxDefaultSize,
  303. wxTE_READONLY);
  304. DoLogLine(header, " Time", " Thread", "Message");
  305. m_txtctrl = new wxTextCtrl(this, wxID_ANY, "",
  306. wxDefaultPosition, wxDefaultSize,
  307. wxTE_MULTILINE | wxTE_READONLY);
  308. wxLog::SetActiveTarget(this);
  309. // use fixed width font to align output in nice columns
  310. wxFont font(wxNORMAL_FONT->GetPointSize(), wxFONTFAMILY_TELETYPE,
  311. wxFONTSTYLE_NORMAL, wxFONTWEIGHT_NORMAL);
  312. header->SetFont(font);
  313. m_txtctrl->SetFont(font);
  314. m_txtctrl->SetFocus();
  315. // layout and show the frame
  316. wxBoxSizer *sizer = new wxBoxSizer(wxVERTICAL);
  317. sizer->Add(header, wxSizerFlags().Expand());
  318. sizer->Add(m_txtctrl, wxSizerFlags(1).Expand());
  319. SetSizer(sizer);
  320. SetSize(600, 350);
  321. Show();
  322. }
  323. MyFrame::~MyFrame()
  324. {
  325. wxLog::SetActiveTarget(m_oldLogger);
  326. // NB: although the OS will terminate all the threads anyhow when the main
  327. // one exits, it's good practice to do it ourselves -- even if it's not
  328. // completely trivial in this example
  329. // tell all the threads to terminate: note that they can't terminate while
  330. // we're deleting them because they will block in their OnExit() -- this is
  331. // important as otherwise we might access invalid array elements
  332. {
  333. wxCriticalSectionLocker locker(wxGetApp().m_critsect);
  334. // check if we have any threads running first
  335. const wxArrayThread& threads = wxGetApp().m_threads;
  336. size_t count = threads.GetCount();
  337. if ( !count )
  338. return;
  339. // set the flag indicating that all threads should exit
  340. wxGetApp().m_shuttingDown = true;
  341. }
  342. // now wait for them to really terminate
  343. wxGetApp().m_semAllDone.Wait();
  344. }
  345. void
  346. MyFrame::DoLogLine(wxTextCtrl *text,
  347. const wxString& timestr,
  348. const wxString& threadstr,
  349. const wxString& msg)
  350. {
  351. text->AppendText(wxString::Format("%9s %10s %s", timestr, threadstr, msg));
  352. }
  353. void
  354. MyFrame::DoLogRecord(wxLogLevel level,
  355. const wxString& msg,
  356. const wxLogRecordInfo& info)
  357. {
  358. // let the default GUI logger treat warnings and errors as they should be
  359. // more noticeable than just another line in the log window and also trace
  360. // messages as there may be too many of them
  361. if ( level <= wxLOG_Warning || level == wxLOG_Trace )
  362. {
  363. m_oldLogger->LogRecord(level, msg, info);
  364. return;
  365. }
  366. DoLogLine
  367. (
  368. m_txtctrl,
  369. wxDateTime(info.timestamp).FormatISOTime(),
  370. info.threadId == wxThread::GetMainId()
  371. ? wxString("main")
  372. : wxString::Format("%lx", info.threadId),
  373. msg + "\n"
  374. );
  375. }
  376. MyThread *MyFrame::CreateThread()
  377. {
  378. MyThread *thread = new MyThread;
  379. if ( thread->Create() != wxTHREAD_NO_ERROR )
  380. {
  381. wxLogError(wxT("Can't create thread!"));
  382. }
  383. wxCriticalSectionLocker enter(wxGetApp().m_critsect);
  384. wxGetApp().m_threads.Add(thread);
  385. return thread;
  386. }
  387. void MyFrame::UpdateThreadStatus()
  388. {
  389. wxCriticalSectionLocker enter(wxGetApp().m_critsect);
  390. // update the counts of running/total threads
  391. size_t nRunning = 0,
  392. nCount = wxGetApp().m_threads.Count();
  393. for ( size_t n = 0; n < nCount; n++ )
  394. {
  395. if ( wxGetApp().m_threads[n]->IsRunning() )
  396. nRunning++;
  397. }
  398. if ( nCount != m_nCount || nRunning != m_nRunning )
  399. {
  400. m_nRunning = nRunning;
  401. m_nCount = nCount;
  402. wxLogStatus(this, wxT("%u threads total, %u running."), unsigned(nCount), unsigned(nRunning));
  403. }
  404. //else: avoid flicker - don't print anything
  405. }
  406. bool MyFrame::Cancelled()
  407. {
  408. wxCriticalSectionLocker lock(m_csCancelled);
  409. return m_cancelled;
  410. }
  411. // ----------------------------------------------------------------------------
  412. // MyFrame - event handlers
  413. // ----------------------------------------------------------------------------
  414. void MyFrame::OnStartThreads(wxCommandEvent& WXUNUSED(event) )
  415. {
  416. static long s_num;
  417. s_num = wxGetNumberFromUser(wxT("How many threads to start: "), wxT(""),
  418. wxT("wxThread sample"), s_num, 1, 10000, this);
  419. if ( s_num == -1 )
  420. {
  421. s_num = 10;
  422. return;
  423. }
  424. unsigned count = unsigned(s_num), n;
  425. wxArrayThread threads;
  426. // first create them all...
  427. for ( n = 0; n < count; n++ )
  428. {
  429. wxThread *thr = CreateThread();
  430. // we want to show the effect of SetPriority(): the first thread will
  431. // have the lowest priority, the second - the highest, all the rest
  432. // the normal one
  433. if ( n == 0 )
  434. thr->SetPriority(wxPRIORITY_MIN);
  435. else if ( n == 1 )
  436. thr->SetPriority(wxPRIORITY_MAX);
  437. else
  438. thr->SetPriority(wxPRIORITY_DEFAULT);
  439. threads.Add(thr);
  440. }
  441. #if wxUSE_STATUSBAR
  442. wxString msg;
  443. msg.Printf(wxT("%d new threads created."), count);
  444. SetStatusText(msg, 1);
  445. #endif // wxUSE_STATUSBAR
  446. // ...and then start them
  447. for ( n = 0; n < count; n++ )
  448. {
  449. threads[n]->Run();
  450. }
  451. }
  452. void MyFrame::OnStartThread(wxCommandEvent& WXUNUSED(event) )
  453. {
  454. MyThread *thread = CreateThread();
  455. if ( thread->Run() != wxTHREAD_NO_ERROR )
  456. {
  457. wxLogError(wxT("Can't start thread!"));
  458. }
  459. #if wxUSE_STATUSBAR
  460. SetStatusText(wxT("New thread started."), 1);
  461. #endif // wxUSE_STATUSBAR
  462. }
  463. void MyFrame::OnStopThread(wxCommandEvent& WXUNUSED(event) )
  464. {
  465. wxThread* toDelete = NULL;
  466. {
  467. wxCriticalSectionLocker enter(wxGetApp().m_critsect);
  468. // stop the last thread
  469. if ( wxGetApp().m_threads.IsEmpty() )
  470. {
  471. wxLogError(wxT("No thread to stop!"));
  472. }
  473. else
  474. {
  475. toDelete = wxGetApp().m_threads.Last();
  476. }
  477. }
  478. if ( toDelete )
  479. {
  480. // This can still crash if the thread gets to delete itself
  481. // in the mean time.
  482. toDelete->Delete();
  483. #if wxUSE_STATUSBAR
  484. SetStatusText(wxT("Last thread stopped."), 1);
  485. #endif // wxUSE_STATUSBAR
  486. }
  487. }
  488. void MyFrame::OnResumeThread(wxCommandEvent& WXUNUSED(event) )
  489. {
  490. wxCriticalSectionLocker enter(wxGetApp().m_critsect);
  491. // resume first suspended thread
  492. size_t n = 0, count = wxGetApp().m_threads.Count();
  493. while ( n < count && !wxGetApp().m_threads[n]->IsPaused() )
  494. n++;
  495. if ( n == count )
  496. {
  497. wxLogError(wxT("No thread to resume!"));
  498. }
  499. else
  500. {
  501. wxGetApp().m_threads[n]->Resume();
  502. #if wxUSE_STATUSBAR
  503. SetStatusText(wxT("Thread resumed."), 1);
  504. #endif // wxUSE_STATUSBAR
  505. }
  506. }
  507. void MyFrame::OnPauseThread(wxCommandEvent& WXUNUSED(event) )
  508. {
  509. wxCriticalSectionLocker enter(wxGetApp().m_critsect);
  510. // pause last running thread
  511. int n = wxGetApp().m_threads.Count() - 1;
  512. while ( n >= 0 && !wxGetApp().m_threads[n]->IsRunning() )
  513. n--;
  514. if ( n < 0 )
  515. {
  516. wxLogError(wxT("No thread to pause!"));
  517. }
  518. else
  519. {
  520. wxGetApp().m_threads[n]->Pause();
  521. #if wxUSE_STATUSBAR
  522. SetStatusText(wxT("Thread paused."), 1);
  523. #endif // wxUSE_STATUSBAR
  524. }
  525. }
  526. void MyFrame::OnIdle(wxIdleEvent& event)
  527. {
  528. UpdateThreadStatus();
  529. event.Skip();
  530. }
  531. void MyFrame::OnQuit(wxCommandEvent& WXUNUSED(event) )
  532. {
  533. Close(true);
  534. }
  535. void MyFrame::OnExecMain(wxCommandEvent& WXUNUSED(event))
  536. {
  537. wxString cmd = wxGetTextFromUser("Please enter the command to execute",
  538. "Enter command",
  539. #ifdef __WXMSW__
  540. "notepad",
  541. #else
  542. "/bin/echo \"Message from another process\"",
  543. #endif
  544. this);
  545. if (cmd.IsEmpty())
  546. return; // user clicked cancel
  547. wxLogMessage(wxT("The exit code from the main program is %ld"),
  548. EXEC(cmd));
  549. }
  550. void MyFrame::OnShowCPUs(wxCommandEvent& WXUNUSED(event))
  551. {
  552. wxString msg;
  553. int nCPUs = wxThread::GetCPUCount();
  554. switch ( nCPUs )
  555. {
  556. case -1:
  557. msg = wxT("Unknown number of CPUs");
  558. break;
  559. case 0:
  560. msg = wxT("WARNING: you're running without any CPUs!");
  561. break;
  562. case 1:
  563. msg = wxT("This system only has one CPU.");
  564. break;
  565. default:
  566. msg.Printf(wxT("This system has %d CPUs"), nCPUs);
  567. }
  568. wxLogMessage(msg);
  569. }
  570. void MyFrame::OnAbout(wxCommandEvent& WXUNUSED(event) )
  571. {
  572. wxMessageDialog dialog(this,
  573. wxT("wxWidgets multithreaded application sample\n")
  574. wxT("(c) 1998 Julian Smart, Guilhem Lavaux\n")
  575. wxT("(c) 2000 Robert Roebling\n")
  576. wxT("(c) 1999,2009 Vadim Zeitlin"),
  577. wxT("About wxThread sample"),
  578. wxOK | wxICON_INFORMATION);
  579. dialog.ShowModal();
  580. }
  581. void MyFrame::OnClear(wxCommandEvent& WXUNUSED(event))
  582. {
  583. m_txtctrl->Clear();
  584. }
  585. void MyFrame::OnUpdateWorker(wxUpdateUIEvent& event)
  586. {
  587. event.Enable( m_dlgProgress == NULL );
  588. }
  589. void MyFrame::OnStartWorker(wxCommandEvent& WXUNUSED(event))
  590. {
  591. MyWorkerThread *thread = new MyWorkerThread(this);
  592. if ( thread->Create() != wxTHREAD_NO_ERROR )
  593. {
  594. wxLogError(wxT("Can't create thread!"));
  595. return;
  596. }
  597. m_dlgProgress = new wxProgressDialog
  598. (
  599. wxT("Progress dialog"),
  600. wxT("Wait until the thread terminates or press [Cancel]"),
  601. 100,
  602. this,
  603. wxPD_CAN_ABORT |
  604. wxPD_APP_MODAL |
  605. wxPD_ELAPSED_TIME |
  606. wxPD_ESTIMATED_TIME |
  607. wxPD_REMAINING_TIME
  608. );
  609. // thread is not running yet, no need for crit sect
  610. m_cancelled = false;
  611. thread->Run();
  612. }
  613. void MyFrame::OnWorkerEvent(wxThreadEvent& event)
  614. {
  615. int n = event.GetInt();
  616. if ( n == -1 )
  617. {
  618. m_dlgProgress->Destroy();
  619. m_dlgProgress = (wxProgressDialog *)NULL;
  620. // the dialog is aborted because the event came from another thread, so
  621. // we may need to wake up the main event loop for the dialog to be
  622. // really closed
  623. wxWakeUpIdle();
  624. }
  625. else
  626. {
  627. if ( !m_dlgProgress->Update(n) )
  628. {
  629. wxCriticalSectionLocker lock(m_csCancelled);
  630. m_cancelled = true;
  631. }
  632. }
  633. }
  634. void MyFrame::OnStartGUIThread(wxCommandEvent& WXUNUSED(event))
  635. {
  636. // we use this to check that disabling logging only affects the main thread
  637. // but the messages from the worker thread will still be logged
  638. wxLogNull noLog;
  639. wxLogMessage("You shouldn't see this message because of wxLogNull");
  640. MyImageDialog dlg(this);
  641. dlg.ShowModal();
  642. }
  643. // ----------------------------------------------------------------------------
  644. // MyImageDialog
  645. // ----------------------------------------------------------------------------
  646. wxBEGIN_EVENT_TABLE(MyImageDialog, wxDialog)
  647. EVT_THREAD(GUITHREAD_EVENT, MyImageDialog::OnGUIThreadEvent)
  648. EVT_PAINT(MyImageDialog::OnPaint)
  649. wxEND_EVENT_TABLE()
  650. MyImageDialog::MyImageDialog(wxFrame *parent)
  651. : wxDialog(parent, wxID_ANY, "Image created by a secondary thread",
  652. wxDefaultPosition, wxSize(GUITHREAD_BMP_SIZE,GUITHREAD_BMP_SIZE)*1.5, wxDEFAULT_DIALOG_STYLE),
  653. m_thread(this)
  654. {
  655. m_nCurrentProgress = 0;
  656. CentreOnScreen();
  657. // NOTE: no need to lock m_csBmp until the thread isn't started:
  658. // create the bitmap
  659. if (!m_bmp.Create(GUITHREAD_BMP_SIZE,GUITHREAD_BMP_SIZE) || !m_bmp.IsOk())
  660. {
  661. wxLogError("Couldn't create the bitmap!");
  662. return;
  663. }
  664. // clean it
  665. wxMemoryDC dc(m_bmp);
  666. dc.SetBackground(*wxBLACK_BRUSH);
  667. dc.Clear();
  668. // draw the bitmap from a secondary thread
  669. if ( m_thread.Create() != wxTHREAD_NO_ERROR ||
  670. m_thread.Run() != wxTHREAD_NO_ERROR )
  671. {
  672. wxLogError(wxT("Can't create/run thread!"));
  673. return;
  674. }
  675. }
  676. MyImageDialog::~MyImageDialog()
  677. {
  678. // in case our thread is still running and for some reason we are destroyed,
  679. // do wait for the thread to complete as it assumes that its MyImageDialog
  680. // pointer is always valid
  681. m_thread.Delete();
  682. }
  683. void MyImageDialog::OnGUIThreadEvent(wxThreadEvent& event)
  684. {
  685. m_nCurrentProgress = int(((float)event.GetInt()*100)/GUITHREAD_NUM_UPDATES);
  686. Refresh();
  687. }
  688. void MyImageDialog::OnPaint(wxPaintEvent& WXUNUSED(evt))
  689. {
  690. wxPaintDC dc(this);
  691. const wxSize& sz = dc.GetSize();
  692. {
  693. // paint the bitmap
  694. wxCriticalSectionLocker locker(m_csBmp);
  695. dc.DrawBitmap(m_bmp, (sz.GetWidth()-GUITHREAD_BMP_SIZE)/2,
  696. (sz.GetHeight()-GUITHREAD_BMP_SIZE)/2);
  697. }
  698. // paint a sort of progress bar with a 10px border:
  699. dc.SetBrush(*wxRED_BRUSH);
  700. dc.DrawRectangle(10,10, m_nCurrentProgress*(sz.GetWidth()-20)/100,30);
  701. dc.SetTextForeground(*wxBLUE);
  702. dc.DrawText(wxString::Format("%d%%", m_nCurrentProgress),
  703. (sz.GetWidth()-dc.GetCharWidth()*2)/2,
  704. 25-dc.GetCharHeight()/2);
  705. }
  706. // ----------------------------------------------------------------------------
  707. // MyThread
  708. // ----------------------------------------------------------------------------
  709. MyThread::MyThread()
  710. : wxThread()
  711. {
  712. m_count = 0;
  713. }
  714. MyThread::~MyThread()
  715. {
  716. wxCriticalSectionLocker locker(wxGetApp().m_critsect);
  717. wxArrayThread& threads = wxGetApp().m_threads;
  718. threads.Remove(this);
  719. if ( threads.IsEmpty() )
  720. {
  721. // signal the main thread that there are no more threads left if it is
  722. // waiting for us
  723. if ( wxGetApp().m_shuttingDown )
  724. {
  725. wxGetApp().m_shuttingDown = false;
  726. wxGetApp().m_semAllDone.Post();
  727. }
  728. }
  729. }
  730. wxThread::ExitCode MyThread::Entry()
  731. {
  732. wxLogMessage("Thread started (priority = %u).", GetPriority());
  733. for ( m_count = 0; m_count < 10; m_count++ )
  734. {
  735. // check if the application is shutting down: in this case all threads
  736. // should stop a.s.a.p.
  737. {
  738. wxCriticalSectionLocker locker(wxGetApp().m_critsect);
  739. if ( wxGetApp().m_shuttingDown )
  740. return NULL;
  741. }
  742. // check if just this thread was asked to exit
  743. if ( TestDestroy() )
  744. break;
  745. wxLogMessage("Thread progress: %u", m_count);
  746. // wxSleep() can't be called from non-GUI thread!
  747. wxThread::Sleep(1000);
  748. }
  749. wxLogMessage("Thread finished.");
  750. return NULL;
  751. }
  752. // ----------------------------------------------------------------------------
  753. // MyWorkerThread
  754. // ----------------------------------------------------------------------------
  755. // define this symbol to 1 to test if the YieldFor() call in the wxProgressDialog::Update
  756. // function provokes a race condition in which the second wxThreadEvent posted by
  757. // MyWorkerThread::Entry is processed by the YieldFor() call of wxProgressDialog::Update
  758. // and results in the destruction of the progress dialog itself, resulting in a crash later.
  759. #define TEST_YIELD_RACE_CONDITION 0
  760. MyWorkerThread::MyWorkerThread(MyFrame *frame)
  761. : wxThread()
  762. {
  763. m_frame = frame;
  764. m_count = 0;
  765. }
  766. void MyWorkerThread::OnExit()
  767. {
  768. }
  769. wxThread::ExitCode MyWorkerThread::Entry()
  770. {
  771. #if TEST_YIELD_RACE_CONDITION
  772. if ( TestDestroy() )
  773. return NULL;
  774. wxThreadEvent event( wxEVT_THREAD, WORKER_EVENT );
  775. event.SetInt( 50 );
  776. wxQueueEvent( m_frame, event.Clone() );
  777. event.SetInt(-1);
  778. wxQueueEvent( m_frame, event.Clone() );
  779. #else
  780. for ( m_count = 0; !m_frame->Cancelled() && (m_count < 100); m_count++ )
  781. {
  782. // check if we were asked to exit
  783. if ( TestDestroy() )
  784. break;
  785. // create any type of command event here
  786. wxThreadEvent event( wxEVT_THREAD, WORKER_EVENT );
  787. event.SetInt( m_count );
  788. // send in a thread-safe way
  789. wxQueueEvent( m_frame, event.Clone() );
  790. wxMilliSleep(200);
  791. }
  792. wxThreadEvent event( wxEVT_THREAD, WORKER_EVENT );
  793. event.SetInt(-1); // that's all
  794. wxQueueEvent( m_frame, event.Clone() );
  795. #endif
  796. return NULL;
  797. }
  798. // ----------------------------------------------------------------------------
  799. // MyGUIThread
  800. // ----------------------------------------------------------------------------
  801. wxThread::ExitCode MyGUIThread::Entry()
  802. {
  803. // uncomment this to check that disabling logging here does disable it for
  804. // this thread -- but not the main one if you also comment out wxLogNull
  805. // line in MyFrame::OnStartGUIThread()
  806. //wxLogNull noLog;
  807. // this goes to the main window
  808. wxLogMessage("GUI thread starting");
  809. // use a thread-specific log target for this thread to show that its
  810. // messages don't appear in the main window while it runs
  811. wxLogBuffer logBuf;
  812. wxLog::SetThreadActiveTarget(&logBuf);
  813. for (int i=0; i<GUITHREAD_NUM_UPDATES && !TestDestroy(); i++)
  814. {
  815. // inform the GUI toolkit that we're going to use GUI functions
  816. // from a secondary thread:
  817. wxMutexGuiEnter();
  818. {
  819. wxCriticalSectionLocker lock(m_dlg->m_csBmp);
  820. // draw some more stuff on the bitmap
  821. wxMemoryDC dc(m_dlg->m_bmp);
  822. dc.SetBrush((i%2)==0 ? *wxBLUE_BRUSH : *wxGREEN_BRUSH);
  823. dc.DrawRectangle(rand()%GUITHREAD_BMP_SIZE, rand()%GUITHREAD_BMP_SIZE, 30, 30);
  824. // simulate long drawing time:
  825. wxMilliSleep(200);
  826. }
  827. // if we don't release the GUI mutex the MyImageDialog won't be able to refresh
  828. wxMutexGuiLeave();
  829. // notify the dialog that another piece of our masterpiece is complete:
  830. wxThreadEvent event( wxEVT_THREAD, GUITHREAD_EVENT );
  831. event.SetInt(i+1);
  832. wxQueueEvent( m_dlg, event.Clone() );
  833. if ( !((i + 1) % 10) )
  834. {
  835. // this message will go to the buffer
  836. wxLogMessage("Step #%d.", i + 1);
  837. }
  838. // give the main thread the time to refresh before we lock the GUI mutex again
  839. // FIXME: find a better way to do this!
  840. wxMilliSleep(100);
  841. }
  842. // now remove the thread-specific thread target
  843. wxLog::SetThreadActiveTarget(NULL);
  844. // so that this goes to the main window again
  845. wxLogMessage("GUI thread finished.");
  846. return (ExitCode)0;
  847. }