]> git.saurik.com Git - wxWidgets.git/blobdiff - samples/thread/thread.cpp
Make TOOLKIT variable constant in MSW makefiles.
[wxWidgets.git] / samples / thread / thread.cpp
index 261e81fc69d6a532cea5269f49688e8e5dc3b6e8..7948c35a473f92c83e452af84ec19c45ec5152ee 100644 (file)
@@ -5,8 +5,8 @@
 // Modified by:
 // Created:     06/16/98
 // RCS-ID:      $Id$
-// Copyright:   (c) 1998-2002 wxWidgets team
-// Licence:     wxWindows license
+// Copyright:   (c) 1998-2009 wxWidgets team
+// Licence:     wxWindows licence
 /////////////////////////////////////////////////////////////////////////////
 
 // ============================================================================
@@ -89,28 +89,22 @@ public:
 // the main application frame
 // ----------------------------------------------------------------------------
 
-class MyFrame: public wxFrame
+class MyFrame : public wxFrame,
+                private wxLog
 {
 public:
     // ctor
-    MyFrame(wxFrame *frame, const wxString& title, int x, int y, int w, int h);
+    MyFrame(const wxString& title);
     virtual ~MyFrame();
 
-    // this function is MT-safe, i.e. it can be called from worker threads
-    // safely without any additional locking
-    void LogThreadMessage(const wxString& text)
-    {
-        wxCriticalSectionLocker lock(m_csMessages);
-        m_messages.push_back(text);
-
-        // as we effectively log the messages from the idle event handler,
-        // ensure it's going to be called now that we have some messages to log
-        wxWakeUpIdle();
-    }
-
     // accessors for MyWorkerThread (called in its context!)
     bool Cancelled();
 
+protected:
+    virtual void DoLogRecord(wxLogLevel level,
+                             const wxString& msg,
+                             const wxLogRecordInfo& info);
+
 private:
     // event handlers
     // --------------
@@ -136,6 +130,13 @@ private:
     void OnUpdateWorker(wxUpdateUIEvent& event);
 
 
+    // logging helper
+    void DoLogLine(wxTextCtrl *text,
+                   const wxString& timestr,
+                   const wxString& threadstr,
+                   const wxString& msg);
+
+
     // thread helper functions
     // -----------------------
 
@@ -145,9 +146,6 @@ private:
     // update display in our status bar: called during idle handling
     void UpdateThreadStatus();
 
-    // log the messages queued by LogThreadMessage()
-    void DoLogThreadMessages();
-
 
     // internal variables
     // ------------------
@@ -155,6 +153,10 @@ private:
     // 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;
@@ -208,21 +210,14 @@ enum
 class MyThread : public wxThread
 {
 public:
-    MyThread(MyFrame *frame);
+    MyThread();
     virtual ~MyThread();
 
     // thread execution starts here
     virtual void *Entry();
 
-    // write something to the text control in the main frame
-    void WriteText(const wxString& text)
-    {
-        m_frame->LogThreadMessage(text);
-    }
-
 public:
     unsigned m_count;
-    MyFrame *m_frame;
 };
 
 // ----------------------------------------------------------------------------
@@ -320,12 +315,7 @@ bool MyApp::OnInit()
     wxLog::AddTraceMask("thread");
 
     // Create the main frame window
-    MyFrame *frame = new MyFrame((wxFrame *)NULL, _T("wxWidgets threads sample"),
-                                 50, 50, 450, 340);
-    SetTopWindow(frame);
-
-    // Show the frame
-    frame->Show(true);
+    new MyFrame("wxWidgets threads sample");
 
     return true;
 }
@@ -356,56 +346,84 @@ BEGIN_EVENT_TABLE(MyFrame, wxFrame)
 END_EVENT_TABLE()
 
 // My frame constructor
