Made wxLogXXX() functions thread-safe.

They can now be called from any thread and will buffer the messages until the
current log target is flushed from the main thread. This makes earlier code to
do the same thing specifically for wxLogWindow unnecessary and also allows to
use wxLogMessage() in the thread sample instead of using manual logging there.


git-svn-id: https://svn.wxwidgets.org/svn/wx/wxWidgets/trunk@61416 c3d73ce0-8a6f-49c7-b76d-6d57e0e08775
This commit is contained in:
Vadim Zeitlin
2009-07-12 17:02:30 +00:00
parent dbe0872fc8
commit 232addd1cd
6 changed files with 228 additions and 136 deletions

View File

@@ -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,10 +346,11 @@ 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)
{
m_oldLogger = wxLog::GetActiveTarget();
SetIcon(wxIcon(sample_xpm));
// Make a menubar
@@ -394,18 +385,45 @@ MyFrame::MyFrame(wxFrame *frame, const wxString& title,
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,9 +450,43 @@ MyFrame::~MyFrame()
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("%x", info.threadId),
msg + "\n"
);
}
MyThread *MyFrame::CreateThread()
{
MyThread *thread = new MyThread(this);
MyThread *thread = new MyThread;
if ( thread->Create() != wxTHREAD_NO_ERROR )
{
@@ -447,19 +499,6 @@ 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);
@@ -623,8 +662,6 @@ void MyFrame::OnPauseThread(wxCommandEvent& WXUNUSED(event) )
void MyFrame::OnIdle(wxIdleEvent& event)
{
DoLogThreadMessages();
UpdateThreadStatus();
event.Skip();
@@ -844,11 +881,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 +909,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 +925,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;
}