]> git.saurik.com Git - wxWidgets.git/blobdiff - samples/thread/thread.cpp
Use wxRefCounter instead of wxObjecrDataRef
[wxWidgets.git] / samples / thread / thread.cpp
index 8b43fe016528cd4c2342c168971a3e1f56927126..c6e7d0d2de98e621284389d69d6334fdb1cfc053 100644 (file)
 class MyThread;
 WX_DEFINE_ARRAY_PTR(wxThread *, wxArrayThread);
 
-// Define a new application type
+// ----------------------------------------------------------------------------
+// the application object
+// ----------------------------------------------------------------------------
+
 class MyApp : public wxApp
 {
 public:
@@ -82,7 +85,10 @@ public:
     bool m_shuttingDown;
 };
 
-// Define a new frame type
+// ----------------------------------------------------------------------------
+// the main application frame
+// ----------------------------------------------------------------------------
+
 class MyFrame: public wxFrame
 {
 public:
@@ -119,9 +125,8 @@ private:
     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);
@@ -164,9 +169,7 @@ private:
 
     // 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()
 };
@@ -187,18 +190,19 @@ enum
     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
@@ -222,7 +226,7 @@ public:
 };
 
 // ----------------------------------------------------------------------------
-// worker thread
+// worker thread
 // ----------------------------------------------------------------------------
 
 class MyWorkerThread : public wxThread
@@ -243,25 +247,50 @@ public:
 };
 
 // ----------------------------------------------------------------------------
-// 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()
 };
 
 // ============================================================================
@@ -314,14 +343,13 @@ BEGIN_EVENT_TABLE(MyFrame, wxFrame)
     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)
@@ -352,13 +380,10 @@ MyFrame::MyFrame(wxFrame *frame, const wxString& title,
     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();
@@ -422,6 +447,53 @@ MyThread *MyFrame::CreateThread()
     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;
@@ -558,42 +630,6 @@ void MyFrame::OnIdle(wxIdleEvent& event)
     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);
@@ -601,17 +637,19 @@ void MyFrame::OnQuit(wxCommandEvent& WXUNUSED(event) )
 
 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))
@@ -709,20 +747,98 @@ void MyFrame::OnWorkerEvent(wxThreadEvent& 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
@@ -755,7 +871,7 @@ MyThread::~MyThread()
     }
 }
 
-void *MyThread::Entry()
+wxThread::ExitCode MyThread::Entry()
 {
     wxString text;
 
@@ -797,6 +913,12 @@ void *MyThread::Entry()
 // 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()
 {
@@ -808,13 +930,7 @@ void MyWorkerThread::OnExit()
 {
 }
 
-// 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() )
@@ -852,3 +968,43 @@ void *MyWorkerThread::Entry()
     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;
+}