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:
@@ -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;
|
||||
}
|
||||
|
Reference in New Issue
Block a user