-MyFrame::MyFrame(wxFrame *frame, const wxString& title,
-                 int x, int y, int w, int h)
-       : wxFrame(frame, wxID_ANY, title, wxPoint(x, y), wxSize(w, h))
+MyFrame::MyFrame(const wxString& title)
+       : wxFrame(NULL, wxID_ANY, title)
 {
-    SetIcon(wxIcon(sample_xpm));
+    m_oldLogger = wxLog::GetActiveTarget();
+
+    SetIcon(wxICON(sample));
 
     // Make a menubar
     wxMenuBar *menuBar = new wxMenuBar;
 
     wxMenu *menuFile = new wxMenu;
-    menuFile->Append(THREAD_CLEAR, _T("&Clear log\tCtrl-L"));
+    menuFile->Append(THREAD_CLEAR, wxT("&Clear log\tCtrl-L"));
     menuFile->AppendSeparator();
-    menuFile->Append(THREAD_QUIT, _T("E&xit\tAlt-X"));
-    menuBar->Append(menuFile, _T("&File"));
+    menuFile->Append(THREAD_QUIT, wxT("E&xit\tAlt-X"));
+    menuBar->Append(menuFile, wxT("&File"));
 
     wxMenu *menuThread = new wxMenu;
-    menuThread->Append(THREAD_START_THREAD, _T("&Start a new thread\tCtrl-N"));
-    menuThread->Append(THREAD_START_THREADS, _T("Start &many threads at once"));
-    menuThread->Append(THREAD_STOP_THREAD, _T("S&top the last spawned thread\tCtrl-S"));
+    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, _T("&Pause the last spawned running thread\tCtrl-P"));
-    menuThread->Append(THREAD_RESUME_THREAD, _T("&Resume the first suspended thread\tCtrl-R"));
+    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, _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"));
+    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, _T("&Show CPU count"));
+    menuHelp->Append(THREAD_SHOWCPUS, wxT("&Show CPU count"));
     menuHelp->AppendSeparator();
-    menuHelp->Append(THREAD_ABOUT, _T("&About..."));
-    menuBar->Append(menuHelp, _T("&Help"));
+    menuHelp->Append(THREAD_ABOUT, wxT("&About"));
+    menuBar->Append(menuHelp, wxT("&Help"));
 
     SetMenuBar(menuBar);
 
     m_nRunning = m_nCount = 0;
 
-    m_dlgProgress = (wxProgressDialog *)NULL;
+    m_dlgProgress = NULL;
 
 #if wxUSE_STATUSBAR
     CreateStatusBar(2);
 #endif // wxUSE_STATUSBAR
 
-    m_txtctrl = new wxTextCtrl(this, wxID_ANY, _T(""), wxPoint(0, 0), wxSize(0, 0),
+    // 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
@@ -432,32 +450,53 @@ MyFrame::~MyFrame()
     wxGetApp().m_semAllDone.Wait();
 }
 
-MyThread *MyFrame::CreateThread()
+void
+MyFrame::DoLogLine(wxTextCtrl *text,
+                   const wxString& timestr,
+                   const wxString& threadstr,
+                   const wxString& msg)
 {
-    MyThread *thread = new MyThread(this);
+    text->AppendText(wxString::Format("%9s %10s %s", timestr, threadstr, msg));
+}
 
-    if ( thread->Create() != wxTHREAD_NO_ERROR )
+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 )
     {
-        wxLogError(wxT("Can't create thread!"));
+        m_oldLogger->LogRecord(level, msg, info);
+        return;
     }
 
-    wxCriticalSectionLocker enter(wxGetApp().m_critsect);
-    wxGetApp().m_threads.Add(thread);
-
-    return thread;
+    DoLogLine
+    (
+        m_txtctrl,
+        wxDateTime(info.timestamp).FormatISOTime(),
+        info.threadId == wxThread::GetMainId()
+            ? wxString("main")
+            : wxString::Format("%lx", info.threadId),
+        msg + "\n"
+    );
 }
 
-void MyFrame::DoLogThreadMessages()
+MyThread *MyFrame::CreateThread()
 {
-    wxCriticalSectionLocker lock(m_csMessages);
+    MyThread *thread = new MyThread;
 
-    const size_t count = m_messages.size();
-    for ( size_t n = 0; n < count; n++ )
+    if ( thread->Create() != wxTHREAD_NO_ERROR )
     {
-        m_txtctrl->AppendText(m_messages[n]);
+        wxLogError(wxT("Can't create thread!"));
     }
 
-    m_messages.clear();
+    wxCriticalSectionLocker enter(wxGetApp().m_critsect);
+    wxGetApp().m_threads.Add(thread);
+
+    return thread;
 }
 
 void MyFrame::UpdateThreadStatus()
