class MyThread;
WX_DEFINE_ARRAY_PTR(wxThread *, wxArrayThread);
-// Define a new application type
+// ----------------------------------------------------------------------------
+// the application object
+// ----------------------------------------------------------------------------
+
class MyApp : public wxApp
{
public:
bool m_shuttingDown;
};
-// Define a new frame type
+// ----------------------------------------------------------------------------
+// the main application frame
+// ----------------------------------------------------------------------------
+
class MyFrame: public wxFrame
{
public:
void OnResumeThread(wxCommandEvent& event);
void OnStartWorker(wxCommandEvent& event);
-
void OnExecMain(wxCommandEvent& event);
- void OnExecThread(wxCommandEvent& event);
+ void OnStartGUIThread(wxCommandEvent& event);
void OnShowCPUs(wxCommandEvent& event);
void OnAbout(wxCommandEvent& event);
// was the worker thread cancelled by user?
bool m_cancelled;
-
- // protects m_cancelled
- wxCriticalSection m_critsectWork;
+ wxCriticalSection m_csCancelled; // protects m_cancelled
DECLARE_EVENT_TABLE()
};
THREAD_STOP_THREAD,
THREAD_PAUSE_THREAD,
THREAD_RESUME_THREAD,
- THREAD_START_WORKER,
+ THREAD_START_WORKER,
THREAD_EXEC_MAIN,
- THREAD_EXEC_THREAD,
+ THREAD_START_GUI_THREAD,
THREAD_SHOWCPUS,
- WORKER_EVENT = wxID_HIGHEST+1 // this one gets sent from the worker thread
+ WORKER_EVENT = wxID_HIGHEST+1, // this one gets sent from MyWorkerThread
+ GUITHREAD_EVENT // this one gets sent from MyGUIThread
};
// ----------------------------------------------------------------------------
-// GUI thread
+// a simple thread
// ----------------------------------------------------------------------------
class MyThread : public wxThread
};
// ----------------------------------------------------------------------------
-// worker thread
+// a worker thread
// ----------------------------------------------------------------------------
class MyWorkerThread : public wxThread
};
// ----------------------------------------------------------------------------
-// a thread which simply calls wxExecute
+// a thread which executes GUI calls using wxMutexGuiEnter/Leave
// ----------------------------------------------------------------------------
-class MyExecThread : public wxThread
+#define GUITHREAD_BMP_SIZE 300
+#define GUITHREAD_NUM_UPDATES 50
+class MyImageDialog;
+
+class MyGUIThread : public wxThread
{
public:
- MyExecThread(const wxChar *command) : wxThread(wxTHREAD_JOINABLE),
- m_command(command)
+ MyGUIThread(MyImageDialog *dlg) : wxThread(wxTHREAD_JOINABLE)
{
- Create();
+ m_dlg = dlg;
}
- virtual ExitCode Entry()
- {
- return wxUIntToPtr(EXEC(m_command));
- }
+ 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:
- wxString m_command;
+ void OnGUIThreadEvent(wxThreadEvent& event);
+ void OnPaint(wxPaintEvent&);
+
+ MyGUIThread m_thread;
+ int m_nCurrentProgress;
+
+ DECLARE_EVENT_TABLE()
};
// ============================================================================
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_EXEC_THREAD, MyFrame::OnExecThread)
+ EVT_MENU(THREAD_START_GUI_THREAD, MyFrame::OnStartGUIThread)
EVT_MENU(THREAD_SHOWCPUS, MyFrame::OnShowCPUs)
EVT_MENU(THREAD_ABOUT, MyFrame::OnAbout)
- EVT_MENU(THREAD_START_WORKER, MyFrame::OnStartWorker)
-
EVT_UPDATE_UI(THREAD_START_WORKER, MyFrame::OnUpdateWorker)
EVT_THREAD(WORKER_EVENT, MyFrame::OnWorkerEvent)
EVT_IDLE(MyFrame::OnIdle)
menuThread->Append(THREAD_RESUME_THREAD, _T("&Resume the first suspended thread\tCtrl-R"));
menuThread->AppendSeparator();
menuThread->Append(THREAD_START_WORKER, _T("Start a &worker thread\tCtrl-W"));
+ menuThread->Append(THREAD_EXEC_MAIN, _T("&Launch a program from main thread\tF5"));
+ menuThread->Append(THREAD_START_GUI_THREAD, _T("Launch a &GUI thread\tF6"));
menuBar->Append(menuThread, _T("&Thread"));
- wxMenu *menuExec = new wxMenu;
- menuExec->Append(THREAD_EXEC_MAIN, _T("&Launch a program from main thread\tF5"));
- menuExec->Append(THREAD_EXEC_THREAD, _T("L&aunch a program from a thread\tCtrl-F5"));
- menuBar->Append(menuExec, _T("&Execute"));
-
wxMenu *menuHelp = new wxMenu;
menuHelp->Append(THREAD_SHOWCPUS, _T("&Show CPU count"));
menuHelp->AppendSeparator();
return thread;
}
+void MyFrame::DoLogThreadMessages()
+{
+ wxCriticalSectionLocker lock(m_csMessages);
+
+ const size_t count = m_messages.size();
+ for ( size_t n = 0; n < count; n++ )
+ {
+ m_txtctrl->AppendText(m_messages[n]);
+ }
+
+ m_messages.clear();
+}
+
+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;
event.Skip();
}
-void MyFrame::DoLogThreadMessages()
-{
- wxCriticalSectionLocker lock(m_csMessages);
-
- const size_t count = m_messages.size();
- for ( size_t n = 0; n < count; n++ )
- {
- m_txtctrl->AppendText(m_messages[n]);
- }
-
- m_messages.clear();
-}
-
-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
-}
-
void MyFrame::OnQuit(wxCommandEvent& WXUNUSED(event) )
{
Close(true);
void MyFrame::OnExecMain(wxCommandEvent& WXUNUSED(event))
{
- wxLogMessage(wxT("The exit code from the main program is %ld"),
- EXEC(_T("/bin/echo \"main program\"")));
-}
-
-void MyFrame::OnExecThread(wxCommandEvent& WXUNUSED(event))
-{
- MyExecThread thread(wxT("/bin/echo \"child thread\""));
- thread.Run();
+ 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 a child thread is %ld"),
- (long)wxPtrToUInt(thread.Wait()));
+ wxLogMessage(wxT("The exit code from the main program is %ld"),
+ EXEC(cmd));
}
void MyFrame::OnShowCPUs(wxCommandEvent& WXUNUSED(event))
{
if ( !m_dlgProgress->Update(n) )
{
- wxCriticalSectionLocker lock(m_critsectWork);
+ wxCriticalSectionLocker lock(m_csCancelled);
m_cancelled = true;
}
}
}
-bool MyFrame::Cancelled()
+void MyFrame::OnStartGUIThread(wxCommandEvent& WXUNUSED(event))
{
- wxCriticalSectionLocker lock(m_critsectWork);
+ MyImageDialog dlg(this);
- return m_cancelled;
+ dlg.ShowModal();
+}
+
+
+// ----------------------------------------------------------------------------
+// MyImageDialog
+// ----------------------------------------------------------------------------
+
+BEGIN_EVENT_TABLE(MyImageDialog, wxDialog)
+ EVT_THREAD(GUITHREAD_EVENT, MyImageDialog::OnGUIThreadEvent)
+ EVT_PAINT(MyImageDialog::OnPaint)
+END_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
}
}
-void *MyThread::Entry()
+wxThread::ExitCode MyThread::Entry()
{
wxString text;
// 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()
{
{
}
-// 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
-
-void *MyWorkerThread::Entry()
+wxThread::ExitCode MyWorkerThread::Entry()
{
#if TEST_YIELD_RACE_CONDITION
if ( TestDestroy() )
return NULL;
}
+
+// ----------------------------------------------------------------------------
+// MyGUIThread
+// ----------------------------------------------------------------------------
+
+wxThread::ExitCode MyGUIThread::Entry()
+{
+ 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_COMMAND_THREAD, GUITHREAD_EVENT );
+ event.SetInt(i+1);
+ wxQueueEvent( m_dlg, event.Clone() );
+
+ // 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);
+ }
+
+ return (ExitCode)0;
+}