From a6b0bd49c72e577a218bfe10fc1526cf2ad6293d Mon Sep 17 00:00:00 2001 From: Vadim Zeitlin <vadim@wxwidgets.org> Date: Sun, 27 Dec 1998 00:54:53 +0000 Subject: [PATCH] 1. Pause()/Resume() implemented for wxMSW 2. crash on startup in the sample corrected git-svn-id: https://svn.wxwidgets.org/svn/wx/wxWidgets/trunk@1267 c3d73ce0-8a6f-49c7-b76d-6d57e0e08775 --- include/wx/thread.h | 52 ++++++--- samples/thread/test.cpp | 229 +++++++++++++++++++++++----------------- src/msw/thread.cpp | 98 +++++++++++++---- 3 files changed, 249 insertions(+), 130 deletions(-) diff --git a/include/wx/thread.h b/include/wx/thread.h index 4ecfaa271e..b02df5244f 100644 --- a/include/wx/thread.h +++ b/include/wx/thread.h @@ -16,31 +16,40 @@ #pragma interface "thread.h" #endif +// ---------------------------------------------------------------------------- +// headers +// ---------------------------------------------------------------------------- #include "wx/object.h" #include "wx/setup.h" +// ---------------------------------------------------------------------------- +// constants +// ---------------------------------------------------------------------------- + typedef enum { - MUTEX_NO_ERROR=0, - MUTEX_DEAD_LOCK, // Mutex has been already locked by THE CALLING thread - MUTEX_BUSY, // Mutex has been already locked by ONE thread - MUTEX_UNLOCKED + wxMUTEX_NO_ERROR = 0, + wxMUTEX_DEAD_LOCK, // Mutex has been already locked by THE CALLING thread + wxMUTEX_BUSY, // Mutex has been already locked by ONE thread + wxMUTEX_UNLOCKED, + wxMUTEX_MISC_ERROR } wxMutexError; typedef enum { - THREAD_NO_ERROR=0, // No error - THREAD_NO_RESOURCE, // No resource left to create a new thread - THREAD_RUNNING, // The thread is already running - THREAD_NOT_RUNNING, // The thread isn't running - THREAD_MISC_ERROR // Some other error + wxTHREAD_NO_ERROR = 0, // No error + wxTHREAD_NO_RESOURCE, // No resource left to create a new thread + wxTHREAD_RUNNING, // The thread is already running + wxTHREAD_NOT_RUNNING, // The thread isn't running + wxTHREAD_MISC_ERROR // Some other error } wxThreadError; // defines the interval of priority. -#define WXTHREAD_MIN_PRIORITY 0 +#define WXTHREAD_MIN_PRIORITY 0 #define WXTHREAD_DEFAULT_PRIORITY 50 -#define WXTHREAD_MAX_PRIORITY 100 +#define WXTHREAD_MAX_PRIORITY 100 -// --------------------------------------------------------------------------- +// ---------------------------------------------------------------------------- // Mutex handler +// ---------------------------------------------------------------------------- class WXDLLEXPORT wxMutexInternal; class WXDLLEXPORT wxMutex { public: @@ -57,6 +66,7 @@ public: // Returns true if the mutex is locked. bool IsLocked() const { return (m_locked > 0); } + protected: friend class wxCondition; @@ -64,8 +74,9 @@ protected: wxMutexInternal *p_internal; }; -// --------------------------------------------------------------------------- +// ---------------------------------------------------------------------------- // Condition handler. +// ---------------------------------------------------------------------------- class wxConditionInternal; class WXDLLEXPORT wxCondition { public: @@ -81,12 +92,14 @@ public: void Signal(); // Broadcasts to all "Waiters". void Broadcast(); + private: wxConditionInternal *p_internal; }; -// --------------------------------------------------------------------------- +// ---------------------------------------------------------------------------- // Thread management class +// ---------------------------------------------------------------------------- class wxThreadInternal; class WXDLLEXPORT wxThread { public: @@ -126,6 +139,9 @@ public: bool IsAlive() const; // Returns true if the thread is running (not paused, not killed). bool IsRunning() const; + // Returns true if the thread is suspended + bool IsPaused() const { return IsAlive() && !IsRunning(); } + // Returns true if the thread is the main thread (aka the GUI thread). static bool IsMain(); @@ -137,6 +153,7 @@ protected: void TestDestroy(); // Exits from the current thread. void Exit(void *status = NULL); + private: // Entry point for the thread. virtual void *Entry() = 0; @@ -147,11 +164,12 @@ private: wxThreadInternal *p_internal; }; -// --------------------------------------------------------------------------- -// Global variables +// ---------------------------------------------------------------------------- +// Global functions and variables +// ---------------------------------------------------------------------------- // GUI mutex handling. void WXDLLEXPORT wxMutexGuiEnter(); void WXDLLEXPORT wxMutexGuiLeave(); -#endif +#endif // __THREADH__ diff --git a/samples/thread/test.cpp b/samples/thread/test.cpp index 1455acf3c5..071f954478 100644 --- a/samples/thread/test.cpp +++ b/samples/thread/test.cpp @@ -31,66 +31,80 @@ // Define a new application type class MyApp: public wxApp { - public: +public: bool OnInit(void); }; -WX_DEFINE_ARRAY(wxThread *,wxArrayThread); +WX_DEFINE_ARRAY(wxThread *, wxArrayThread); // Define a new frame type class MyFrame: public wxFrame { - public: +public: + // ctor MyFrame(wxFrame *frame, char *title, int x, int y, int w, int h); - public: + // operations + void WriteText(const wxString& text) { m_txtctrl->WriteText(text); } + + // callbacks void OnQuit(wxCommandEvent& event); void OnAbout(wxCommandEvent& event); + void OnStartThread(wxCommandEvent& event); void OnStopThread(wxCommandEvent& event); void OnPauseThread(wxCommandEvent& event); + void OnResumeThread(wxCommandEvent& event); + void OnSize(wxSizeEvent &event); bool OnClose(void) { return TRUE; } + +public: + wxArrayThread m_threads; - public: - wxArrayThread m_threads; - wxTextCtrl *m_txtctrl; - - DECLARE_EVENT_TABLE() +private: + wxTextCtrl *m_txtctrl; + + DECLARE_EVENT_TABLE() }; class MyThread: public wxThread { - public: +public: MyThread(MyFrame *frame); - + void *Entry(); - public: + +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() { - wxString text; - - text.Printf("Thread 0x%x: Hello !\n", GetID()); - DeferDestroy(TRUE); - - while (1) { - TestDestroy(); - wxMutexGuiEnter(); - m_frame->m_txtctrl->WriteText(text); - wxMutexGuiLeave(); - wxSleep(1); - } - - return NULL; + wxString text; + + DeferDestroy(TRUE); + + while (1) { + TestDestroy(); + wxMutexGuiEnter(); + + text.Printf("[%u] Thread 0x%x here.\n", ++m_count, GetID()); + m_frame->WriteText(text); + + wxMutexGuiLeave(); + wxSleep(1); + } + + return NULL; } // ID for the menu commands @@ -100,14 +114,17 @@ void *MyThread::Entry() #define TEST_START_THREAD 103 #define TEST_STOP_THREAD 104 #define TEST_PAUSE_THREAD 105 +#define TEST_RESUME_THREAD 106 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_START_THREAD, MyFrame::OnStartThread) + EVT_MENU(TEST_STOP_THREAD, MyFrame::OnStopThread) + EVT_MENU(TEST_PAUSE_THREAD, MyFrame::OnPauseThread) + EVT_MENU(TEST_RESUME_THREAD, MyFrame::OnResumeThread) + + EVT_SIZE(MyFrame::OnSize) END_EVENT_TABLE() // Create a new application object @@ -116,93 +133,117 @@ IMPLEMENT_APP (MyApp) // `Main program' equivalent, creating windows and returning main app frame bool MyApp::OnInit(void) { - // Create the main frame window - MyFrame *frame = new MyFrame((wxFrame *) NULL, (char *) "Minimal wxWindows App", 50, 50, 450, 340); - - // Make a menubar - wxMenu *file_menu = new wxMenu; - - file_menu->Append(TEST_ABOUT, "&About"); - file_menu->Append(TEST_QUIT, "E&xit"); - 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"); - 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); - - // 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); - - // Show the frame - frame->Show(TRUE); - - SetTopWindow(frame); - - return TRUE; + // Create the main frame window + MyFrame *frame = new MyFrame((wxFrame *) NULL, "wxWindows thread sample", + 50, 50, 450, 340); + + // Make a menubar + wxMenu *file_menu = new wxMenu; + + file_menu->Append(TEST_ABOUT, "&About"); + file_menu->Append(TEST_QUIT, "E&xit"); + 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"); + thread_menu->Append(TEST_STOP_THREAD, "Stop a running thread"); + thread_menu->AppendSeparator(); + thread_menu->Append(TEST_PAUSE_THREAD, "Pause a running thread"); + thread_menu->Append(TEST_RESUME_THREAD, "Resume suspended thread"); + menu_bar->Append(thread_menu, "Thread"); + frame->SetMenuBar(menu_bar); + + // Show the frame + frame->Show(TRUE); + + SetTopWindow(frame); + + return TRUE; } // 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::OnStartThread(wxCommandEvent& WXUNUSED(event) ) +MyFrame::MyFrame(wxFrame *frame, char *title, int x, int y, int w, int h) + : wxFrame(frame, -1, title, wxPoint(x, y), wxSize(w, h)) { - MyThread *thread = new MyThread(this); + wxPanel *panel = new wxPanel(this, -1, wxPoint(0, 0), wxSize(400, 200), + wxTAB_TRAVERSAL ); + + m_txtctrl = new wxTextCtrl(panel, -1, "", wxPoint(10,30), wxSize(390, 190), + wxTE_MULTILINE); - thread->Create(); + (void)new wxStaticText(panel, -1, "Log window", wxPoint(10,10)); +} - m_threads.Add(thread); +void MyFrame::OnStartThread(wxCommandEvent& WXUNUSED(event) ) +{ + MyThread *thread = new MyThread(this); + + thread->Create(); + + m_threads.Add(thread); } void MyFrame::OnStopThread(wxCommandEvent& WXUNUSED(event) ) { - int no_thrd = m_threads.Count()-1; - - if (no_thrd < 0) - return; + int no_thrd = m_threads.Count()-1; + + if (no_thrd < 0) + return; + + delete (m_threads[no_thrd]); + m_threads.Remove(no_thrd); +} - delete (m_threads[no_thrd]); - m_threads.Remove(no_thrd); +void MyFrame::OnResumeThread(wxCommandEvent& WXUNUSED(event) ) +{ + // resume first suspended thread + size_t n = 0; + while ( n < m_threads.Count() && m_threads[n]->IsPaused() ) + n--; + + if ( n < 0 ) + wxLogError("No thread to pause!"); + else + m_threads[n]->Resume(); } void MyFrame::OnPauseThread(wxCommandEvent& WXUNUSED(event) ) { + // pause last running thread + int n = m_threads.Count() - 1; + while ( n >= 0 && !m_threads[n]->IsRunning() ) + n--; + + if ( n < 0 ) + wxLogError("No thread to pause!"); + else + m_threads[n]->Pause(); } void MyFrame::OnSize(wxSizeEvent& event ) { - wxFrame::OnSize(event); - - wxSize size( GetClientSize() ); - - m_txtctrl->SetSize( 10, 30, size.x-20, size.y-40 ); + wxFrame::OnSize(event); + + wxSize size( GetClientSize() ); + + m_txtctrl->SetSize( 10, 30, size.x-20, size.y-40 ); } void MyFrame::OnQuit(wxCommandEvent& WXUNUSED(event) ) { - unsigned int i; - for (i=0;i<m_threads.Count();i++) - delete (m_threads[i]); - Close(TRUE); + unsigned int i; + for (i=0;i<m_threads.Count();i++) + delete (m_threads[i]); + 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); - - dialog.ShowModal(); + wxMessageDialog dialog(this, "wxThread sample (based on minimal)\nJulian Smart and Guilhem Lavaux", + "About wxThread sample", wxYES_NO|wxCANCEL); + + dialog.ShowModal(); } diff --git a/src/msw/thread.cpp b/src/msw/thread.cpp index 6ca57c6ce3..0832f9a2b7 100644 --- a/src/msw/thread.cpp +++ b/src/msw/thread.cpp @@ -59,13 +59,18 @@ wxMutex::wxMutex() { p_internal = new wxMutexInternal; p_internal->p_mutex = CreateMutex(NULL, FALSE, NULL); + if ( !p_internal->p_mutex ) + { + wxLogSysError(_("Can not create mutex.")); + } + m_locked = 0; } wxMutex::~wxMutex() { if (m_locked > 0) - wxDebugMsg("wxMutex warning: freeing a locked mutex (%d locks)\n", m_locked); + wxLogDebug("Warning: freeing a locked mutex (%d locks).", m_locked); CloseHandle(p_internal->p_mutex); } @@ -75,10 +80,10 @@ wxMutexError wxMutex::Lock() ret = WaitForSingleObject(p_internal->p_mutex, INFINITE); if (ret == WAIT_ABANDONED) - return MUTEX_BUSY; + return wxMUTEX_BUSY; m_locked++; - return MUTEX_NO_ERROR; + return wxMUTEX_NO_ERROR; } wxMutexError wxMutex::TryLock() @@ -87,23 +92,25 @@ wxMutexError wxMutex::TryLock() ret = WaitForSingleObject(p_internal->p_mutex, 0); if (ret == WAIT_TIMEOUT || ret == WAIT_ABANDONED) - return MUTEX_BUSY; + return wxMUTEX_BUSY; m_locked++; - return MUTEX_NO_ERROR; + return wxMUTEX_NO_ERROR; } wxMutexError wxMutex::Unlock() { - BOOL ret; - if (m_locked > 0) m_locked--; - // Why does this have 3 args? The redundant ones removed by JACS -// ret = ReleaseMutex(p_internal->p_mutex, 1, NULL); - ret = ReleaseMutex(p_internal->p_mutex); - return MUTEX_NO_ERROR; + BOOL ret = ReleaseMutex(p_internal->p_mutex); + if ( ret != 0 ) + { + wxLogSysError(_("Couldn't release a mutex")); + return wxMUTEX_MISC_ERROR; + } + + return wxMUTEX_NO_ERROR; } class wxConditionInternal { @@ -116,6 +123,11 @@ wxCondition::wxCondition() { p_internal = new wxConditionInternal; p_internal->event = CreateEvent(NULL, FALSE, FALSE, NULL); + if ( !p_internal->event ) + { + wxLogSysError(_("Can not create event object.")); + } + p_internal->waiters = 0; } @@ -157,7 +169,12 @@ void wxCondition::Broadcast() int i; for (i=0;i<p_internal->waiters;i++) - SetEvent(p_internal->event); + { + if ( SetEvent(p_internal->event) == 0 ) + { + wxLogSysError(_("Couldn't change the state of event object.")); + } + } } class wxThreadInternal { @@ -183,16 +200,19 @@ DWORD wxThreadInternal::WinThreadStart(LPVOID arg) wxThreadError wxThread::Create() { - int win_prio, prio = p_internal->prio; + int prio = p_internal->prio; p_internal->thread_id = CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE)wxThreadInternal::WinThreadStart, (void *)this, CREATE_SUSPENDED, &p_internal->tid); - if (p_internal->thread_id == NULL) { - printf("Error = %d\n", GetLastError()); - return THREAD_NO_RESOURCE; + + if ( p_internal->thread_id == NULL ) + { + wxLogSysError(_("Can't create thread")); + return wxTHREAD_NO_RESOURCE; } + int win_prio; if (prio <= 20) win_prio = THREAD_PRIORITY_LOWEST; else if (prio <= 40) @@ -203,26 +223,57 @@ wxThreadError wxThread::Create() win_prio = THREAD_PRIORITY_ABOVE_NORMAL; else if (prio <= 100) win_prio = THREAD_PRIORITY_HIGHEST; + else + { + wxFAIL_MSG("invalid value of thread priority parameter"); + win_prio = THREAD_PRIORITY_NORMAL; + } SetThreadPriority(p_internal->thread_id, win_prio); ResumeThread(p_internal->thread_id); p_internal->state = STATE_RUNNING; - return THREAD_NO_ERROR; + return wxTHREAD_NO_ERROR; } wxThreadError wxThread::Destroy() { if (p_internal->state != STATE_RUNNING) - return THREAD_NOT_RUNNING; + return wxTHREAD_NOT_RUNNING; if (p_internal->defer == FALSE) TerminateThread(p_internal->thread_id, 0); else p_internal->state = STATE_CANCELED; - return THREAD_NO_ERROR; + return wxTHREAD_NO_ERROR; +} + +wxThreadError wxThread::Pause() +{ + DWORD nSuspendCount = ::SuspendThread(p_internal->thread_id); + if ( nSuspendCount == (DWORD)-1 ) + { + wxLogSysError(_("Can not suspend thread %x"), p_internal->thread_id); + + return wxTHREAD_MISC_ERROR; // no idea what might provoke this error... + } + + return wxTHREAD_NO_ERROR; +} + +wxThreadError wxThread::Resume() +{ + DWORD nSuspendCount = ::ResumeThread(p_internal->thread_id); + if ( nSuspendCount == (DWORD)-1 ) + { + wxLogSysError(_("Can not resume thread %x"), p_internal->thread_id); + + return wxTHREAD_MISC_ERROR; // no idea what might provoke this error... + } + + return wxTHREAD_NO_ERROR; } void wxThread::Exit(void *status) @@ -335,3 +386,12 @@ public: IMPLEMENT_DYNAMIC_CLASS(wxThreadModule, wxModule) +void WXDLLEXPORT wxMutexGuiEnter() +{ + wxFAIL_MSG("not implemented"); +} + +void WXDLLEXPORT wxMutexGuiLeave() +{ + wxFAIL_MSG("not implemented"); +} -- 2.47.2