From bee503b05f2e67035e1f45bd33e94451daeceb31 Mon Sep 17 00:00:00 2001 From: Vadim Zeitlin Date: Sat, 9 Jan 1999 00:28:27 +0000 Subject: [PATCH] thread fixes for MSW (samples doesn't compile currently under !MSW, will be fixed a.s.a.p.) git-svn-id: https://svn.wxwidgets.org/svn/wx/wxWidgets/trunk@1351 c3d73ce0-8a6f-49c7-b76d-6d57e0e08775 --- include/wx/thread.h | 30 +++++- samples/thread/test.cpp | 135 ++++++++++++++++++-------- src/msw/app.cpp | 123 ++++++++++++++++++------ src/msw/thread.cpp | 205 +++++++++++++++++++++++++++++++--------- 4 files changed, 376 insertions(+), 117 deletions(-) diff --git a/include/wx/thread.h b/include/wx/thread.h index d5cd919400..fb12b9ccc8 100644 --- a/include/wx/thread.h +++ b/include/wx/thread.h @@ -139,13 +139,13 @@ private: class WXDLLEXPORT wxCriticalSectionLocker { public: - wxCriticalSectionLocker(wxCriticalSection *critsect) - { (m_critsect = critsect)->Enter(); } + wxCriticalSectionLocker(wxCriticalSection& critsect) : m_critsect(critsect) + { m_critsect.Enter(); } ~wxCriticalSectionLocker() - { m_critsect->Leave(); } + { m_critsect.Leave(); } private: - wxCriticalSection *m_critsect; + wxCriticalSection& m_critsect; }; #endif @@ -253,6 +253,20 @@ private: void WXDLLEXPORT wxMutexGuiEnter(); void WXDLLEXPORT wxMutexGuiLeave(); +// implementation only +#ifdef __WXMSW__ + // unlock GUI if there are threads waiting for and lock it back when + // there are no more of them - should be called periodically by the main + // thread + void WXDLLEXPORT wxMutexGuiLeaveOrEnter(); + + // returns TRUE if the main thread has GUI lock + inline bool WXDLLEXPORT wxGuiOwnedByMainThread(); + + // wakes up the main thread if it's sleeping inside ::GetMessage() + inline void WXDLLEXPORT wxWakeUpMainThread(); +#endif // MSW + #else // !wxUSE_THREADS // no thread support @@ -261,4 +275,12 @@ inline void WXDLLEXPORT wxMutexGuiLeave() { } #endif // wxUSE_THREADS +// automatically unlock GUI mutex in dtor +class WXDLLEXPORT wxMutexGuiLocker +{ +public: + wxMutexGuiLocker() { wxMutexGuiEnter(); } + ~wxMutexGuiLocker() { wxMutexGuiLeave(); } +}; + #endif // __THREADH__ diff --git a/samples/thread/test.cpp b/samples/thread/test.cpp index a2aaaf940c..cc19d4d063 100644 --- a/samples/thread/test.cpp +++ b/samples/thread/test.cpp @@ -9,9 +9,17 @@ // Licence: wxWindows license ///////////////////////////////////////////////////////////////////////////// +/* + TODO: + + 1. show how SetPriority() works. + 2. 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". @@ -55,26 +63,35 @@ public: // callbacks void OnQuit(wxCommandEvent& event); void OnAbout(wxCommandEvent& event); + void OnClear(wxCommandEvent& event); void OnStartThread(wxCommandEvent& event); void OnStopThread(wxCommandEvent& event); void OnPauseThread(wxCommandEvent& event); void OnResumeThread(wxCommandEvent& event); - void OnSize(wxSizeEvent &event); void OnIdle(wxIdleEvent &event); bool OnClose() { return TRUE; } + // called by dying thread + void OnThreadExit(wxThread *thread); + public: wxArrayThread m_threads; private: - wxTextCtrl *m_txtctrl; + void DeleteThread(size_t index); + + // crit section protects access to the array below + wxCriticalSection m_critsect; + wxArrayInt m_aToDelete; + + wxTextCtrl *m_txtctrl; DECLARE_EVENT_TABLE() }; -class MyThread: public wxThread +class MyThread : public wxThread { public: MyThread(MyFrame *frame); @@ -82,6 +99,9 @@ public: // thread execution starts here virtual void *Entry(); + // called when the thread exits - whether + virtual void OnExit(); + // write something to the text control void WriteText(const wxString& text); @@ -104,9 +124,14 @@ void MyThread::WriteText(const wxString& text) // before doing any GUI calls we must ensure that this thread is the only // one doing it! - wxMutexGuiEnter(); + wxMutexGuiLocker guiLocker; + m_frame->WriteText(msg); - wxMutexGuiLeave(); +} + +void MyThread::OnExit() +{ + m_frame->OnThreadExit(this); } void *MyThread::Entry() @@ -118,7 +143,7 @@ void *MyThread::Entry() text.Printf("Thread 0x%x started.\n", GetID()); WriteText(text); - for ( m_count = 0; m_count < 20; m_count++ ) + for ( m_count = 0; m_count < 10; m_count++ ) { // check if we were asked to exit if ( TestDestroy() ) @@ -127,7 +152,11 @@ void *MyThread::Entry() text.Printf("[%u] Thread 0x%x here.\n", m_count, GetID()); WriteText(text); +#ifdef __WXMSW__ + ::Sleep(1000); +#else wxSleep(1); +#endif } text.Printf("Thread 0x%x finished.\n", GetID()); @@ -142,22 +171,22 @@ enum TEST_QUIT = 1, TEST_TEXT = 101, TEST_ABOUT = 102, - TEST_START_THREAD = 103, - TEST_STOP_THREAD = 104, - TEST_PAUSE_THREAD = 105, - TEST_RESUME_THREAD = 106 + TEST_CLEAR = 103, + TEST_START_THREAD = 203, + TEST_STOP_THREAD = 204, + TEST_PAUSE_THREAD = 205, + TEST_RESUME_THREAD = 206 }; BEGIN_EVENT_TABLE(MyFrame, wxFrame) 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_STOP_THREAD, MyFrame::OnStopThread) EVT_MENU(TEST_PAUSE_THREAD, MyFrame::OnPauseThread) EVT_MENU(TEST_RESUME_THREAD, MyFrame::OnResumeThread) - EVT_SIZE(MyFrame::OnSize) - EVT_IDLE(MyFrame::OnIdle) END_EVENT_TABLE() @@ -168,12 +197,16 @@ IMPLEMENT_APP (MyApp) bool MyApp::OnInit() { // Create the main frame window - MyFrame *frame = new MyFrame((wxFrame *)NULL, "", 50, 50, 450, 340); + 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"); + file_menu->AppendSeparator(); file_menu->Append(TEST_ABOUT, "&About"); + file_menu->AppendSeparator(); file_menu->Append(TEST_QUIT, "E&xit"); wxMenuBar *menu_bar = new wxMenuBar; menu_bar->Append(file_menu, "&File"); @@ -199,13 +232,11 @@ bool MyApp::OnInit() MyFrame::MyFrame(wxFrame *frame, char *title, int x, int y, int w, int h) : wxFrame(frame, -1, title, wxPoint(x, y), wxSize(w, h)) { - wxPanel *panel = new wxPanel(this, -1, wxPoint(0, 0), wxSize(400, 200), - wxTAB_TRAVERSAL); + CreateStatusBar(); - m_txtctrl = new wxTextCtrl(panel, -1, "", wxPoint(10,30), wxSize(390, 190), - wxTE_MULTILINE); + m_txtctrl = new wxTextCtrl(this, -1, "", wxPoint(0, 0), wxSize(0, 0), + wxTE_MULTILINE | wxTE_READONLY); - (void)new wxStaticText(panel, -1, "Log window", wxPoint(10,10)); } void MyFrame::OnStartThread(wxCommandEvent& WXUNUSED(event) ) @@ -214,35 +245,43 @@ void MyFrame::OnStartThread(wxCommandEvent& WXUNUSED(event) ) thread->Create(); + wxCriticalSectionLocker enter(m_critsect); m_threads.Add(thread); } void MyFrame::OnStopThread(wxCommandEvent& WXUNUSED(event) ) { - int no_thrd = m_threads.Count()-1; + int no_thrd = m_threads.Count() - 1; + + if ( no_thrd < 0 ) + { + wxLogError("No thread to stop!"); - if (no_thrd < 0) return; + } - delete m_threads[no_thrd]; - m_threads.Remove(no_thrd); + DeleteThread(no_thrd); } 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--; if ( n < 0 ) - wxLogError("No thread to pause!"); + wxLogError("No thread to resume!"); else m_threads[n]->Resume(); } void MyFrame::OnPauseThread(wxCommandEvent& WXUNUSED(event) ) { + wxCriticalSectionLocker enter(m_critsect); + // pause last running thread int n = m_threads.Count() - 1; while ( n >= 0 && !m_threads[n]->IsRunning() ) @@ -257,6 +296,17 @@ void MyFrame::OnPauseThread(wxCommandEvent& WXUNUSED(event) ) // 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 + { + wxCriticalSectionLocker enter(m_critsect); + + size_t nCount = m_aToDelete.Count(); + for ( size_t n = 0; n < nCount; n++ ) + DeleteThread((size_t)m_aToDelete[n]); + + m_aToDelete.Empty(); + } + size_t nRunning = 0, nCount = m_threads.Count(); for ( size_t n = 0; n < nCount; n++ ) @@ -265,19 +315,7 @@ void MyFrame::OnIdle(wxIdleEvent &event) nRunning++; } - wxString title; - title.Printf("wxWindows thread sample (%u threads, %u running).", - nCount, nRunning); - SetTitle(title); -} - -void MyFrame::OnSize(wxSizeEvent& event) -{ - wxFrame::OnSize(event); - - wxSize size( GetClientSize() ); - - m_txtctrl->SetSize( 10, 30, size.x-20, size.y-40 ); + wxLogStatus(this, "%u threads total, %u running.", nCount, nRunning); } void MyFrame::OnQuit(wxCommandEvent& WXUNUSED(event) ) @@ -291,11 +329,30 @@ void MyFrame::OnQuit(wxCommandEvent& WXUNUSED(event) ) void MyFrame::OnAbout(wxCommandEvent& WXUNUSED(event) ) { wxMessageDialog dialog(this, "wxThread sample (based on minimal)\n" - "Julian Smart and Guilhem Lavaux", + "Julian Smart, Guilhem Lavaux, Vadim Zeitlin", "About wxThread sample", wxOK | wxICON_INFORMATION); dialog.ShowModal(); } +void MyFrame::OnClear(wxCommandEvent& WXUNUSED(event)) +{ + m_txtctrl->Clear(); +} + +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); +} + +void MyFrame::DeleteThread(size_t index) +{ + delete m_threads[index]; + m_threads.Remove(index); +} \ No newline at end of file diff --git a/src/msw/app.cpp b/src/msw/app.cpp index bd54f4b772..e795e42453 100644 --- a/src/msw/app.cpp +++ b/src/msw/app.cpp @@ -40,8 +40,15 @@ #include "wx/module.h" #if wxUSE_THREADS -#include "wx/thread.h" -#endif + #include "wx/thread.h" + + // define the array of MSG strutures + WX_DECLARE_OBJARRAY(MSG, wxMsgArray); + + #include "wx/arrimpl.cpp" + + WX_DEFINE_OBJARRAY(wxMsgArray); +#endif // wxUSE_THREADS #if wxUSE_WX_RESOURCES #include "wx/resource.h" @@ -736,24 +743,82 @@ bool wxApp::Initialized() /* * Get and process a message, returning FALSE if WM_QUIT - * received. + * received (and also set the flag telling the app to exit the main loop) * */ bool wxApp::DoMessage() { - if (!::GetMessage(&s_currentMsg, (HWND) NULL, 0, 0)) - { - return FALSE; - } + BOOL rc = ::GetMessage(&s_currentMsg, (HWND) NULL, 0, 0); + if ( rc == 0 ) + { + // got WM_QUIT + m_keepGoing = FALSE; + + return FALSE; + } + else if ( rc == -1 ) + { + // should never happen, but let's test for it nevertheless + wxLogLastError("GetMessage"); + } + else + { +#if wxUSE_THREADS + wxASSERT_MSG( wxThread::IsMain(), + "only the main thread can process Windows messages" ); - // Process the message - if (!ProcessMessage((WXMSG *)&s_currentMsg)) - { - ::TranslateMessage(&s_currentMsg); - wxApp::sm_lastMessageTime = s_currentMsg.time; /* MATTHEW: timeStamp impl. */ - ::DispatchMessage(&s_currentMsg); - } - return TRUE; + static bool s_hadGuiLock = TRUE; + static wxMsgArray s_aSavedMessages; + + // if a secondary thread owns is doing GUI calls, save all messages for + // later processing - we can't process them right now because it will + // lead to recursive library calls (and we're not reentrant) + if ( !wxGuiOwnedByMainThread() ) + { + s_hadGuiLock = FALSE; + + s_aSavedMessages.Add(s_currentMsg); + + return TRUE; + } + else + { + // have we just regained the GUI lock? if so, post all of the saved + // messages + // + // FIXME of course, it's not _exactly_ the same as processing the + // messages normally - expect some things to break... + if ( !s_hadGuiLock ) + { + s_hadGuiLock = TRUE; + + size_t count = s_aSavedMessages.Count(); + for ( size_t n = 0; n < count; n++ ) + { + MSG& msg = s_aSavedMessages[n]; + + if ( !ProcessMessage((WXMSG *)&msg) ) + { + ::TranslateMessage(&msg); + ::DispatchMessage(&msg); + } + } + + s_aSavedMessages.Empty(); + } + } +#endif // wxUSE_THREADS + + // Process the message + if ( !ProcessMessage((WXMSG *)&s_currentMsg) ) + { + ::TranslateMessage(&s_currentMsg); + wxApp::sm_lastMessageTime = s_currentMsg.time; /* MATTHEW: timeStamp impl. */ + ::DispatchMessage(&s_currentMsg); + } + } + + return TRUE; } /* @@ -773,12 +838,19 @@ bool wxApp::DoMessage() int wxApp::MainLoop() { m_keepGoing = TRUE; - while (m_keepGoing) + + while ( m_keepGoing ) { - while (!::PeekMessage(&s_currentMsg, 0, 0, 0, PM_NOREMOVE) && - ProcessIdle()) {} - if (!DoMessage()) - m_keepGoing = FALSE; + #if wxUSE_THREADS + wxMutexGuiLeaveOrEnter(); + #endif // wxUSE_THREADS + + while ( !::PeekMessage(&s_currentMsg, 0, 0, 0, PM_NOREMOVE) && + ProcessIdle() ) + { + } + + DoMessage(); } return s_currentMsg.wParam; @@ -806,8 +878,7 @@ bool wxApp::Pending() void wxApp::Dispatch() { - if (!DoMessage()) - m_keepGoing = FALSE; + DoMessage(); } /* @@ -871,12 +942,6 @@ void wxApp::OnIdle(wxIdleEvent& event) // idle events event.RequestMore(TRUE); } -#if wxUSE_THREADS - // give a chance to all other threads to perform GUI calls - wxMutexGuiLeave(); - ::Sleep(0); - wxMutexGuiEnter(); -#endif s_inOnIdle = FALSE; } @@ -1029,7 +1094,7 @@ bool wxYield() // if we see a WM_QUIT. (?) while (PeekMessage(&msg, NULL, 0, 0, PM_NOREMOVE) && msg.message != WM_QUIT) { - if (!wxTheApp->DoMessage()) + if ( !wxTheApp->DoMessage() ) break; } diff --git a/src/msw/thread.cpp b/src/msw/thread.cpp index d958232456..eb0f663724 100644 --- a/src/msw/thread.cpp +++ b/src/msw/thread.cpp @@ -2,10 +2,11 @@ // Name: thread.cpp // Purpose: wxThread Implementation // Author: Original from Wolfram Gloger/Guilhem Lavaux -// Modified by: +// Modified by: Vadim Zeitlin to make it work :-) // Created: 04/22/98 // RCS-ID: $Id$ -// Copyright: (c) Wolfram Gloger (1996, 1997); Guilhem Lavaux (1998) +// Copyright: (c) Wolfram Gloger (1996, 1997); Guilhem Lavaux (1998), +// Vadim Zeitlin (1999) // Licence: wxWindows licence ///////////////////////////////////////////////////////////////////////////// @@ -41,6 +42,7 @@ enum thread_state { STATE_IDLE = 0, STATE_RUNNING, + STATE_PAUSED, STATE_CANCELED, STATE_EXITED }; @@ -51,12 +53,21 @@ enum thread_state // id of the main thread - the one which can call GUI functions without first // calling wxMutexGuiEnter() -static HANDLE s_idMainThread; +static DWORD s_idMainThread = 0; + +// if it's FALSE, some secondary thread is holding the GUI lock +static bool s_bGuiOwnedByMainThread = TRUE; // critical section which controls access to all GUI functions: any secondary // thread (i.e. except the main one) must enter this crit section before doing // any GUI calls -static wxCriticalSection *s_critsectGui; +static wxCriticalSection *s_critsectGui = NULL; + +// critical section which protects s_nWaitingForGui variable +static wxCriticalSection *s_critsectWaitingForGui = NULL; + +// number of threads waiting for GUI in wxMutexGuiEnter() +static size_t s_nWaitingForGui = 0; // ============================================================================ // Windows implementation of thread classes @@ -263,40 +274,43 @@ void wxCriticalSection::Leave() class wxThreadInternal { public: - static DWORD WinThreadStart(LPVOID arg); + static DWORD WinThreadStart(wxThread *thread); - HANDLE thread_id; - int state; - int prio, defer; - DWORD tid; + HANDLE hThread; + thread_state state; + int prio, defer; + DWORD tid; }; -DWORD wxThreadInternal::WinThreadStart(LPVOID arg) +DWORD wxThreadInternal::WinThreadStart(wxThread *thread) { - wxThread *ptr = (wxThread *)arg; - DWORD ret; - - ret = (DWORD)ptr->Entry(); - ptr->p_internal->state = STATE_EXITED; + DWORD ret = (DWORD)thread->Entry(); + thread->p_internal->state = STATE_EXITED; + thread->OnExit(); return ret; } wxThreadError wxThread::Create() { - 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 ) + p_internal->hThread = ::CreateThread + ( + NULL, // default security + 0, // default stack size + (LPTHREAD_START_ROUTINE) + wxThreadInternal::WinThreadStart, // entry point + (void *)this, // parameter + CREATE_SUSPENDED, // flags + &p_internal->tid // [out] thread id + ); + + if ( p_internal->hThread == NULL ) { wxLogSysError(_("Can't create thread")); return wxTHREAD_NO_RESOURCE; } - int win_prio; + int win_prio, prio = p_internal->prio; if (prio <= 20) win_prio = THREAD_PRIORITY_LOWEST; else if (prio <= 40) @@ -313,53 +327,65 @@ wxThreadError wxThread::Create() win_prio = THREAD_PRIORITY_NORMAL; } - SetThreadPriority(p_internal->thread_id, win_prio); - - ResumeThread(p_internal->thread_id); - p_internal->state = STATE_RUNNING; + if ( SetThreadPriority(p_internal->hThread, win_prio) == 0 ) + { + wxLogSysError(_("Can't set thread priority")); + } - return wxTHREAD_NO_ERROR; + return Resume(); } wxThreadError wxThread::Destroy() { - if (p_internal->state != STATE_RUNNING) + if ( p_internal->state != STATE_RUNNING ) return wxTHREAD_NOT_RUNNING; if ( p_internal->defer ) + { // soft termination: just set the flag and wait until the thread // auto terminates p_internal->state = STATE_CANCELED; + } else + { // kill the thread - TerminateThread(p_internal->thread_id, 0); + OnExit(); + if ( ::TerminateThread(p_internal->hThread, 0) == 0 ) + { + wxLogLastError("TerminateThread"); + } + } return wxTHREAD_NO_ERROR; } wxThreadError wxThread::Pause() { - DWORD nSuspendCount = ::SuspendThread(p_internal->thread_id); + DWORD nSuspendCount = ::SuspendThread(p_internal->hThread); if ( nSuspendCount == (DWORD)-1 ) { - wxLogSysError(_("Can not suspend thread %x"), p_internal->thread_id); + wxLogSysError(_("Can not suspend thread %x"), p_internal->hThread); return wxTHREAD_MISC_ERROR; // no idea what might provoke this error... } + p_internal->state = STATE_PAUSED; + return wxTHREAD_NO_ERROR; } wxThreadError wxThread::Resume() { - DWORD nSuspendCount = ::ResumeThread(p_internal->thread_id); + DWORD nSuspendCount = ::ResumeThread(p_internal->hThread); if ( nSuspendCount == (DWORD)-1 ) { - wxLogSysError(_("Can not resume thread %x"), p_internal->thread_id); + wxLogSysError(_("Can not resume thread %x"), p_internal->hThread); return wxTHREAD_MISC_ERROR; // no idea what might provoke this error... } + p_internal->state = STATE_RUNNING; + return wxTHREAD_NO_ERROR; } @@ -396,14 +422,15 @@ void *wxThread::Join() if (p_internal->state == STATE_IDLE) return NULL; - if (wxThread::IsMain()) - s_critsectGui->Leave(); - WaitForSingleObject(p_internal->thread_id, INFINITE); - if (wxThread::IsMain()) - s_critsectGui->Enter(); + // FIXME this dead locks... wxThread class design must be changed +#if 0 + WaitForSingleObject(p_internal->hThread, INFINITE); +#else + ::TerminateThread(p_internal->hThread, 0); +#endif // 0 - GetExitCodeThread(p_internal->thread_id, &exit_code); - CloseHandle(p_internal->thread_id); + GetExitCodeThread(p_internal->hThread, &exit_code); + CloseHandle(p_internal->hThread); p_internal->state = STATE_IDLE; @@ -427,7 +454,7 @@ bool wxThread::IsAlive() const bool wxThread::IsMain() { - return (GetCurrentThread() == s_idMainThread); + return ::GetCurrentThreadId() == s_idMainThread; } wxThread::wxThread() @@ -468,9 +495,12 @@ IMPLEMENT_DYNAMIC_CLASS(wxThreadModule, wxModule) bool wxThreadModule::OnInit() { + s_critsectWaitingForGui = new wxCriticalSection(); + s_critsectGui = new wxCriticalSection(); s_critsectGui->Enter(); - s_idMainThread = GetCurrentThread(); + + s_idMainThread = ::GetCurrentThreadId(); return TRUE; } @@ -483,18 +513,103 @@ void wxThreadModule::OnExit() delete s_critsectGui; s_critsectGui = NULL; } + + wxDELETE(s_critsectWaitingForGui); } +// ---------------------------------------------------------------------------- // under Windows, these functions are implemented usign a critical section and // not a mutex, so the names are a bit confusing +// ---------------------------------------------------------------------------- + void WXDLLEXPORT wxMutexGuiEnter() { - //s_critsectGui->Enter(); + // this would dead lock everything... + wxASSERT_MSG( !wxThread::IsMain(), + "main thread doesn't want to block in wxMutexGuiEnter()!" ); + + // the order in which we enter the critical sections here is crucial!! + + // set the flag telling to the main thread that we want to do some GUI + { + wxCriticalSectionLocker enter(*s_critsectWaitingForGui); + + s_nWaitingForGui++; + } + + wxWakeUpMainThread(); + + // now we may block here because the main thread will soon let us in + // (during the next iteration of OnIdle()) + s_critsectGui->Enter(); } void WXDLLEXPORT wxMutexGuiLeave() { - //s_critsectGui->Leave(); + wxCriticalSectionLocker enter(*s_critsectWaitingForGui); + + if ( wxThread::IsMain() ) + { + s_bGuiOwnedByMainThread = FALSE; + } + else + { + // decrement the number of waiters now + wxASSERT_MSG( s_nWaitingForGui > 0, + "calling wxMutexGuiLeave() without entering it first?" ); + + s_nWaitingForGui--; + + wxWakeUpMainThread(); + } + + s_critsectGui->Leave(); +} + +void WXDLLEXPORT wxMutexGuiLeaveOrEnter() +{ + wxASSERT_MSG( wxThread::IsMain(), + "only main thread may call wxMutexGuiLeaveOrEnter()!" ); + + wxCriticalSectionLocker enter(*s_critsectWaitingForGui); + + if ( s_nWaitingForGui == 0 ) + { + // no threads are waiting for GUI - so we may acquire the lock without + // any danger (but only if we don't already have it) + if ( !wxGuiOwnedByMainThread() ) + { + s_critsectGui->Enter(); + + s_bGuiOwnedByMainThread = TRUE; + } + //else: already have it, nothing to do + } + else + { + // some threads are waiting, release the GUI lock if we have it + if ( wxGuiOwnedByMainThread() ) + { + wxMutexGuiLeave(); + } + //else: some other worker thread is doing GUI + } +} + +bool WXDLLEXPORT wxGuiOwnedByMainThread() +{ + return s_bGuiOwnedByMainThread; +} + +// wake up the main thread if it's in ::GetMessage() +void WXDLLEXPORT wxWakeUpMainThread() +{ + // sending any message would do - hopefully WM_NULL is harmless enough + if ( !::PostThreadMessage(s_idMainThread, WM_NULL, 0, 0) ) + { + // should never happen + wxLogLastError("PostThreadMessage(WM_NULL)"); + } } #endif // wxUSE_THREADS -- 2.47.2