| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077 |
- /////////////////////////////////////////////////////////////////////////////
- // Name: thread.cpp
- // Purpose: wxWidgets thread sample
- // Author: Guilhem Lavaux, Vadim Zeitlin
- // Modified by:
- // Created: 06/16/98
- // Copyright: (c) 1998-2009 wxWidgets team
- // Licence: wxWindows licence
- /////////////////////////////////////////////////////////////////////////////
- // ============================================================================
- // declarations
- // ============================================================================
- // ----------------------------------------------------------------------------
- // headers
- // ----------------------------------------------------------------------------
- // For compilers that support precompilation, includes "wx/wx.h".
- #include "wx/wxprec.h"
- #ifdef __BORLANDC__
- #pragma hdrstop
- #endif
- #ifndef WX_PRECOMP
- #include "wx/wx.h"
- #endif
- #if !wxUSE_THREADS
- #error "This sample requires thread support!"
- #endif // wxUSE_THREADS
- #include "wx/thread.h"
- #include "wx/dynarray.h"
- #include "wx/numdlg.h"
- #include "wx/progdlg.h"
- // ----------------------------------------------------------------------------
- // resources
- // ----------------------------------------------------------------------------
- #include "../sample.xpm"
- // ----------------------------------------------------------------------------
- // private classes
- // ----------------------------------------------------------------------------
- // define this to use wxExecute in the exec tests, otherwise just use system
- #define USE_EXECUTE
- #ifdef USE_EXECUTE
- #define EXEC(cmd) wxExecute((cmd), wxEXEC_SYNC)
- #else
- #define EXEC(cmd) system(cmd)
- #endif
- class MyThread;
- WX_DEFINE_ARRAY_PTR(wxThread *, wxArrayThread);
- // ----------------------------------------------------------------------------
- // the application object
- // ----------------------------------------------------------------------------
- class MyApp : public wxApp
- {
- public:
- MyApp();
- virtual ~MyApp(){};
- virtual bool OnInit();
- // critical section protects access to all of the fields below
- wxCriticalSection m_critsect;
- // all the threads currently alive - as soon as the thread terminates, it's
- // removed from the array
- wxArrayThread m_threads;
- // semaphore used to wait for the threads to exit, see MyFrame::OnQuit()
- wxSemaphore m_semAllDone;
- // indicates that we're shutting down and all threads should exit
- bool m_shuttingDown;
- };
- // ----------------------------------------------------------------------------
- // the main application frame
- // ----------------------------------------------------------------------------
- class MyFrame : public wxFrame,
- private wxLog
- {
- public:
- // ctor
- MyFrame(const wxString& title);
- virtual ~MyFrame();
- // accessors for MyWorkerThread (called in its context!)
- bool Cancelled();
- protected:
- virtual void DoLogRecord(wxLogLevel level,
- const wxString& msg,
- const wxLogRecordInfo& info);
- private:
- // event handlers
- // --------------
- void OnQuit(wxCommandEvent& event);
- void OnClear(wxCommandEvent& event);
- void OnStartThread(wxCommandEvent& event);
- void OnStartThreads(wxCommandEvent& event);
- void OnStopThread(wxCommandEvent& event);
- void OnPauseThread(wxCommandEvent& event);
- void OnResumeThread(wxCommandEvent& event);
- void OnStartWorker(wxCommandEvent& event);
- void OnExecMain(wxCommandEvent& event);
- void OnStartGUIThread(wxCommandEvent& event);
- void OnShowCPUs(wxCommandEvent& event);
- void OnAbout(wxCommandEvent& event);
- void OnIdle(wxIdleEvent &event);
- void OnWorkerEvent(wxThreadEvent& event);
- void OnUpdateWorker(wxUpdateUIEvent& event);
- // logging helper
- void DoLogLine(wxTextCtrl *text,
- const wxString& timestr,
- const wxString& threadstr,
- const wxString& msg);
- // thread helper functions
- // -----------------------
- // helper function - creates a new thread (but doesn't run it)
- MyThread *CreateThread();
- // update display in our status bar: called during idle handling
- void UpdateThreadStatus();
- // internal variables
- // ------------------
- // just some place to put our messages in
- wxTextCtrl *m_txtctrl;
- // old log target, we replace it with one using m_txtctrl during this
- // frame life time
- wxLog *m_oldLogger;
- // the array of pending messages to be displayed and the critical section
- // protecting it
- wxArrayString m_messages;
- wxCriticalSection m_csMessages;
- // remember the number of running threads and total number of threads
- size_t m_nRunning,
- m_nCount;
- // the progress dialog which we show while worker thread is running
- wxProgressDialog *m_dlgProgress;
- // was the worker thread cancelled by user?
- bool m_cancelled;
- wxCriticalSection m_csCancelled; // protects m_cancelled
- wxDECLARE_EVENT_TABLE();
- };
- // ----------------------------------------------------------------------------
- // constants
- // ----------------------------------------------------------------------------
- // ID for the menu commands
- enum
- {
- THREAD_QUIT = wxID_EXIT,
- THREAD_ABOUT = wxID_ABOUT,
- THREAD_TEXT = 101,
- THREAD_CLEAR,
- THREAD_START_THREAD = 201,
- THREAD_START_THREADS,
- THREAD_STOP_THREAD,
- THREAD_PAUSE_THREAD,
- THREAD_RESUME_THREAD,
- THREAD_START_WORKER,
- THREAD_EXEC_MAIN,
- THREAD_START_GUI_THREAD,
- THREAD_SHOWCPUS,
- WORKER_EVENT = wxID_HIGHEST+1, // this one gets sent from MyWorkerThread
- GUITHREAD_EVENT // this one gets sent from MyGUIThread
- };
- // ----------------------------------------------------------------------------
- // a simple thread
- // ----------------------------------------------------------------------------
- class MyThread : public wxThread
- {
- public:
- MyThread();
- virtual ~MyThread();
- // thread execution starts here
- virtual void *Entry();
- public:
- unsigned m_count;
- };
- // ----------------------------------------------------------------------------
- // a worker thread
- // ----------------------------------------------------------------------------
- class MyWorkerThread : public wxThread
- {
- public:
- MyWorkerThread(MyFrame *frame);
- // thread execution starts here
- virtual void *Entry();
- // called when the thread exits - whether it terminates normally or is
- // stopped with Delete() (but not when it is Kill()ed!)
- virtual void OnExit();
- public:
- MyFrame *m_frame;
- unsigned m_count;
- };
- // ----------------------------------------------------------------------------
- // a thread which executes GUI calls using wxMutexGuiEnter/Leave
- // ----------------------------------------------------------------------------
- #define GUITHREAD_BMP_SIZE 300
- #define GUITHREAD_NUM_UPDATES 50
- class MyImageDialog;
- class MyGUIThread : public wxThread
- {
- public:
- MyGUIThread(MyImageDialog *dlg) : wxThread(wxTHREAD_JOINABLE)
- {
- m_dlg = dlg;
- }
- virtual ExitCode Entry();
- private:
- MyImageDialog *m_dlg;
- };
- // ----------------------------------------------------------------------------
- // an helper dialog used by MyFrame::OnStartGUIThread
- // ----------------------------------------------------------------------------
- class MyImageDialog: public wxDialog
- {
- public:
- // ctor
- MyImageDialog(wxFrame *frame);
- ~MyImageDialog();
- // stuff used by MyGUIThread:
- wxBitmap m_bmp; // the bitmap drawn by MyGUIThread
- wxCriticalSection m_csBmp; // protects m_bmp
- private:
- void OnGUIThreadEvent(wxThreadEvent& event);
- void OnPaint(wxPaintEvent&);
- MyGUIThread m_thread;
- int m_nCurrentProgress;
- wxDECLARE_EVENT_TABLE();
- };
- // ============================================================================
- // implementation
- // ============================================================================
- // ----------------------------------------------------------------------------
- // the application class
- // ----------------------------------------------------------------------------
- // Create a new application object
- IMPLEMENT_APP(MyApp)
- MyApp::MyApp()
- {
- m_shuttingDown = false;
- }
- // `Main program' equivalent, creating windows and returning main app frame
- bool MyApp::OnInit()
- {
- if ( !wxApp::OnInit() )
- return false;
- // uncomment this to get some debugging messages from the trace code
- // on the console (or just set WXTRACE env variable to include "thread")
- wxLog::AddTraceMask("thread");
- // Create the main frame window
- new MyFrame("wxWidgets threads sample");
- return true;
- }
- // ----------------------------------------------------------------------------
- // MyFrame
- // ----------------------------------------------------------------------------
- wxBEGIN_EVENT_TABLE(MyFrame, wxFrame)
- EVT_MENU(THREAD_QUIT, MyFrame::OnQuit)
- EVT_MENU(THREAD_CLEAR, MyFrame::OnClear)
- EVT_MENU(THREAD_START_THREAD, MyFrame::OnStartThread)
- EVT_MENU(THREAD_START_THREADS, MyFrame::OnStartThreads)
- EVT_MENU(THREAD_STOP_THREAD, MyFrame::OnStopThread)
- EVT_MENU(THREAD_PAUSE_THREAD, MyFrame::OnPauseThread)
- EVT_MENU(THREAD_RESUME_THREAD, MyFrame::OnResumeThread)
- EVT_MENU(THREAD_START_WORKER, MyFrame::OnStartWorker)
- EVT_MENU(THREAD_EXEC_MAIN, MyFrame::OnExecMain)
- EVT_MENU(THREAD_START_GUI_THREAD, MyFrame::OnStartGUIThread)
- EVT_MENU(THREAD_SHOWCPUS, MyFrame::OnShowCPUs)
- EVT_MENU(THREAD_ABOUT, MyFrame::OnAbout)
- EVT_UPDATE_UI(THREAD_START_WORKER, MyFrame::OnUpdateWorker)
- EVT_THREAD(WORKER_EVENT, MyFrame::OnWorkerEvent)
- EVT_IDLE(MyFrame::OnIdle)
- wxEND_EVENT_TABLE()
- // My frame constructor
- MyFrame::MyFrame(const wxString& title)
- : wxFrame(NULL, wxID_ANY, title)
- {
- m_oldLogger = wxLog::GetActiveTarget();
- SetIcon(wxICON(sample));
- // Make a menubar
- wxMenuBar *menuBar = new wxMenuBar;
- wxMenu *menuFile = new wxMenu;
- menuFile->Append(THREAD_CLEAR, wxT("&Clear log\tCtrl-L"));
- menuFile->AppendSeparator();
- menuFile->Append(THREAD_QUIT, wxT("E&xit\tAlt-X"));
- menuBar->Append(menuFile, wxT("&File"));
- wxMenu *menuThread = new wxMenu;
- menuThread->Append(THREAD_START_THREAD, wxT("&Start a new thread\tCtrl-N"));
- menuThread->Append(THREAD_START_THREADS, wxT("Start &many threads at once"));
- menuThread->Append(THREAD_STOP_THREAD, wxT("S&top the last spawned thread\tCtrl-S"));
- menuThread->AppendSeparator();
- menuThread->Append(THREAD_PAUSE_THREAD, wxT("&Pause the last spawned running thread\tCtrl-P"));
- menuThread->Append(THREAD_RESUME_THREAD, wxT("&Resume the first suspended thread\tCtrl-R"));
- menuThread->AppendSeparator();
- menuThread->Append(THREAD_START_WORKER, wxT("Start a &worker thread\tCtrl-W"));
- menuThread->Append(THREAD_EXEC_MAIN, wxT("&Launch a program from main thread\tF5"));
- menuThread->Append(THREAD_START_GUI_THREAD, wxT("Launch a &GUI thread\tF6"));
- menuBar->Append(menuThread, wxT("&Thread"));
- wxMenu *menuHelp = new wxMenu;
- menuHelp->Append(THREAD_SHOWCPUS, wxT("&Show CPU count"));
- menuHelp->AppendSeparator();
- menuHelp->Append(THREAD_ABOUT, wxT("&About"));
- menuBar->Append(menuHelp, wxT("&Help"));
- SetMenuBar(menuBar);
- m_nRunning = m_nCount = 0;
- m_dlgProgress = NULL;
- #if wxUSE_STATUSBAR
- CreateStatusBar(2);
- #endif // wxUSE_STATUSBAR
- // create the logging text control and a header showing the meaning of the
- // different columns
- wxTextCtrl *header = new wxTextCtrl(this, wxID_ANY, "",
- wxDefaultPosition, wxDefaultSize,
- wxTE_READONLY);
- DoLogLine(header, " Time", " Thread", "Message");
- m_txtctrl = new wxTextCtrl(this, wxID_ANY, "",
- wxDefaultPosition, wxDefaultSize,
- wxTE_MULTILINE | wxTE_READONLY);
- wxLog::SetActiveTarget(this);
- // use fixed width font to align output in nice columns
- wxFont font(wxNORMAL_FONT->GetPointSize(), wxFONTFAMILY_TELETYPE,
- wxFONTSTYLE_NORMAL, wxFONTWEIGHT_NORMAL);
- header->SetFont(font);
- m_txtctrl->SetFont(font);
- m_txtctrl->SetFocus();
- // layout and show the frame
- wxBoxSizer *sizer = new wxBoxSizer(wxVERTICAL);
- sizer->Add(header, wxSizerFlags().Expand());
- sizer->Add(m_txtctrl, wxSizerFlags(1).Expand());
- SetSizer(sizer);
- SetSize(600, 350);
- Show();
- }
- MyFrame::~MyFrame()
- {
- wxLog::SetActiveTarget(m_oldLogger);
- // NB: although the OS will terminate all the threads anyhow when the main
- // one exits, it's good practice to do it ourselves -- even if it's not
- // completely trivial in this example
- // tell all the threads to terminate: note that they can't terminate while
- // we're deleting them because they will block in their OnExit() -- this is
- // important as otherwise we might access invalid array elements
- {
- wxCriticalSectionLocker locker(wxGetApp().m_critsect);
- // check if we have any threads running first
- const wxArrayThread& threads = wxGetApp().m_threads;
- size_t count = threads.GetCount();
- if ( !count )
- return;
- // set the flag indicating that all threads should exit
- wxGetApp().m_shuttingDown = true;
- }
- // now wait for them to really terminate
- wxGetApp().m_semAllDone.Wait();
- }
- void
- MyFrame::DoLogLine(wxTextCtrl *text,
- const wxString& timestr,
- const wxString& threadstr,
- const wxString& msg)
- {
- text->AppendText(wxString::Format("%9s %10s %s", timestr, threadstr, msg));
- }
- void
- MyFrame::DoLogRecord(wxLogLevel level,
- const wxString& msg,
- const wxLogRecordInfo& info)
- {
- // let the default GUI logger treat warnings and errors as they should be
- // more noticeable than just another line in the log window and also trace
- // messages as there may be too many of them
- if ( level <= wxLOG_Warning || level == wxLOG_Trace )
- {
- m_oldLogger->LogRecord(level, msg, info);
- return;
- }
- DoLogLine
- (
- m_txtctrl,
- wxDateTime(info.timestamp).FormatISOTime(),
- info.threadId == wxThread::GetMainId()
- ? wxString("main")
- : wxString::Format("%lx", info.threadId),
- msg + "\n"
- );
- }
- MyThread *MyFrame::CreateThread()
- {
- MyThread *thread = new MyThread;
- if ( thread->Create() != wxTHREAD_NO_ERROR )
- {
- wxLogError(wxT("Can't create thread!"));
- }
- wxCriticalSectionLocker enter(wxGetApp().m_critsect);
- wxGetApp().m_threads.Add(thread);
- return thread;
- }
- void MyFrame::UpdateThreadStatus()
- {
- wxCriticalSectionLocker enter(wxGetApp().m_critsect);
- // update the counts of running/total threads
- size_t nRunning = 0,
- nCount = wxGetApp().m_threads.Count();
- for ( size_t n = 0; n < nCount; n++ )
- {
- if ( wxGetApp().m_threads[n]->IsRunning() )
- nRunning++;
- }
- if ( nCount != m_nCount || nRunning != m_nRunning )
- {
- m_nRunning = nRunning;
- m_nCount = nCount;
- wxLogStatus(this, wxT("%u threads total, %u running."), unsigned(nCount), unsigned(nRunning));
- }
- //else: avoid flicker - don't print anything
- }
- bool MyFrame::Cancelled()
- {
- wxCriticalSectionLocker lock(m_csCancelled);
- return m_cancelled;
- }
- // ----------------------------------------------------------------------------
- // MyFrame - event handlers
- // ----------------------------------------------------------------------------
- void MyFrame::OnStartThreads(wxCommandEvent& WXUNUSED(event) )
- {
- static long s_num;
- s_num = wxGetNumberFromUser(wxT("How many threads to start: "), wxT(""),
- wxT("wxThread sample"), s_num, 1, 10000, this);
- if ( s_num == -1 )
- {
- s_num = 10;
- return;
- }
- unsigned count = unsigned(s_num), n;
- wxArrayThread threads;
- // first create them all...
- for ( n = 0; n < count; n++ )
- {
- wxThread *thr = CreateThread();
- // we want to show the effect of SetPriority(): the first thread will
- // have the lowest priority, the second - the highest, all the rest
- // the normal one
- if ( n == 0 )
- thr->SetPriority(wxPRIORITY_MIN);
- else if ( n == 1 )
- thr->SetPriority(wxPRIORITY_MAX);
- else
- thr->SetPriority(wxPRIORITY_DEFAULT);
- threads.Add(thr);
- }
- #if wxUSE_STATUSBAR
- wxString msg;
- msg.Printf(wxT("%d new threads created."), count);
- SetStatusText(msg, 1);
- #endif // wxUSE_STATUSBAR
- // ...and then start them
- for ( n = 0; n < count; n++ )
- {
- threads[n]->Run();
- }
- }
- void MyFrame::OnStartThread(wxCommandEvent& WXUNUSED(event) )
- {
- MyThread *thread = CreateThread();
- if ( thread->Run() != wxTHREAD_NO_ERROR )
- {
- wxLogError(wxT("Can't start thread!"));
- }
- #if wxUSE_STATUSBAR
- SetStatusText(wxT("New thread started."), 1);
- #endif // wxUSE_STATUSBAR
- }
- void MyFrame::OnStopThread(wxCommandEvent& WXUNUSED(event) )
- {
- wxThread* toDelete = NULL;
- {
- wxCriticalSectionLocker enter(wxGetApp().m_critsect);
- // stop the last thread
- if ( wxGetApp().m_threads.IsEmpty() )
- {
- wxLogError(wxT("No thread to stop!"));
- }
- else
- {
- toDelete = wxGetApp().m_threads.Last();
- }
- }
- if ( toDelete )
- {
- // This can still crash if the thread gets to delete itself
- // in the mean time.
- toDelete->Delete();
- #if wxUSE_STATUSBAR
- SetStatusText(wxT("Last thread stopped."), 1);
- #endif // wxUSE_STATUSBAR
- }
- }
- void MyFrame::OnResumeThread(wxCommandEvent& WXUNUSED(event) )
- {
- wxCriticalSectionLocker enter(wxGetApp().m_critsect);
- // resume first suspended thread
- size_t n = 0, count = wxGetApp().m_threads.Count();
- while ( n < count && !wxGetApp().m_threads[n]->IsPaused() )
- n++;
- if ( n == count )
- {
- wxLogError(wxT("No thread to resume!"));
- }
- else
- {
- wxGetApp().m_threads[n]->Resume();
- #if wxUSE_STATUSBAR
- SetStatusText(wxT("Thread resumed."), 1);
- #endif // wxUSE_STATUSBAR
- }
- }
- void MyFrame::OnPauseThread(wxCommandEvent& WXUNUSED(event) )
- {
- wxCriticalSectionLocker enter(wxGetApp().m_critsect);
- // pause last running thread
- int n = wxGetApp().m_threads.Count() - 1;
- while ( n >= 0 && !wxGetApp().m_threads[n]->IsRunning() )
- n--;
- if ( n < 0 )
- {
- wxLogError(wxT("No thread to pause!"));
- }
- else
- {
- wxGetApp().m_threads[n]->Pause();
- #if wxUSE_STATUSBAR
- SetStatusText(wxT("Thread paused."), 1);
- #endif // wxUSE_STATUSBAR
- }
- }
- void MyFrame::OnIdle(wxIdleEvent& event)
- {
- UpdateThreadStatus();
- event.Skip();
- }
- void MyFrame::OnQuit(wxCommandEvent& WXUNUSED(event) )
- {
- Close(true);
- }
- void MyFrame::OnExecMain(wxCommandEvent& WXUNUSED(event))
- {
- wxString cmd = wxGetTextFromUser("Please enter the command to execute",
- "Enter command",
- #ifdef __WXMSW__
- "notepad",
- #else
- "/bin/echo \"Message from another process\"",
- #endif
- this);
- if (cmd.IsEmpty())
- return; // user clicked cancel
- wxLogMessage(wxT("The exit code from the main program is %ld"),
- EXEC(cmd));
- }
- void MyFrame::OnShowCPUs(wxCommandEvent& WXUNUSED(event))
- {
- wxString msg;
- int nCPUs = wxThread::GetCPUCount();
- switch ( nCPUs )
- {
- case -1:
- msg = wxT("Unknown number of CPUs");
- break;
- case 0:
- msg = wxT("WARNING: you're running without any CPUs!");
- break;
- case 1:
- msg = wxT("This system only has one CPU.");
- break;
- default:
- msg.Printf(wxT("This system has %d CPUs"), nCPUs);
- }
- wxLogMessage(msg);
- }
- void MyFrame::OnAbout(wxCommandEvent& WXUNUSED(event) )
- {
- wxMessageDialog dialog(this,
- wxT("wxWidgets multithreaded application sample\n")
- wxT("(c) 1998 Julian Smart, Guilhem Lavaux\n")
- wxT("(c) 2000 Robert Roebling\n")
- wxT("(c) 1999,2009 Vadim Zeitlin"),
- wxT("About wxThread sample"),
- wxOK | wxICON_INFORMATION);
- dialog.ShowModal();
- }
- void MyFrame::OnClear(wxCommandEvent& WXUNUSED(event))
- {
- m_txtctrl->Clear();
- }
- void MyFrame::OnUpdateWorker(wxUpdateUIEvent& event)
- {
- event.Enable( m_dlgProgress == NULL );
- }
- void MyFrame::OnStartWorker(wxCommandEvent& WXUNUSED(event))
- {
- MyWorkerThread *thread = new MyWorkerThread(this);
- if ( thread->Create() != wxTHREAD_NO_ERROR )
- {
- wxLogError(wxT("Can't create thread!"));
- return;
- }
- m_dlgProgress = new wxProgressDialog
- (
- wxT("Progress dialog"),
- wxT("Wait until the thread terminates or press [Cancel]"),
- 100,
- this,
- wxPD_CAN_ABORT |
- wxPD_APP_MODAL |
- wxPD_ELAPSED_TIME |
- wxPD_ESTIMATED_TIME |
- wxPD_REMAINING_TIME
- );
- // thread is not running yet, no need for crit sect
- m_cancelled = false;
- thread->Run();
- }
- void MyFrame::OnWorkerEvent(wxThreadEvent& event)
- {
- int n = event.GetInt();
- if ( n == -1 )
- {
- m_dlgProgress->Destroy();
- m_dlgProgress = (wxProgressDialog *)NULL;
- // the dialog is aborted because the event came from another thread, so
- // we may need to wake up the main event loop for the dialog to be
- // really closed
- wxWakeUpIdle();
- }
- else
- {
- if ( !m_dlgProgress->Update(n) )
- {
- wxCriticalSectionLocker lock(m_csCancelled);
- m_cancelled = true;
- }
- }
- }
- void MyFrame::OnStartGUIThread(wxCommandEvent& WXUNUSED(event))
- {
- // we use this to check that disabling logging only affects the main thread
- // but the messages from the worker thread will still be logged
- wxLogNull noLog;
- wxLogMessage("You shouldn't see this message because of wxLogNull");
- MyImageDialog dlg(this);
- dlg.ShowModal();
- }
- // ----------------------------------------------------------------------------
- // MyImageDialog
- // ----------------------------------------------------------------------------
- wxBEGIN_EVENT_TABLE(MyImageDialog, wxDialog)
- EVT_THREAD(GUITHREAD_EVENT, MyImageDialog::OnGUIThreadEvent)
- EVT_PAINT(MyImageDialog::OnPaint)
- wxEND_EVENT_TABLE()
- MyImageDialog::MyImageDialog(wxFrame *parent)
- : wxDialog(parent, wxID_ANY, "Image created by a secondary thread",
- wxDefaultPosition, wxSize(GUITHREAD_BMP_SIZE,GUITHREAD_BMP_SIZE)*1.5, wxDEFAULT_DIALOG_STYLE),
- m_thread(this)
- {
- m_nCurrentProgress = 0;
- CentreOnScreen();
- // NOTE: no need to lock m_csBmp until the thread isn't started:
- // create the bitmap
- if (!m_bmp.Create(GUITHREAD_BMP_SIZE,GUITHREAD_BMP_SIZE) || !m_bmp.IsOk())
- {
- wxLogError("Couldn't create the bitmap!");
- return;
- }
- // clean it
- wxMemoryDC dc(m_bmp);
- dc.SetBackground(*wxBLACK_BRUSH);
- dc.Clear();
- // draw the bitmap from a secondary thread
- if ( m_thread.Create() != wxTHREAD_NO_ERROR ||
- m_thread.Run() != wxTHREAD_NO_ERROR )
- {
- wxLogError(wxT("Can't create/run thread!"));
- return;
- }
- }
- MyImageDialog::~MyImageDialog()
- {
- // in case our thread is still running and for some reason we are destroyed,
- // do wait for the thread to complete as it assumes that its MyImageDialog
- // pointer is always valid
- m_thread.Delete();
- }
- void MyImageDialog::OnGUIThreadEvent(wxThreadEvent& event)
- {
- m_nCurrentProgress = int(((float)event.GetInt()*100)/GUITHREAD_NUM_UPDATES);
- Refresh();
- }
- void MyImageDialog::OnPaint(wxPaintEvent& WXUNUSED(evt))
- {
- wxPaintDC dc(this);
- const wxSize& sz = dc.GetSize();
- {
- // paint the bitmap
- wxCriticalSectionLocker locker(m_csBmp);
- dc.DrawBitmap(m_bmp, (sz.GetWidth()-GUITHREAD_BMP_SIZE)/2,
- (sz.GetHeight()-GUITHREAD_BMP_SIZE)/2);
- }
- // paint a sort of progress bar with a 10px border:
- dc.SetBrush(*wxRED_BRUSH);
- dc.DrawRectangle(10,10, m_nCurrentProgress*(sz.GetWidth()-20)/100,30);
- dc.SetTextForeground(*wxBLUE);
- dc.DrawText(wxString::Format("%d%%", m_nCurrentProgress),
- (sz.GetWidth()-dc.GetCharWidth()*2)/2,
- 25-dc.GetCharHeight()/2);
- }
- // ----------------------------------------------------------------------------
- // MyThread
- // ----------------------------------------------------------------------------
- MyThread::MyThread()
- : wxThread()
- {
- m_count = 0;
- }
- MyThread::~MyThread()
- {
- wxCriticalSectionLocker locker(wxGetApp().m_critsect);
- wxArrayThread& threads = wxGetApp().m_threads;
- threads.Remove(this);
- if ( threads.IsEmpty() )
- {
- // signal the main thread that there are no more threads left if it is
- // waiting for us
- if ( wxGetApp().m_shuttingDown )
- {
- wxGetApp().m_shuttingDown = false;
- wxGetApp().m_semAllDone.Post();
- }
- }
- }
- wxThread::ExitCode MyThread::Entry()
- {
- wxLogMessage("Thread started (priority = %u).", GetPriority());
- for ( m_count = 0; m_count < 10; m_count++ )
- {
- // check if the application is shutting down: in this case all threads
- // should stop a.s.a.p.
- {
- wxCriticalSectionLocker locker(wxGetApp().m_critsect);
- if ( wxGetApp().m_shuttingDown )
- return NULL;
- }
- // check if just this thread was asked to exit
- if ( TestDestroy() )
- break;
- wxLogMessage("Thread progress: %u", m_count);
- // wxSleep() can't be called from non-GUI thread!
- wxThread::Sleep(1000);
- }
- wxLogMessage("Thread finished.");
- return NULL;
- }
- // ----------------------------------------------------------------------------
- // MyWorkerThread
- // ----------------------------------------------------------------------------
- // define this symbol to 1 to test if the YieldFor() call in the wxProgressDialog::Update
- // function provokes a race condition in which the second wxThreadEvent posted by
- // MyWorkerThread::Entry is processed by the YieldFor() call of wxProgressDialog::Update
- // and results in the destruction of the progress dialog itself, resulting in a crash later.
- #define TEST_YIELD_RACE_CONDITION 0
- MyWorkerThread::MyWorkerThread(MyFrame *frame)
- : wxThread()
- {
- m_frame = frame;
- m_count = 0;
- }
- void MyWorkerThread::OnExit()
- {
- }
- wxThread::ExitCode MyWorkerThread::Entry()
- {
- #if TEST_YIELD_RACE_CONDITION
- if ( TestDestroy() )
- return NULL;
- wxThreadEvent event( wxEVT_THREAD, WORKER_EVENT );
- event.SetInt( 50 );
- wxQueueEvent( m_frame, event.Clone() );
- event.SetInt(-1);
- wxQueueEvent( m_frame, event.Clone() );
- #else
- for ( m_count = 0; !m_frame->Cancelled() && (m_count < 100); m_count++ )
- {
- // check if we were asked to exit
- if ( TestDestroy() )
- break;
- // create any type of command event here
- wxThreadEvent event( wxEVT_THREAD, WORKER_EVENT );
- event.SetInt( m_count );
- // send in a thread-safe way
- wxQueueEvent( m_frame, event.Clone() );
- wxMilliSleep(200);
- }
- wxThreadEvent event( wxEVT_THREAD, WORKER_EVENT );
- event.SetInt(-1); // that's all
- wxQueueEvent( m_frame, event.Clone() );
- #endif
- return NULL;
- }
- // ----------------------------------------------------------------------------
- // MyGUIThread
- // ----------------------------------------------------------------------------
- wxThread::ExitCode MyGUIThread::Entry()
- {
- // uncomment this to check that disabling logging here does disable it for
- // this thread -- but not the main one if you also comment out wxLogNull
- // line in MyFrame::OnStartGUIThread()
- //wxLogNull noLog;
- // this goes to the main window
- wxLogMessage("GUI thread starting");
- // use a thread-specific log target for this thread to show that its
- // messages don't appear in the main window while it runs
- wxLogBuffer logBuf;
- wxLog::SetThreadActiveTarget(&logBuf);
- for (int i=0; i<GUITHREAD_NUM_UPDATES && !TestDestroy(); i++)
- {
- // inform the GUI toolkit that we're going to use GUI functions
- // from a secondary thread:
- wxMutexGuiEnter();
- {
- wxCriticalSectionLocker lock(m_dlg->m_csBmp);
- // draw some more stuff on the bitmap
- wxMemoryDC dc(m_dlg->m_bmp);
- dc.SetBrush((i%2)==0 ? *wxBLUE_BRUSH : *wxGREEN_BRUSH);
- dc.DrawRectangle(rand()%GUITHREAD_BMP_SIZE, rand()%GUITHREAD_BMP_SIZE, 30, 30);
- // simulate long drawing time:
- wxMilliSleep(200);
- }
- // if we don't release the GUI mutex the MyImageDialog won't be able to refresh
- wxMutexGuiLeave();
- // notify the dialog that another piece of our masterpiece is complete:
- wxThreadEvent event( wxEVT_THREAD, GUITHREAD_EVENT );
- event.SetInt(i+1);
- wxQueueEvent( m_dlg, event.Clone() );
- if ( !((i + 1) % 10) )
- {
- // this message will go to the buffer
- wxLogMessage("Step #%d.", i + 1);
- }
- // give the main thread the time to refresh before we lock the GUI mutex again
- // FIXME: find a better way to do this!
- wxMilliSleep(100);
- }
- // now remove the thread-specific thread target
- wxLog::SetThreadActiveTarget(NULL);
- // so that this goes to the main window again
- wxLogMessage("GUI thread finished.");
- return (ExitCode)0;
- }
|