X-Git-Url: https://git.saurik.com/wxWidgets.git/blobdiff_plain/bee503b05f2e67035e1f45bd33e94451daeceb31..aeafc354935512ddba84502bc60ca3b6246c14ce:/samples/thread/test.cpp diff --git a/samples/thread/test.cpp b/samples/thread/test.cpp index cc19d4d063..dce914e8e6 100644 --- a/samples/thread/test.cpp +++ b/samples/thread/test.cpp @@ -42,12 +42,13 @@ #include "wx/time.h" // Define a new application type -class MyApp: public wxApp +class MyApp : public wxApp { public: - bool OnInit(void); + bool OnInit(); }; +class MyThread; WX_DEFINE_ARRAY(wxThread *, wxArrayThread); // Define a new frame type @@ -55,7 +56,7 @@ class MyFrame: public wxFrame { public: // ctor - MyFrame(wxFrame *frame, char *title, int x, int y, int w, int h); + MyFrame(wxFrame *frame, const wxString& title, int x, int y, int w, int h); // operations void WriteText(const wxString& text) { m_txtctrl->WriteText(text); } @@ -66,6 +67,7 @@ public: void OnClear(wxCommandEvent& event); void OnStartThread(wxCommandEvent& event); + void OnStartThreads(wxCommandEvent& event); void OnStopThread(wxCommandEvent& event); void OnPauseThread(wxCommandEvent& event); void OnResumeThread(wxCommandEvent& event); @@ -73,20 +75,37 @@ public: void OnIdle(wxIdleEvent &event); bool OnClose() { return TRUE; } - // called by dying thread + // called by dying thread _in_that_thread_context_ void OnThreadExit(wxThread *thread); -public: +private: + // helper function - creates a new thread (but doesn't run it) + MyThread *CreateThread(); + + // crit section protects access to all of the arrays below + wxCriticalSection m_critsect; + + // all the threads currently alive - as soon as the thread terminates, it's + // removed from the array wxArrayThread m_threads; -private: - void DeleteThread(size_t index); + // both of these arrays are only valid between 2 iterations of OnIdle(), + // they're cleared each time it is excuted. - // crit section protects access to the array below - wxCriticalSection m_critsect; - wxArrayInt m_aToDelete; + // the array of threads which finished (either because they did their work + // or because they were explicitly stopped) + wxArrayThread m_terminated; + + // the array of threads which were stopped by the user and not terminated + // by themselves - these threads shouldn't be Delete()d second time from + // OnIdle() + wxArrayThread m_stopped; + + // just some place to put our messages in + wxTextCtrl *m_txtctrl; - wxTextCtrl *m_txtctrl; + // remember the number of running threads and total number of threads + size_t m_nRunning, m_nCount; DECLARE_EVENT_TABLE() }; @@ -99,7 +118,8 @@ public: // thread execution starts here virtual void *Entry(); - // called when the thread exits - whether + // 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 @@ -138,8 +158,6 @@ void *MyThread::Entry() { wxString text; - DeferDestroy(TRUE); - text.Printf("Thread 0x%x started.\n", GetID()); WriteText(text); @@ -152,11 +170,8 @@ void *MyThread::Entry() text.Printf("[%u] Thread 0x%x here.\n", m_count, GetID()); WriteText(text); -#ifdef __WXMSW__ - ::Sleep(1000); -#else - wxSleep(1); -#endif + // wxSleep() can't be called from non-GUI thread! + wxThread::Sleep(1000); } text.Printf("Thread 0x%x finished.\n", GetID()); @@ -170,12 +185,13 @@ enum { TEST_QUIT = 1, TEST_TEXT = 101, - TEST_ABOUT = 102, - TEST_CLEAR = 103, - TEST_START_THREAD = 203, - TEST_STOP_THREAD = 204, - TEST_PAUSE_THREAD = 205, - TEST_RESUME_THREAD = 206 + 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) @@ -183,6 +199,7 @@ BEGIN_EVENT_TABLE(MyFrame, wxFrame) 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) @@ -213,6 +230,7 @@ bool MyApp::OnInit() wxMenu *thread_menu = new wxMenu; thread_menu->Append(TEST_START_THREAD, "&Start a new thread"); + thread_menu->Append(TEST_START_THREADS, "Start &many threads at once"); thread_menu->Append(TEST_STOP_THREAD, "S&top a running thread"); thread_menu->AppendSeparator(); thread_menu->Append(TEST_PAUSE_THREAD, "&Pause a running thread"); @@ -229,38 +247,102 @@ bool MyApp::OnInit() } // My frame constructor -MyFrame::MyFrame(wxFrame *frame, char *title, int x, int y, int w, int h) +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)) { - CreateStatusBar(); + m_nRunning = m_nCount = 0; + + CreateStatusBar(2); m_txtctrl = new wxTextCtrl(this, -1, "", wxPoint(0, 0), wxSize(0, 0), wxTE_MULTILINE | wxTE_READONLY); } -void MyFrame::OnStartThread(wxCommandEvent& WXUNUSED(event) ) +MyThread *MyFrame::CreateThread() { MyThread *thread = new MyThread(this); - thread->Create(); + if ( thread->Create() != wxTHREAD_NO_ERROR ) + { + wxLogError("Can't create thread!"); + } wxCriticalSectionLocker enter(m_critsect); m_threads.Add(thread); + + return thread; } -void MyFrame::OnStopThread(wxCommandEvent& WXUNUSED(event) ) +void MyFrame::OnStartThreads(wxCommandEvent& WXUNUSED(event) ) { - int no_thrd = m_threads.Count() - 1; + static wxString s_str; + s_str = wxGetTextFromUser("How many threads to start: ", + "wxThread sample", + s_str, this); + if ( s_str.IsEmpty() ) + return; - if ( no_thrd < 0 ) + 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++ ) { - wxLogError("No thread to stop!"); + threads.Add(CreateThread()); + } - return; + 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 = CreateThread(); + + if ( thread->Run() != wxTHREAD_NO_ERROR ) + { + wxLogError("Can't start thread!"); + } + + SetStatusText("New thread started.", 1); +} + +void MyFrame::OnStopThread(wxCommandEvent& WXUNUSED(event) ) +{ + // stop the last thread + if ( m_threads.IsEmpty() ) + { + wxLogError("No thread to stop!"); } + else + { + m_critsect.Enter(); - DeleteThread(no_thrd); + wxThread *thread = m_threads.Last(); + m_stopped.Add(thread); + + // it's important to leave critical section before calling Delete() + // because delete will (implicitly) call OnThreadExit() which also tries + // to enter the same crit section - would dead lock. + m_critsect.Leave(); + + thread->Delete(); + + SetStatusText("Thread stopped.", 1); + } } void MyFrame::OnResumeThread(wxCommandEvent& WXUNUSED(event) ) @@ -268,14 +350,20 @@ void MyFrame::OnResumeThread(wxCommandEvent& WXUNUSED(event) ) wxCriticalSectionLocker enter(m_critsect); // resume first suspended thread - size_t n = 0; - while ( n < m_threads.Count() && m_threads[n]->IsPaused() ) - n--; + size_t n = 0, count = m_threads.Count(); + while ( n < count && !m_threads[n]->IsPaused() ) + n++; - if ( n < 0 ) + if ( n == count ) + { wxLogError("No thread to resume!"); + } else + { m_threads[n]->Resume(); + + SetStatusText("Thread resumed.", 1); + } } void MyFrame::OnPauseThread(wxCommandEvent& WXUNUSED(event) ) @@ -288,23 +376,36 @@ void MyFrame::OnPauseThread(wxCommandEvent& WXUNUSED(event) ) n--; if ( n < 0 ) + { wxLogError("No thread to pause!"); + } else + { m_threads[n]->Pause(); + + SetStatusText("Thread paused.", 1); + } } // set the frame title indicating the current number of threads void MyFrame::OnIdle(wxIdleEvent &event) { - // first remove from the array all the threads which died since last call + // first wait for all the threads which dies since the last call { wxCriticalSectionLocker enter(m_critsect); - size_t nCount = m_aToDelete.Count(); + size_t nCount = m_terminated.GetCount(); for ( size_t n = 0; n < nCount; n++ ) - DeleteThread((size_t)m_aToDelete[n]); - - m_aToDelete.Empty(); + { + // don't delete the threads which were stopped - they were already + // deleted in OnStopThread() + wxThread *thread = m_terminated[n]; + if ( m_stopped.Index(thread) == wxNOT_FOUND ) + thread->Delete(); + } + + m_stopped.Empty(); + m_terminated.Empty(); } size_t nRunning = 0, @@ -315,21 +416,32 @@ void MyFrame::OnIdle(wxIdleEvent &event) nRunning++; } - wxLogStatus(this, "%u threads total, %u running.", nCount, 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) ) { - for ( size_t i = 0; i < m_threads.Count(); i++ ) - delete m_threads[i]; + size_t count = m_threads.Count(); + for ( size_t i = 0; i < count; i++ ) + { + m_threads[i]->Delete(); + } Close(TRUE); } void MyFrame::OnAbout(wxCommandEvent& WXUNUSED(event) ) { - wxMessageDialog dialog(this, "wxThread sample (based on minimal)\n" - "Julian Smart, Guilhem Lavaux, Vadim Zeitlin", + 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); @@ -343,16 +455,8 @@ void MyFrame::OnClear(wxCommandEvent& WXUNUSED(event)) void MyFrame::OnThreadExit(wxThread *thread) { - int index = m_threads.Index(thread); - wxCHECK_RET( index != -1, "unknown thread being deleted??" ); - wxCriticalSectionLocker enter(m_critsect); - m_aToDelete.Add(index); + m_threads.Remove(thread); + m_terminated.Add(thread); } - -void MyFrame::DeleteThread(size_t index) -{ - delete m_threads[index]; - m_threads.Remove(index); -} \ No newline at end of file