void WXDLLIMPEXP_BASE wxMutexGuiLeaveOrEnter() { wxASSERT_MSG( wxThread::IsMain(), wxT("only main thread may call wxMutexGuiLeaveOrEnter()!") ); if ( !gs_critsectWaitingForGui ) return; wxCriticalSectionLocker enter(*gs_critsectWaitingForGui); if ( gs_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() ) { gs_critsectGui->Enter(); gs_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 wxGUIEventLoop::Dispatch() { MSG msg; if ( !GetNextMessage(&msg) ) return false; #if wxUSE_THREADS wxASSERT_MSG( wxThread::IsMain(), wxT("only the main thread can process Windows messages") ); static bool s_hadGuiLock = true; static wxMsgList s_aSavedMessages; // if a secondary thread owning the mutex 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; // leave out WM_COMMAND messages: too dangerous, sometimes // the message will be processed twice if ( !wxIsWaitingForThread() || msg.message != WM_COMMAND ) { MSG* pMsg = new MSG(msg); s_aSavedMessages.Append(pMsg); } 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; wxMsgList::compatibility_iterator node = s_aSavedMessages.GetFirst(); while (node) { MSG* pMsg = node->GetData(); s_aSavedMessages.Erase(node); ProcessMessage(pMsg); delete pMsg; node = s_aSavedMessages.GetFirst(); } } } #endif // wxUSE_THREADS ProcessMessage(&msg); return true; }
void wxThreadModule::OnExit() { if ( gs_critsectGui ) { if ( !wxGuiOwnedByMainThread() ) { gs_critsectGui->Enter(); gs_bGuiOwnedByMainThread = true; } gs_critsectGui->Leave(); wxDELETE(gs_critsectGui); } wxDELETE(gs_critsectWaitingForGui); }
void wxThreadInternal::Wait() { wxCHECK_RET( !m_isDetached, wxT("can't wait for a detached thread") ); // if the thread we're waiting for is waiting for the GUI mutex, we will // deadlock so make sure we release it temporarily if ( wxThread::IsMain() ) { // give the thread we're waiting for chance to do the GUI call // it might be in, we don't do this conditionally as the to be waited on // thread might have to acquire the mutex later but before terminating if ( wxGuiOwnedByMainThread() ) wxMutexGuiLeave(); } { wxCriticalSectionLocker lock(m_csJoinFlag); if ( m_shouldBeJoined ) { void *param1, *param2, *rc; OSStatus err = MPWaitOnQueue( m_notifyQueueId, ¶m1, ¶m2, &rc, kDurationForever ); if (err != noErr) { wxLogSysError( wxT( "Cannot wait for thread termination.")); rc = (void*) -1; } // actually param1 would be the address of m_exitcode // but we don't need this here m_exitcode = rc; m_shouldBeJoined = false; } } }
bool wxEventLoop::Dispatch() { wxCHECK_MSG( IsRunning(), false, _T("can't call Dispatch() if not running") ); MSG msg; BOOL rc = ::GetMessage(&msg, (HWND) NULL, 0, 0); if ( rc == 0 ) { // got WM_QUIT return false; } if ( rc == -1 ) { // should never happen, but let's test for it nevertheless wxLogLastError(wxT("GetMessage")); // still break from the loop return false; } #if wxUSE_THREADS wxASSERT_MSG( wxThread::IsMain(), wxT("only the main thread can process Windows messages") ); static bool s_hadGuiLock = true; static wxMsgList s_aSavedMessages; // if a secondary thread owning the mutex 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; // leave out WM_COMMAND messages: too dangerous, sometimes // the message will be processed twice if ( !wxIsWaitingForThread() || msg.message != WM_COMMAND ) { MSG* pMsg = new MSG(msg); s_aSavedMessages.Append(pMsg); } 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; wxMsgList::compatibility_iterator node = s_aSavedMessages.GetFirst(); while (node) { MSG* pMsg = node->GetData(); s_aSavedMessages.Erase(node); ProcessMessage(pMsg); delete pMsg; node = s_aSavedMessages.GetFirst(); } } } #endif // wxUSE_THREADS ProcessMessage(&msg); return true; }
wxThreadError wxThread::Delete(ExitCode *pRc) { ExitCode rc = 0; // Delete() is always safe to call, so consider all possible states // we might need to resume the thread, but we might also not need to cancel // it if it doesn't run yet bool shouldResume = false, shouldCancel = true, isRunning = false; // check if the thread already started to run { wxCriticalSectionLocker lock((wxCriticalSection &)m_critsect); if ( m_internal->GetState() == STATE_NEW ) { // WinThreadStart() will see it and terminate immediately, no need // to cancel the thread - but we still need to resume it to let it // run m_internal->SetState(STATE_EXITED); Resume(); // it knows about STATE_EXITED special case shouldCancel = false; isRunning = true; // shouldResume is correctly set to false here } else { shouldResume = IsPaused(); } } // resume the thread if it is paused if ( shouldResume ) Resume(); TID hThread = m_internal->GetHandle(); if ( isRunning || IsRunning()) { if (IsMain()) { // set flag for wxIsWaitingForThread() gs_bWaitingForThread = true; } // ask the thread to terminate if ( shouldCancel ) { wxCriticalSectionLocker lock(m_critsect); m_internal->Cancel(); } #if 0 // we can't just wait for the thread to terminate because it might be // calling some GUI functions and so it will never terminate before we // process the Windows messages that result from these functions DWORD result = 0; // suppress warnings from broken compilers do { if ( IsMain() ) { // give the thread we're waiting for chance to do the GUI call // it might be in if ( (gs_nWaitingForGui > 0) && wxGuiOwnedByMainThread() ) { wxMutexGuiLeave(); } } result = ::DosWaitThread(&hThread, DCWW_NOWAIT); // FIXME: We ought to have a message processing loop here!! switch ( result ) { case ERROR_INTERRUPT: case ERROR_THREAD_NOT_TERMINATED: break; case ERROR_INVALID_THREADID: case NO_ERROR: // thread we're waiting for just terminated // or even does not exist any more. result = NO_ERROR; break; default: wxFAIL_MSG(wxT("unexpected result of DosWaitThread")); } if ( IsMain() ) { // event processing - needed if we are the main thread // to give other threads a chance to do remaining GUI // processing and terminate cleanly. wxTheApp->HandleSockets(); if (wxTheApp->Pending()) if ( !wxTheApp->DoMessage() ) { // WM_QUIT received: kill the thread Kill(); return wxTHREAD_KILLED; } else wxUsleep(10); } else wxUsleep(10); } while ( result != NO_ERROR ); #else // !wxUSE_GUI // simply wait for the thread to terminate // // OTOH, even console apps create windows (in wxExecute, for WinSock // &c), so may be use MsgWaitForMultipleObject() too here? if ( ::DosWaitThread(&hThread, DCWW_WAIT) != NO_ERROR ) { wxFAIL_MSG(wxT("unexpected result of DosWaitThread")); } #endif // wxUSE_GUI/!wxUSE_GUI if ( IsMain() ) { gs_bWaitingForThread = false; } } #if 0 // although the thread might be already in the EXITED state it might not // have terminated yet and so we are not sure that it has actually // terminated if the "if" above hadn't been taken do { if ( !::GetExitCodeThread(hThread, (LPDWORD)&rc) ) { wxLogLastError(wxT("GetExitCodeThread")); rc = (ExitCode)-1; } } while ( (DWORD)rc == STILL_ACTIVE ); #endif if ( IsDetached() ) { // if the thread exits normally, this is done in WinThreadStart, but in // this case it would have been too early because // MsgWaitForMultipleObject() would fail if the thread handle was // closed while we were waiting on it, so we must do it here delete this; } if ( pRc ) *pRc = rc; return rc == (ExitCode)-1 ? wxTHREAD_MISC_ERROR : wxTHREAD_NO_ERROR; }