// 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
/////////////////////////////////////////////////////////////////////////////
// ============================================================================
// 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
// --------------
void OnUpdateWorker(wxUpdateUIEvent& event);
+ // logging helper
+ void DoLogLine(wxTextCtrl *text,
+ const wxString& timestr,
+ const wxString& threadstr,
+ const wxString& msg);
+
+
// thread helper functions
// -----------------------
// update display in our status bar: called during idle handling
void UpdateThreadStatus();
- // log the messages queued by LogThreadMessage()
- void DoLogThreadMessages();
-
// 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;
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;
};
// ----------------------------------------------------------------------------
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;
}
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
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()
{
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;
// 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);
}
}
#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
}
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
}
}
wxGetApp().m_threads[n]->Resume();
#if wxUSE_STATUSBAR
- SetStatusText(_T("Thread resumed."), 1);
+ SetStatusText(wxT("Thread resumed."), 1);
#endif // wxUSE_STATUSBAR
}
}
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();
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:
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();
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 |
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();
// MyThread
// ----------------------------------------------------------------------------
-MyThread::MyThread(MyFrame *frame)
+MyThread::MyThread()
: wxThread()
{
m_count = 0;
- m_frame = frame;
}
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++ )
{
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;
}
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() );
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
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
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
wxMutexGuiLeave();
// notify the dialog that another piece of our masterpiece is complete:
- wxThreadEvent event( wxEVT_COMMAND_THREAD, GUITHREAD_EVENT );
+ 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;
}