@@ -498,8 +537,8 @@ void MyFrame::OnStartThreads(wxCommandEvent& WXUNUSED(event) )
 {
     static long s_num;
 
-    s_num = wxGetNumberFromUser(_T("How many threads to start: "), _T(""),
-                                _T("wxThread sample"), s_num, 1, 10000, this);
+    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;
@@ -520,11 +559,11 @@ void MyFrame::OnStartThreads(wxCommandEvent& WXUNUSED(event) )
         // have the lowest priority, the second - the highest, all the rest
         // the normal one
         if ( n == 0 )
-            thr->SetPriority(WXTHREAD_MIN_PRIORITY);
+            thr->SetPriority(wxPRIORITY_MIN);
         else if ( n == 1 )
-            thr->SetPriority(WXTHREAD_MAX_PRIORITY);
+            thr->SetPriority(wxPRIORITY_MAX);
         else
-            thr->SetPriority(WXTHREAD_DEFAULT_PRIORITY);
+            thr->SetPriority(wxPRIORITY_DEFAULT);
 
         threads.Add(thr);
     }
@@ -552,12 +591,14 @@ void MyFrame::OnStartThread(wxCommandEvent& WXUNUSED(event) )
     }
 
 #if wxUSE_STATUSBAR
-    SetStatusText(_T("New thread started."), 1);
+    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
@@ -567,10 +608,18 @@ void MyFrame::OnStopThread(wxCommandEvent& WXUNUSED(event) )
     }
     else
     {
-        wxGetApp().m_threads.Last()->Delete();
+        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(_T("Last thread stopped."), 1);
+        SetStatusText(wxT("Last thread stopped."), 1);
 #endif // wxUSE_STATUSBAR
     }
 }
@@ -593,7 +642,7 @@ void MyFrame::OnResumeThread(wxCommandEvent& WXUNUSED(event) )
         wxGetApp().m_threads[n]->Resume();
 
 #if wxUSE_STATUSBAR
-        SetStatusText(_T("Thread resumed."), 1);
+        SetStatusText(wxT("Thread resumed."), 1);
 #endif // wxUSE_STATUSBAR
     }
 }
@@ -616,15 +665,13 @@ void MyFrame::OnPauseThread(wxCommandEvent& WXUNUSED(event) )
         wxGetApp().m_threads[n]->Pause();
 
 #if wxUSE_STATUSBAR
