X-Git-Url: https://git.saurik.com/wxWidgets.git/blobdiff_plain/696e1ea0b7cee1394693a0e507bbf49c9863952b..32a87ae7b59f6a7087eeb43d176083c5366eb4df:/src/msw/thread.cpp?ds=sidebyside diff --git a/src/msw/thread.cpp b/src/msw/thread.cpp index 8bb3c99382..f76f9a4c7b 100644 --- a/src/msw/thread.cpp +++ b/src/msw/thread.cpp @@ -26,7 +26,7 @@ #endif #ifndef WX_PRECOMP - #include "wx/wx.h" +# include "wx/wx.h" #endif #if wxUSE_THREADS @@ -36,12 +36,29 @@ #include "wx/module.h" #include "wx/thread.h" +#ifdef Yield +# undef Yield +#endif + // must have this symbol defined to get _beginthread/_endthread declarations #ifndef _MT #define _MT #endif -#ifdef __VISUALC__ +#if defined(__VISUALC__) || \ + (defined(__BORLANDC__) && (__BORLANDC__ >= 0x500)) || \ + (defined(__GNUG__) && defined(__MSVCRT__)) + +#if defined(__BORLANDC__) && !defined(__MT__) +// I can't set -tWM in the IDE (anyone?) so have to do this +#define __MT__ +#endif + +#if defined(__BORLANDC__) && !defined(__MFC_COMPAT__) +// Needed to know about _beginthreadex etc.. +#define __MFC_COMPAT__ +#endif + #include #endif @@ -99,33 +116,43 @@ static bool gs_waitingForThread = FALSE; class wxMutexInternal { public: - HANDLE p_mutex; + wxMutexInternal() + { + m_mutex = ::CreateMutex(NULL, FALSE, NULL); + if ( !m_mutex ) + { + wxLogSysError(_("Can not create mutex")); + } + } + + ~wxMutexInternal() { if ( m_mutex ) CloseHandle(m_mutex); } + +public: + HANDLE m_mutex; }; wxMutex::wxMutex() { m_internal = new wxMutexInternal; - m_internal->p_mutex = CreateMutex(NULL, FALSE, NULL); - if ( !m_internal->p_mutex ) - { - wxLogSysError(_("Can not create mutex.")); - } m_locked = 0; } wxMutex::~wxMutex() { - if (m_locked > 0) - wxLogDebug(wxT("Warning: freeing a locked mutex (%d locks)."), m_locked); - CloseHandle(m_internal->p_mutex); + if ( m_locked > 0 ) + { + wxLogDebug(_T("Warning: freeing a locked mutex (%d locks)."), m_locked); + } + + delete m_internal; } wxMutexError wxMutex::Lock() { DWORD ret; - ret = WaitForSingleObject(m_internal->p_mutex, INFINITE); + ret = WaitForSingleObject(m_internal->m_mutex, INFINITE); switch ( ret ) { case WAIT_ABANDONED: @@ -152,7 +179,7 @@ wxMutexError wxMutex::TryLock() { DWORD ret; - ret = WaitForSingleObject(m_internal->p_mutex, 0); + ret = WaitForSingleObject(m_internal->m_mutex, 0); if (ret == WAIT_TIMEOUT || ret == WAIT_ABANDONED) return wxMUTEX_BUSY; @@ -165,7 +192,7 @@ wxMutexError wxMutex::Unlock() if (m_locked > 0) m_locked--; - BOOL ret = ReleaseMutex(m_internal->p_mutex); + BOOL ret = ReleaseMutex(m_internal->m_mutex); if ( ret == 0 ) { wxLogSysError(_("Couldn't release a mutex")); @@ -184,44 +211,83 @@ class wxConditionInternal public: wxConditionInternal() { - event = ::CreateEvent( - NULL, // default secutiry - FALSE, // not manual reset - FALSE, // nonsignaled initially - NULL // nameless event - ); - if ( !event ) + m_hEvent = ::CreateEvent( + NULL, // default secutiry + FALSE, // not manual reset + FALSE, // nonsignaled initially + NULL // nameless event + ); + if ( !m_hEvent ) { wxLogSysError(_("Can not create event object.")); } - waiters = 0; + + // nobody waits for us yet + m_nWaiters = 0; } bool Wait(DWORD timeout) { - waiters++; + // as m_nWaiters variable is accessed from multiple waiting threads + // (and possibly from the broadcasting thread), we need to change its + // value atomically + ::InterlockedIncrement(&m_nWaiters); - // FIXME this should be MsgWaitForMultipleObjects() as well probably - DWORD rc = ::WaitForSingleObject(event, timeout); + // FIXME this should be MsgWaitForMultipleObjects() as we want to keep + // processing Windows messages while waiting (or don't we?) + DWORD rc = ::WaitForSingleObject(m_hEvent, timeout); - waiters--; + ::InterlockedDecrement(&m_nWaiters); return rc != WAIT_TIMEOUT; } + void Signal() + { + // set the event to signaled: if a thread is already waiting on it, it + // will be woken up, otherwise the event will remain in the signaled + // state until someone waits on it. In any case, the system will return + // it to a non signalled state afterwards. If multiple threads are + // waiting, only one will be woken up. + if ( !::SetEvent(m_hEvent) ) + { + wxLogLastError(wxT("SetEvent")); + } + } + + void Broadcast() + { + // we need to save the original value as m_nWaiters is goign to be + // decreased by the signalled thread resulting in the loop being + // executed less times than needed + LONG nWaiters = m_nWaiters; + + // this works because all these threads are already waiting and so each + // SetEvent() inside Signal() is really a PulseEvent() because the + // event state is immediately returned to non-signaled + for ( LONG n = 0; n < nWaiters; n++ ) + { + Signal(); + } + } + ~wxConditionInternal() { - if ( event ) + if ( m_hEvent ) { - if ( !::CloseHandle(event) ) + if ( !::CloseHandle(m_hEvent) ) { - wxLogLastError("CloseHandle(event)"); + wxLogLastError(wxT("CloseHandle(event)")); } } } - HANDLE event; - int waiters; +private: + // the Win32 synchronization object corresponding to this event + HANDLE m_hEvent; + + // number of threads waiting for this condition + LONG m_nWaiters; }; wxCondition::wxCondition() @@ -247,26 +313,12 @@ bool wxCondition::Wait(unsigned long sec, void wxCondition::Signal() { - // set the event to signaled: if a thread is already waiting on it, it will - // be woken up, otherwise the event will remain in the signaled state until - // someone waits on it. In any case, the system will return it to a non - // signalled state afterwards. If multiple threads are waiting, only one - // will be woken up. - if ( !::SetEvent(m_internal->event) ) - { - wxLogLastError("SetEvent"); - } + m_internal->Signal(); } void wxCondition::Broadcast() { - // this works because all these threads are already waiting and so each - // SetEvent() inside Signal() is really a PulseEvent() because the event - // state is immediately returned to non-signaled - for ( int i = 0; i < m_internal->waiters; i++ ) - { - Signal(); - } + m_internal->Broadcast(); } // ---------------------------------------------------------------------------- @@ -275,8 +327,14 @@ void wxCondition::Broadcast() wxCriticalSection::wxCriticalSection() { - wxASSERT_MSG( sizeof(CRITICAL_SECTION) <= sizeof(m_buffer), +#ifdef __WXDEBUG__ + // Done this way to stop warnings during compilation about statement + // always being false + int csSize = sizeof(CRITICAL_SECTION); + int bSize = sizeof(m_buffer); + wxASSERT_MSG( csSize <= bSize, _T("must increase buffer size in wx/thread.h") ); +#endif ::InitializeCriticalSection((CRITICAL_SECTION *)m_buffer); } @@ -324,7 +382,7 @@ public: { if ( !::CloseHandle(m_hThread) ) { - wxLogLastError("CloseHandle(thread)"); + wxLogLastError(wxT("CloseHandle(thread)")); } m_hThread = 0; @@ -363,31 +421,38 @@ private: DWORD wxThreadInternal::WinThreadStart(wxThread *thread) { - // first of all, check whether we hadn't been cancelled already + DWORD rc; + bool wasCancelled; + + // first of all, check whether we hadn't been cancelled already and don't + // start the user code at all then if ( thread->m_internal->GetState() == STATE_EXITED ) { - return (DWORD)-1; + rc = (DWORD)-1; + wasCancelled = TRUE; } - - // store the thread object in the TLS - if ( !::TlsSetValue(gs_tlsThisThread, thread) ) + else // do run thread { - wxLogSysError(_("Can not start thread: error writing TLS.")); + // store the thread object in the TLS + if ( !::TlsSetValue(gs_tlsThisThread, thread) ) + { + wxLogSysError(_("Can not start thread: error writing TLS.")); - return (DWORD)-1; - } + return (DWORD)-1; + } - DWORD rc = (DWORD)thread->Entry(); + rc = (DWORD)thread->Entry(); - // enter m_critsect before changing the thread state - thread->m_critsect.Enter(); - bool wasCancelled = thread->m_internal->GetState() == STATE_CANCELED; - thread->m_internal->SetState(STATE_EXITED); - thread->m_critsect.Leave(); + // enter m_critsect before changing the thread state + thread->m_critsect.Enter(); + wasCancelled = thread->m_internal->GetState() == STATE_CANCELED; + thread->m_internal->SetState(STATE_EXITED); + thread->m_critsect.Leave(); + } thread->OnExit(); - // if the thread was cancelled (from Delete()), then it the handle is still + // if the thread was cancelled (from Delete()), then its handle is still // needed there if ( thread->IsDetached() && !wasCancelled ) { @@ -432,7 +497,9 @@ bool wxThreadInternal::Create(wxThread *thread) // for compilers which have it, we should use C RTL function for thread // creation instead of Win32 API one because otherwise we will have memory // leaks if the thread uses C RTL (and most threads do) -#ifdef __VISUALC__ +#if defined(__VISUALC__) || \ + (defined(__BORLANDC__) && (__BORLANDC__ >= 0x500)) || \ + (defined(__GNUG__) && defined(__MSVCRT__)) typedef unsigned (__stdcall *RtlThreadStart)(void *); m_hThread = (HANDLE)_beginthreadex(NULL, 0, @@ -440,7 +507,7 @@ bool wxThreadInternal::Create(wxThread *thread) wxThreadInternal::WinThreadStart, thread, CREATE_SUSPENDED, (unsigned int *)&m_tid); -#else // !VC++ +#else // compiler doesn't have _beginthreadex m_hThread = ::CreateThread ( NULL, // default security @@ -451,7 +518,7 @@ bool wxThreadInternal::Create(wxThread *thread) CREATE_SUSPENDED, // flags &m_tid // [out] thread id ); -#endif // VC++/!VC++ +#endif // _beginthreadex/CreateThread if ( m_hThread == NULL ) { @@ -493,7 +560,13 @@ bool wxThreadInternal::Resume() return FALSE; } - m_state = STATE_RUNNING; + // don't change the state from STATE_EXITED because it's special and means + // we are going to terminate without running any user code - if we did it, + // the codei n Delete() wouldn't work + if ( m_state != STATE_EXITED ) + { + m_state = STATE_RUNNING; + } return TRUE; } @@ -521,6 +594,10 @@ bool wxThread::IsMain() return ::GetCurrentThreadId() == gs_idMainThread; } +#ifdef Yield +#undef Yield +#endif + void wxThread::Yield() { // 0 argument to Sleep() is special and means to just give away the rest of @@ -612,13 +689,12 @@ bool wxThread::SetConcurrency(size_t level) if ( hModKernel ) { pfnSetProcessAffinityMask = (SETPROCESSAFFINITYMASK) - ::GetProcAddress(hModKernel, _T("SetProcessAffinityMask")); + ::GetProcAddress(hModKernel, "SetProcessAffinityMask"); } // we've discovered a MT version of Win9x! wxASSERT_MSG( pfnSetProcessAffinityMask, - _T("this system has several CPUs but no " - "SetProcessAffinityMask function?") ); + _T("this system has several CPUs but no SetProcessAffinityMask function?") ); } if ( !pfnSetProcessAffinityMask ) @@ -721,29 +797,44 @@ wxThreadError wxThread::Delete(ExitCode *pRc) // Delete() is always safe to call, so consider all possible states - // has the thread started to run? - bool shouldResume = FALSE; + // 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(m_critsect); if ( m_internal->GetState() == STATE_NEW ) { - // WinThreadStart() will see it and terminate immediately + // 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); - shouldResume = TRUE; + Resume(); // it knows about STATE_EXITED special case + + shouldCancel = FALSE; + isRunning = TRUE; + + // shouldResume is correctly set to FALSE here + } + else + { + shouldResume = IsPaused(); } } - // is the thread paused? - if ( shouldResume || IsPaused() ) + // resume the thread if it is paused + if ( shouldResume ) Resume(); HANDLE hThread = m_internal->GetHandle(); // does is still run? - if ( IsRunning() ) + if ( isRunning || IsRunning() ) { if ( IsMain() ) { @@ -756,6 +847,7 @@ wxThreadError wxThread::Delete(ExitCode *pRc) } // ask the thread to terminate + if ( shouldCancel ) { wxCriticalSectionLocker lock(m_critsect); @@ -839,7 +931,7 @@ wxThreadError wxThread::Delete(ExitCode *pRc) if ( !::GetExitCodeThread(hThread, (LPDWORD)&rc) ) { - wxLogLastError("GetExitCodeThread"); + wxLogLastError(wxT("GetExitCodeThread")); rc = (ExitCode)-1; } @@ -848,7 +940,7 @@ wxThreadError wxThread::Delete(ExitCode *pRc) { // 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 therad handle was + // MsgWaitForMultipleObject() would fail if the thread handle was // closed while we were waiting on it, so we must do it here delete this; } @@ -893,7 +985,9 @@ void wxThread::Exit(ExitCode status) delete this; } -#ifdef __VISUALC__ +#if defined(__VISUALC__) || \ + (defined(__BORLANDC__) && (__BORLANDC__ >= 0x500)) || \ + (defined(__GNUG__) && defined(__MSVCRT__)) _endthreadex((unsigned)status); #else // !VC++ ::ExitThread((DWORD)status); @@ -980,9 +1074,7 @@ bool wxThreadModule::OnInit() // in normal circumstances it will only happen if all other // TLS_MINIMUM_AVAILABLE (>= 64) indices are already taken - in other // words, this should never happen - wxLogSysError(_("Thread module initialization failed: " - "impossible to allocate index in thread " - "local storage")); + wxLogSysError(_("Thread module initialization failed: impossible to allocate index in thread local storage")); return FALSE; } @@ -994,8 +1086,7 @@ bool wxThreadModule::OnInit() ::TlsFree(gs_tlsThisThread); gs_tlsThisThread = 0xFFFFFFFF; - wxLogSysError(_("Thread module initialization failed: " - "can not store value in thread local storage")); + wxLogSysError(_("Thread module initialization failed: can not store value in thread local storage")); return FALSE; } @@ -1015,7 +1106,7 @@ void wxThreadModule::OnExit() { if ( !::TlsFree(gs_tlsThisThread) ) { - wxLogLastError("TlsFree failed."); + wxLogLastError(wxT("TlsFree failed.")); } if ( gs_critsectGui ) @@ -1066,7 +1157,7 @@ void WXDLLEXPORT wxMutexGuiLeave() } else { - // decrement the number of waiters now + // decrement the number of threads waiting for GUI access now wxASSERT_MSG( gs_nWaitingForGui > 0, wxT("calling wxMutexGuiLeave() without entering it first?") ); @@ -1120,7 +1211,7 @@ void WXDLLEXPORT wxWakeUpMainThread() if ( !::PostThreadMessage(gs_idMainThread, WM_NULL, 0, 0) ) { // should never happen - wxLogLastError("PostThreadMessage(WM_NULL)"); + wxLogLastError(wxT("PostThreadMessage(WM_NULL)")); } }