X-Git-Url: https://git.saurik.com/wxWidgets.git/blobdiff_plain/c1b8f155dce7f553267997f4865b103cbd23c17a..f901941933f6bad712e855476b90c169315f9391:/samples/thread/thread.cpp diff --git a/samples/thread/thread.cpp b/samples/thread/thread.cpp index 261e81fc69..7948c35a47 100644 --- a/samples/thread/thread.cpp +++ b/samples/thread/thread.cpp @@ -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