-        SetStatusText(_T("Thread paused."), 1);
+        SetStatusText(wxT("Thread paused."), 1);
 #endif // wxUSE_STATUSBAR
     }
 }
 
 void MyFrame::OnIdle(wxIdleEvent& event)
 {
-    DoLogThreadMessages();
-
     UpdateThreadStatus();
 
     event.Skip();
@@ -660,15 +707,15 @@ void MyFrame::OnShowCPUs(wxCommandEvent& WXUNUSED(event))
     switch ( nCPUs )
     {
         case -1:
-            msg = _T("Unknown number of CPUs");
+            msg = wxT("Unknown number of CPUs");
             break;
 
         case 0:
-            msg = _T("WARNING: you're running without any CPUs!");
+            msg = wxT("WARNING: you're running without any CPUs!");
             break;
 
         case 1:
-            msg = _T("This system only has one CPU.");
+            msg = wxT("This system only has one CPU.");
             break;
 
         default:
@@ -681,11 +728,11 @@ void MyFrame::OnShowCPUs(wxCommandEvent& WXUNUSED(event))
 void MyFrame::OnAbout(wxCommandEvent& WXUNUSED(event) )
 {
     wxMessageDialog dialog(this,
-                           _T("wxWidgets multithreaded application sample\n")
-                           _T("(c) 1998 Julian Smart, Guilhem Lavaux\n")
-                           _T("(c) 1999 Vadim Zeitlin\n")
-                           _T("(c) 2000 Robert Roebling"),
-                           _T("About wxThread sample"),
+                           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();
@@ -713,8 +760,8 @@ void MyFrame::OnStartWorker(wxCommandEvent& WXUNUSED(event))
 
     m_dlgProgress = new wxProgressDialog
                         (
-                         _T("Progress dialog"),
-                         _T("Wait until the thread terminates or press [Cancel]"),
+                         wxT("Progress dialog"),
+                         wxT("Wait until the thread terminates or press [Cancel]"),
                          100,
                          this,
                          wxPD_CAN_ABORT |
@@ -756,6 +803,11 @@ void MyFrame::OnWorkerEvent(wxThreadEvent& event)
 
 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();
@@ -833,7 +885,7 @@ void MyImageDialog::OnPaint(wxPaintEvent& WXUNUSED(evt))
 
     // paint a sort of progress bar with a 10px border:
     dc.SetBrush(*wxRED_BRUSH);
-    dc.DrawRectangle(10,10, 10+m_nCurrentProgress*(GUITHREAD_BMP_SIZE-20)/100,30);
+    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,
@@ -844,11 +896,10 @@ void MyImageDialog::OnPaint(wxPaintEvent& WXUNUSED(evt))
 // MyThread
 // ----------------------------------------------------------------------------
 
-MyThread::MyThread(MyFrame *frame)
+MyThread::MyThread()
         : wxThread()
 {
     m_count = 0;
-    m_frame = frame;
 }
 
 MyThread::~MyThread()
@@ -873,12 +924,7 @@ MyThread::~MyThread()
 
 wxThread::ExitCode MyThread::Entry()
 {
-    wxString text;
-
-    text.Printf(wxT("Thread %p started (priority = %u).\n"),
-                GetId(), GetPriority());
-    WriteText(text);
-    // wxLogMessage(text); -- test wxLog thread safeness
+    wxLogMessage("Thread started (priority = %u).", GetPriority());
 
     for ( m_count = 0; m_count < 10; m_count++ )
     {
@@ -894,16 +940,13 @@ wxThread::ExitCode MyThread::Entry()
         if ( TestDestroy() )
             break;
 
-        text.Printf(wxT("[%u] Thread %p here.\n"), m_count, GetId());
-        WriteText(text);
+        wxLogMessage("Thread progress: %u", m_count);
 
         // wxSleep() can't be called from non-GUI thread!
         wxThread::Sleep(1000);
     }
 
-    text.Printf(wxT("Thread %p finished.\n"), GetId());
-    WriteText(text);
-    // wxLogMessage(text); -- test wxLog thread safeness
+    wxLogMessage("Thread finished.");
 
     return NULL;
 }
@@ -936,7 +979,7 @@ wxThread::ExitCode MyWorkerThread::Entry()
     if ( TestDestroy() )
         return NULL;
 
-    wxThreadEvent event( wxEVT_COMMAND_THREAD, WORKER_EVENT );
+    wxThreadEvent event( wxEVT_THREAD, WORKER_EVENT );
 
     event.SetInt( 50 );
     wxQueueEvent( m_frame, event.Clone() );
@@ -951,7 +994,7 @@ wxThread::ExitCode MyWorkerThread::Entry()
             break;
 
         // create any type of command event here
-        wxThreadEvent event( wxEVT_COMMAND_THREAD, WORKER_EVENT );
+        wxThreadEvent event( wxEVT_THREAD, WORKER_EVENT );
         event.SetInt( m_count );
 
         // send in a thread-safe way
@@ -960,7 +1003,7 @@ wxThread::ExitCode MyWorkerThread::Entry()
         wxMilliSleep(200);
     }
 
-    wxThreadEvent event( wxEVT_COMMAND_THREAD, WORKER_EVENT );
+    wxThreadEvent event( wxEVT_THREAD, WORKER_EVENT );
     event.SetInt(-1); // that's all
     wxQueueEvent( m_frame, event.Clone() );
 #endif
@@ -975,6 +1018,19 @@ wxThread::ExitCode MyWorkerThread::Entry()
 
 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
@@ -997,14 +1053,26 @@ wxThread::ExitCode MyGUIThread::Entry()
         wxMutexGuiLeave();
 
         // notify the dialog that another piece of our masterpiece is complete:
-        wxThreadEvent event( wxEVT_COMMAND_THREAD, GUITHREAD_EVENT );
-        event.SetInt(i);
+        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;
 }