X-Git-Url: https://git.saurik.com/wxWidgets.git/blobdiff_plain/f3855ef064498aadea229d8202eedd78aed3f8e1..519cb848a8f4c91c73421bb75314754284e593a4:/samples/thread/test.cpp?ds=sidebyside diff --git a/samples/thread/test.cpp b/samples/thread/test.cpp index 1455acf3c5..08aa48a00a 100644 --- a/samples/thread/test.cpp +++ b/samples/thread/test.cpp @@ -6,203 +6,432 @@ // Created: 06/16/98 // RCS-ID: $Id$ // Copyright: (c) Julian Smart, Markus Holzem, Guilhem Lavaux -// Licence: wxWindows license +// Licence: wxWindows license ///////////////////////////////////////////////////////////////////////////// +/* + TODO: use worker threads to update progress controls instead of writing + messages - it will be more visual + */ + #ifdef __GNUG__ -#pragma implementation "test.cpp" -#pragma interface "test.cpp" + #pragma implementation "test.cpp" + #pragma interface "test.cpp" #endif // For compilers that support precompilation, includes "wx/wx.h". #include "wx/wxprec.h" #ifdef __BORLANDC__ -#pragma hdrstop + #pragma hdrstop #endif #ifndef WX_PRECOMP -#include "wx/wx.h" + #include "wx/wx.h" #endif +#if !wxUSE_THREADS + #error "This sample requires thread support!" +#endif // wxUSE_THREADS + #include "wx/thread.h" #include "wx/dynarray.h" +#include "wx/time.h" + +class MyThread; +WX_DEFINE_ARRAY(wxThread *, wxArrayThread); // Define a new application type -class MyApp: public wxApp +class MyApp : public wxApp { - public: - bool OnInit(void); +public: + virtual bool OnInit(); + +public: + // all the threads currently alive - as soon as the thread terminates, it's + // removed from the array + wxArrayThread m_threads; + + // crit section protects access to all of the arrays below + wxCriticalSection m_critsect; }; -WX_DEFINE_ARRAY(wxThread *,wxArrayThread); +// Create a new application object +IMPLEMENT_APP(MyApp) // Define a new frame type class MyFrame: public wxFrame { - public: - MyFrame(wxFrame *frame, char *title, int x, int y, int w, int h); - - public: +public: + // ctor + MyFrame(wxFrame *frame, const wxString& title, int x, int y, int w, int h); + + // operations + void WriteText(const wxString& text) { m_txtctrl->WriteText(text); } + + // callbacks void OnQuit(wxCommandEvent& event); void OnAbout(wxCommandEvent& event); + void OnClear(wxCommandEvent& event); + void OnStartThread(wxCommandEvent& event); + void OnStartThreads(wxCommandEvent& event); void OnStopThread(wxCommandEvent& event); void OnPauseThread(wxCommandEvent& event); - void OnSize(wxSizeEvent &event); - bool OnClose(void) { return TRUE; } + void OnResumeThread(wxCommandEvent& event); + + void OnIdle(wxIdleEvent &event); + +private: + // helper function - creates a new thread (but doesn't run it) + MyThread *CreateThread(); - public: - wxArrayThread m_threads; - wxTextCtrl *m_txtctrl; + // just some place to put our messages in + wxTextCtrl *m_txtctrl; - DECLARE_EVENT_TABLE() + // remember the number of running threads and total number of threads + size_t m_nRunning, m_nCount; + + DECLARE_EVENT_TABLE() }; -class MyThread: public wxThread +class MyThread : public wxThread { - public: +public: MyThread(MyFrame *frame); - void *Entry(); - public: + // thread execution starts here + virtual void *Entry(); + + // called when the thread exits - whether it terminates normally or is + // stopped with Delete() (but not when it is Kill()ed!) + virtual void OnExit(); + + // write something to the text control + void WriteText(const wxString& text); + +public: + size_t m_count; MyFrame *m_frame; }; MyThread::MyThread(MyFrame *frame) - : wxThread() + : wxThread() { - m_frame = frame; + m_count = 0; + m_frame = frame; } -void *MyThread::Entry() +void MyThread::WriteText(const wxString& text) { - wxString text; + wxString msg; - text.Printf("Thread 0x%x: Hello !\n", GetID()); - DeferDestroy(TRUE); + // before doing any GUI calls we must ensure that this thread is the only + // one doing it! - while (1) { - TestDestroy(); wxMutexGuiEnter(); - m_frame->m_txtctrl->WriteText(text); + + msg << wxTime().FormatTime() << ": " << text; + + m_frame->WriteText(msg); + wxMutexGuiLeave(); - wxSleep(1); - } - - return NULL; +} + +void MyThread::OnExit() +{ + wxCriticalSectionLocker locker(wxGetApp().m_critsect); + + wxGetApp().m_threads.Remove(this); +} + +void *MyThread::Entry() +{ + wxString text; + + text.Printf("Thread 0x%x started (priority = %d).\n", + GetID(), GetPriority()); + WriteText(text); + + for ( m_count = 0; m_count < 10; m_count++ ) + { + // check if we were asked to exit + if ( TestDestroy() ) + break; + + text.Printf("[%u] Thread 0x%x here.\n", m_count, GetID()); + WriteText(text); + + // wxSleep() can't be called from non-GUI thread! + wxThread::Sleep(1000); + } + + text.Printf("Thread 0x%x finished.\n", GetID()); + WriteText(text); + + return NULL; } // ID for the menu commands -#define TEST_QUIT 1 -#define TEST_TEXT 101 -#define TEST_ABOUT 102 -#define TEST_START_THREAD 103 -#define TEST_STOP_THREAD 104 -#define TEST_PAUSE_THREAD 105 +enum +{ + TEST_QUIT = 1, + TEST_TEXT = 101, + TEST_ABOUT, + TEST_CLEAR, + TEST_START_THREAD = 201, + TEST_START_THREADS, + TEST_STOP_THREAD, + TEST_PAUSE_THREAD, + TEST_RESUME_THREAD +}; BEGIN_EVENT_TABLE(MyFrame, wxFrame) - EVT_MENU(TEST_QUIT, MyFrame::OnQuit) - EVT_MENU(TEST_ABOUT, MyFrame::OnAbout) - EVT_MENU(TEST_START_THREAD, MyFrame::OnStartThread) - EVT_MENU(TEST_STOP_THREAD, MyFrame::OnStopThread) - EVT_MENU(TEST_PAUSE_THREAD, MyFrame::OnPauseThread) - EVT_SIZE(MyFrame::OnSize) + EVT_MENU(TEST_QUIT, MyFrame::OnQuit) + EVT_MENU(TEST_ABOUT, MyFrame::OnAbout) + EVT_MENU(TEST_CLEAR, MyFrame::OnClear) + EVT_MENU(TEST_START_THREAD, MyFrame::OnStartThread) + EVT_MENU(TEST_START_THREADS, MyFrame::OnStartThreads) + EVT_MENU(TEST_STOP_THREAD, MyFrame::OnStopThread) + EVT_MENU(TEST_PAUSE_THREAD, MyFrame::OnPauseThread) + EVT_MENU(TEST_RESUME_THREAD, MyFrame::OnResumeThread) + + EVT_IDLE(MyFrame::OnIdle) END_EVENT_TABLE() -// Create a new application object -IMPLEMENT_APP (MyApp) - // `Main program' equivalent, creating windows and returning main app frame -bool MyApp::OnInit(void) +bool MyApp::OnInit() { - // Create the main frame window - MyFrame *frame = new MyFrame((wxFrame *) NULL, (char *) "Minimal wxWindows App", 50, 50, 450, 340); + // Create the main frame window + MyFrame *frame = new MyFrame((wxFrame *)NULL, "wxWindows threads sample", + 50, 50, 450, 340); + + // Make a menubar + wxMenu *file_menu = new wxMenu; + + file_menu->Append(TEST_CLEAR, "&Clear log\tCtrl-L"); + file_menu->AppendSeparator(); + file_menu->Append(TEST_ABOUT, "&About"); + file_menu->AppendSeparator(); + file_menu->Append(TEST_QUIT, "E&xit\tAlt-X"); + wxMenuBar *menu_bar = new wxMenuBar; + menu_bar->Append(file_menu, "&File"); + + wxMenu *thread_menu = new wxMenu; + thread_menu->Append(TEST_START_THREAD, "&Start a new thread\tCtrl-N"); + thread_menu->Append(TEST_START_THREADS, "Start &many threads at once"); + thread_menu->Append(TEST_STOP_THREAD, "S&top a running thread\tCtrl-S"); + thread_menu->AppendSeparator(); + thread_menu->Append(TEST_PAUSE_THREAD, "&Pause a running thread\tCtrl-P"); + thread_menu->Append(TEST_RESUME_THREAD, "&Resume suspended thread\tCtrl-R"); + menu_bar->Append(thread_menu, "&Thread"); + frame->SetMenuBar(menu_bar); + + // Show the frame + frame->Show(TRUE); + + SetTopWindow(frame); + + return TRUE; +} - // Make a menubar - wxMenu *file_menu = new wxMenu; +// My frame constructor +MyFrame::MyFrame(wxFrame *frame, const wxString& title, + int x, int y, int w, int h) + : wxFrame(frame, -1, title, wxPoint(x, y), wxSize(w, h)) +{ + m_nRunning = m_nCount = 0; - file_menu->Append(TEST_ABOUT, "&About"); - file_menu->Append(TEST_QUIT, "E&xit"); - wxMenuBar *menu_bar = new wxMenuBar; - menu_bar->Append(file_menu, "&File"); + CreateStatusBar(2); - wxMenu *thread_menu = new wxMenu; - thread_menu->Append(TEST_START_THREAD, "Start a new thread"); - thread_menu->Append(TEST_STOP_THREAD, "Stop a running thread"); - thread_menu->Append(TEST_PAUSE_THREAD, "Pause a running thread"); - menu_bar->Append(thread_menu, "Thread"); - frame->SetMenuBar(menu_bar); + m_txtctrl = new wxTextCtrl(this, -1, "", wxPoint(0, 0), wxSize(0, 0), + wxTE_MULTILINE | wxTE_READONLY); - // Make a panel with a message - wxPanel *panel = new wxPanel( frame, -1, wxPoint(0, 0), wxSize(400, 200), wxTAB_TRAVERSAL ); - - (void)new wxStaticText( panel, -1, "Log window", wxPoint(10,10) ); - +} - frame->m_txtctrl = new wxTextCtrl(panel, -1, "", wxPoint(10,30), wxSize(390, 190), - wxTE_MULTILINE); +MyThread *MyFrame::CreateThread() +{ + MyThread *thread = new MyThread(this); + + if ( thread->Create() != wxTHREAD_NO_ERROR ) + { + wxLogError("Can't create thread!"); + } - // Show the frame - frame->Show(TRUE); - - SetTopWindow(frame); + wxCriticalSectionLocker enter(wxGetApp().m_critsect); + wxGetApp().m_threads.Add(thread); - return TRUE; + return thread; } -// My frame constructor -MyFrame::MyFrame(wxFrame *frame, char *title, int x, int y, int w, int h): - wxFrame(frame, -1, title, wxPoint(x, y), wxSize(w, h)) -{} +void MyFrame::OnStartThreads(wxCommandEvent& WXUNUSED(event) ) +{ + static wxString s_str; + s_str = wxGetTextFromUser("How many threads to start: ", + "wxThread sample", + s_str, this); + if ( s_str.IsEmpty() ) + return; + + size_t count, n; + sscanf(s_str, "%u", &count); + if ( count == 0 ) + return; + + wxArrayThread threads; + + // first create them all... + for ( n = 0; n < count; n++ ) + { + wxThread *thr = CreateThread(); + + // we want to show the effect of SetPriority(): the first thread will + // have the lowest priority, the second - the highest, all the rest + // the normal one + if ( n == 0 ) + thr->SetPriority(WXTHREAD_MIN_PRIORITY); + else if ( n == 1 ) + thr->SetPriority(WXTHREAD_MAX_PRIORITY); + else + thr->SetPriority(WXTHREAD_DEFAULT_PRIORITY); + + threads.Add(thr); + } + + wxString msg; + msg.Printf("%d new threads created.", count); + SetStatusText(msg, 1); + + // ...and then start them + for ( n = 0; n < count; n++ ) + { + threads[n]->Run(); + } +} void MyFrame::OnStartThread(wxCommandEvent& WXUNUSED(event) ) { - MyThread *thread = new MyThread(this); + MyThread *thread = CreateThread(); - thread->Create(); + if ( thread->Run() != wxTHREAD_NO_ERROR ) + { + wxLogError("Can't start thread!"); + } - m_threads.Add(thread); + SetStatusText("New thread started.", 1); } void MyFrame::OnStopThread(wxCommandEvent& WXUNUSED(event) ) { - int no_thrd = m_threads.Count()-1; - - if (no_thrd < 0) - return; + // stop the last thread + if ( wxGetApp().m_threads.IsEmpty() ) + { + wxLogError("No thread to stop!"); + } + else + { + wxGetApp().m_critsect.Enter(); + + wxThread *thread = wxGetApp().m_threads.Last(); + + // it's important to leave critical section before calling Delete() + // because delete will (implicitly) call OnExit() which also tries + // to enter the same crit section - would dead lock. + wxGetApp().m_critsect.Leave(); + + thread->Delete(); + + SetStatusText("Thread stopped.", 1); + } +} - delete (m_threads[no_thrd]); - m_threads.Remove(no_thrd); +void MyFrame::OnResumeThread(wxCommandEvent& WXUNUSED(event) ) +{ + wxCriticalSectionLocker enter(wxGetApp().m_critsect); + + // resume first suspended thread + size_t n = 0, count = wxGetApp().m_threads.Count(); + while ( n < count && !wxGetApp().m_threads[n]->IsPaused() ) + n++; + + if ( n == count ) + { + wxLogError("No thread to resume!"); + } + else + { + wxGetApp().m_threads[n]->Resume(); + + SetStatusText("Thread resumed.", 1); + } } void MyFrame::OnPauseThread(wxCommandEvent& WXUNUSED(event) ) { + wxCriticalSectionLocker enter(wxGetApp().m_critsect); + + // pause last running thread + int n = wxGetApp().m_threads.Count() - 1; + while ( n >= 0 && !wxGetApp().m_threads[n]->IsRunning() ) + n--; + + if ( n < 0 ) + { + wxLogError("No thread to pause!"); + } + else + { + wxGetApp().m_threads[n]->Pause(); + + SetStatusText("Thread paused.", 1); + } } -void MyFrame::OnSize(wxSizeEvent& event ) +// set the frame title indicating the current number of threads +void MyFrame::OnIdle(wxIdleEvent &event) { - wxFrame::OnSize(event); - - wxSize size( GetClientSize() ); - - m_txtctrl->SetSize( 10, 30, size.x-20, size.y-40 ); + // update the counts of running/total threads + size_t nRunning = 0, + nCount = wxGetApp().m_threads.Count(); + for ( size_t n = 0; n < nCount; n++ ) + { + if ( wxGetApp().m_threads[n]->IsRunning() ) + nRunning++; + } + + if ( nCount != m_nCount || nRunning != m_nRunning ) + { + m_nRunning = nRunning; + m_nCount = nCount; + + wxLogStatus(this, "%u threads total, %u running.", nCount, nRunning); + } + //else: avoid flicker - don't print anything } void MyFrame::OnQuit(wxCommandEvent& WXUNUSED(event) ) { - unsigned int i; - for (i=0;iDelete(); + } + + Close(TRUE); } void MyFrame::OnAbout(wxCommandEvent& WXUNUSED(event) ) { - wxMessageDialog dialog(this, "wxThread sample (based on minimal)\nJulian Smart and Guilhem Lavaux", - "About wxThread sample", wxYES_NO|wxCANCEL); + wxMessageDialog dialog(this, "wxWindows multithreaded application sample\n" + "(c) 1998 Julian Smart, Guilhem Lavaux\n" + "(c) 1999 Vadim Zeitlin", + "About wxThread sample", + wxOK | wxICON_INFORMATION); - dialog.ShowModal(); + dialog.ShowModal(); } - +void MyFrame::OnClear(wxCommandEvent& WXUNUSED(event)) +{ + m_txtctrl->Clear(); +}