From 9e84b84742248ef54a561b4937bbc039332c2c51 Mon Sep 17 00:00:00 2001 From: Vadim Zeitlin Date: Thu, 6 Jun 2002 18:31:59 +0000 Subject: [PATCH] wxMutex changes (explicitly specify the type), return values for all wxMutex/wxSemaphore/wxCondition methods and general cleanup git-svn-id: https://svn.wxwidgets.org/svn/wx/wxWidgets/trunk@15761 c3d73ce0-8a6f-49c7-b76d-6d57e0e08775 --- include/wx/thread.h | 273 ++++++++------- include/wx/thrimpl.cpp | 175 ++++++++++ src/msw/thread.cpp | 404 +++++++++------------- src/unix/threadpsx.cpp | 758 ++++++++++++++++------------------------- 4 files changed, 791 insertions(+), 819 deletions(-) create mode 100644 include/wx/thrimpl.cpp diff --git a/include/wx/thread.h b/include/wx/thread.h index 319829ba71..9917e594df 100644 --- a/include/wx/thread.h +++ b/include/wx/thread.h @@ -1,5 +1,5 @@ ///////////////////////////////////////////////////////////////////////////// -// Name: thread.h +// Name: wx/thread.h // Purpose: Thread API // Author: Guilhem Lavaux // Modified by: Vadim Zeitlin (modifications partly inspired by omnithreads @@ -10,15 +10,15 @@ // Licence: wxWindows licence ///////////////////////////////////////////////////////////////////////////// -#ifndef __THREADH__ -#define __THREADH__ +#ifndef _WX_THREAD_H_ +#define _WX_THREAD_H_ // ---------------------------------------------------------------------------- // headers // ---------------------------------------------------------------------------- // get the value of wxUSE_THREADS configuration flag -#include "wx/setup.h" +#include "wx/defs.h" #if wxUSE_THREADS @@ -40,11 +40,30 @@ enum wxMutexError { - 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 + wxMUTEX_NO_ERROR = 0, // operation completed successfully + wxMUTEX_INVALID, // mutex hasn't been initialized + wxMUTEX_DEAD_LOCK, // mutex is already locked by the calling thread + wxMUTEX_BUSY, // mutex is already locked by another thread + wxMUTEX_UNLOCKED, // attempt to unlock a mutex which is not locked + wxMUTEX_MISC_ERROR // any other error +}; + +enum wxCondError +{ + wxCOND_NO_ERROR = 0, + wxCOND_INVALID, + wxCOND_TIMEOUT, // WaitTimeout() has timed out + wxCOND_MISC_ERROR +}; + +enum wxSemaError +{ + wxSEMA_NO_ERROR = 0, + wxSEMA_INVALID, // semaphore hasn't been initialized successfully + wxSEMA_BUSY, // returned by TryWait() if Wait() would block + wxSEMA_TIMEOUT, // returned by WaitTimeout() + wxSEMA_OVERFLOW, // Post() would increase counter past the max + wxSEMA_MISC_ERROR }; enum wxThreadError @@ -71,6 +90,37 @@ enum WXTHREAD_MAX_PRIORITY = 100u }; +// There are 2 types of mutexes: normal mutexes and recursive ones. The attempt +// to lock a normal mutex by a thread which already owns it results in +// undefined behaviour (it always works under Windows, it will almost always +// result in a deadlock under Unix). Locking a recursive mutex in such +// situation always succeeds and it must be unlocked as many times as it has +// been locked. +// +// However recursive mutexes have several important drawbacks: first, in the +// POSIX implementation, they're less efficient. Second, and more importantly, +// they CAN NOT BE USED WITH CONDITION VARIABLES under Unix! Using them with +// wxCondition will work under Windows and some Unices (notably Linux) but will +// deadlock under other Unix versions (e.g. Solaris). As it might be difficult +// to ensure that a recursive mutex is not used with wxCondition, it is a good +// idea to avoid using recursive mutexes at all. Also, the last problem with +// them is that some (older) Unix versions don't support this at all -- which +// results in a configure warning when building and a deadlock when using them. +enum wxMutexType +{ + // normal mutex: try to always use this one + wxMUTEX_DEFAULT, + + // recursive mutex: don't use these ones with wxCondition + wxMUTEX_RECURSIVE +}; + +// forward declarations +class WXDLLEXPORT wxConditionInternal; +class WXDLLEXPORT wxMutexInternal; +class WXDLLEXPORT wxSemaphoreInternal; +class WXDLLEXPORT wxThreadInternal; + // ---------------------------------------------------------------------------- // A mutex object is a synchronization object whose state is set to signaled // when it is not owned by any thread, and nonsignaled when it is owned. Its @@ -80,34 +130,44 @@ enum // you should consider wxMutexLocker whenever possible instead of directly // working with wxMutex class - it is safer -class WXDLLEXPORT wxConditionInternal; -class WXDLLEXPORT wxMutexInternal; class WXDLLEXPORT wxMutex { public: // constructor & destructor - wxMutex(); + // ------------------------ + + // create either default (always safe) or recursive mutex + wxMutex(wxMutexType mutexType = wxMUTEX_DEFAULT); + + // destroys the mutex kernel object ~wxMutex(); - // Lock the mutex. + // test if the mutex has been created successfully + bool IsOk() const; + + // mutex operations + // ---------------- + + // Lock the mutex, blocking on it until it is unlocked by the other thread. + // The result of locking a mutex already locked by the current thread + // depend on the mutex type. + // + // The caller must call Unlock() later if Lock() returned wxMUTEX_NO_ERROR. wxMutexError Lock(); - // Try to lock the mutex: if it can't, returns immediately with an error. + + // Try to lock the mutex: if it is currently locked, return immediately + // with an error. Otherwise the caller must call Unlock(). wxMutexError TryLock(); - // Unlock the mutex. - wxMutexError Unlock(); - // Returns true if the mutex is locked. - bool IsLocked() const { return (m_locked > 0); } + // Unlock the mutex. It is an error to unlock an already unlocked mutex + wxMutexError Unlock(); protected: - // no assignment operator nor copy ctor - wxMutex(const wxMutex&); - wxMutex& operator=(const wxMutex&); - - int m_locked; wxMutexInternal *m_internal; friend class wxConditionInternal; + + DECLARE_NO_COPY_CLASS(wxMutex) }; // a helper class which locks the mutex in the ctor and unlocks it in the dtor: @@ -148,17 +208,11 @@ private: // which makes it possible to have static globals of this class // ---------------------------------------------------------------------------- -class WXDLLEXPORT wxCriticalSectionInternal; - // in order to avoid any overhead under platforms where critical sections are // just mutexes make all wxCriticalSection class functions inline #if !defined(__WXMSW__) && !defined(__WXPM__) - #define WXCRITICAL_INLINE inline - #define wxCRITSECT_IS_MUTEX 1 #else // MSW || OS2 - #define WXCRITICAL_INLINE - #define wxCRITSECT_IS_MUTEX 0 #endif // MSW/!MSW @@ -168,50 +222,62 @@ class WXDLLEXPORT wxCriticalSection { public: // ctor & dtor - WXCRITICAL_INLINE wxCriticalSection(); - WXCRITICAL_INLINE ~wxCriticalSection(); + inline wxCriticalSection(); + inline ~wxCriticalSection(); // enter the section (the same as locking a mutex) - WXCRITICAL_INLINE void Enter(); + inline void Enter(); + // leave the critical section (same as unlocking a mutex) - WXCRITICAL_INLINE void Leave(); + inline void Leave(); private: - // no assignment operator nor copy ctor - wxCriticalSection(const wxCriticalSection&); - wxCriticalSection& operator=(const wxCriticalSection&); - #if wxCRITSECT_IS_MUTEX wxMutex m_mutex; #elif defined(__WXMSW__) // we can't allocate any memory in the ctor, so use placement new - // unfortunately, we have to hardcode the sizeof() here because we can't // include windows.h from this public header + // + // if CRITICAL_SECTION size changes in Windows, you'll get an assert from + // thread.cpp and will need to increase the buffer size char m_buffer[24]; -#elif !defined(__WXPM__) - wxCriticalSectionInternal *m_critsect; #else // nothing for OS/2 -#endif // !Unix/Unix +#endif // Unix/Win32/OS2 + + DECLARE_NO_COPY_CLASS(wxCriticalSection) }; -// keep your preprocessor name space clean -#undef WXCRITICAL_INLINE +#if wxCRITSECT_IS_MUTEX + // implement wxCriticalSection using mutexes + inline wxCriticalSection::wxCriticalSection() { } + inline wxCriticalSection::~wxCriticalSection() { } + + inline void wxCriticalSection::Enter() { (void)m_mutex.Lock(); } + inline void wxCriticalSection::Leave() { (void)m_mutex.Unlock(); } +#endif // wxCRITSECT_IS_MUTEX // wxCriticalSectionLocker is the same to critical sections as wxMutexLocker is // to th mutexes class WXDLLEXPORT wxCriticalSectionLocker { public: - inline wxCriticalSectionLocker(wxCriticalSection& critsect); - inline ~wxCriticalSectionLocker(); + wxCriticalSectionLocker(wxCriticalSection& cs) + : m_critsect(cs) + { + m_critsect.Enter(); + } -private: - // no assignment operator nor copy ctor - wxCriticalSectionLocker(const wxCriticalSectionLocker&); - wxCriticalSectionLocker& operator=(const wxCriticalSectionLocker&); + ~wxCriticalSectionLocker() + { + m_critsect.Leave(); + } +private: wxCriticalSection& m_critsect; + + DECLARE_NO_COPY_CLASS(wxCriticalSectionLocker) }; // ---------------------------------------------------------------------------- @@ -221,25 +287,24 @@ private: class WXDLLEXPORT wxCondition { - DECLARE_NO_COPY_CLASS(wxCondition) - public: - // constructor and destructor - - // Each wxCondition object is associated with with a wxMutex object The - // mutex object MUST be locked before calling Wait() + // Each wxCondition object is associated with a (single) wxMutex object. + // The mutex object MUST be locked before calling Wait() wxCondition(wxMutex& mutex); // dtor is not virtual, don't use this class polymorphically ~wxCondition(); + // return TRUE if the condition has been created successfully + bool IsOk() const; + // NB: the associated mutex MUST be locked beforehand by the calling thread // // it atomically releases the lock on the associated mutex // and starts waiting to be woken up by a Signal()/Broadcast() // once its signaled, then it will wait until it can reacquire // the lock on the associated mutex object, before returning. - void Wait(); + wxCondError Wait(); // exactly as Wait() except that it may also return if the specified // timeout ellapses even if the condition hasn't been signalled: in this @@ -248,7 +313,7 @@ public: // // the timeeout parameter specifies a interval that needs to be waited in // milliseconds - bool Wait( unsigned long timeout_millis ); + wxCondError WaitTimeout(unsigned long milliseconds); // NB: the associated mutex may or may not be locked by the calling thread // @@ -256,7 +321,7 @@ public: // if no thread is blocking in Wait(), then the signal is NOT remembered // The thread which was blocking on Wait(), will then reacquire the lock // on the associated mutex object before returning - void Signal(); + wxCondError Signal(); // NB: the associated mutex may or may not be locked by the calling thread // @@ -264,10 +329,17 @@ public: // if no thread is blocking in Wait(), then the signal is NOT remembered // The threads which were blocking on Wait(), will then reacquire the lock // on the associated mutex object before returning. - void Broadcast(); + wxCondError Broadcast(); + + + // deprecated version, don't use + bool Wait(unsigned long milliseconds) + { return WaitTimeout(milliseconds) == wxCOND_NO_ERROR; } private: wxConditionInternal *m_internal; + + DECLARE_NO_COPY_CLASS(wxCondition) }; // ---------------------------------------------------------------------------- @@ -275,11 +347,8 @@ private: // a shared resource // ---------------------------------------------------------------------------- -class WXDLLEXPORT wxSemaphoreInternal; class WXDLLEXPORT wxSemaphore { - DECLARE_NO_COPY_CLASS(wxSemaphore) - public: // specifying a maxcount of 0 actually makes wxSemaphore behave as if there // is no upper limit, if maxcount is 1 the semaphore behaves as a mutex @@ -288,24 +357,29 @@ public: // dtor is not virtual, don't use this class polymorphically ~wxSemaphore(); + // return TRUE if the semaphore has been created successfully + bool IsOk() const; + // wait indefinitely, until the semaphore count goes beyond 0 // and then decrement it and return (this method might have been called // Acquire()) - void Wait(); + wxSemaError Wait(); - // same as Wait(), but does not block, returns TRUE if successful and - // FALSE if the count is zero - bool TryWait(); + // same as Wait(), but does not block, returns wxSEMA_NO_ERROR if + // successful and wxSEMA_BUSY if the count is currently zero + wxSemaError TryWait(); - // same as Wait(), but as a timeout limit, returns TRUE if the semaphore - // was acquired and FALSE if the timeout has ellapsed - bool Wait( unsigned long timeout_millis ); + // same as Wait(), but as a timeout limit, returns wxSEMA_NO_ERROR if the + // semaphore was acquired and wxSEMA_TIMEOUT if the timeout has ellapsed + wxSemaError WaitTimeout(unsigned long milliseconds); // increments the semaphore count and signals one of the waiting threads - void Post(); + wxSemaError Post(); private: wxSemaphoreInternal *m_internal; + + DECLARE_NO_COPY_CLASS(wxSemaphore) }; // ---------------------------------------------------------------------------- @@ -328,7 +402,6 @@ private: typedef unsigned long wxThreadIdType; #endif -class wxThreadInternal; class WXDLLEXPORT wxThread { public: @@ -508,8 +581,6 @@ void WXDLLEXPORT wxMutexGuiLeave(); #else // !wxUSE_THREADS -#include "wx/defs.h" // for WXDLLEXPORT - // no thread support inline void WXDLLEXPORT wxMutexGuiEnter() { } inline void WXDLLEXPORT wxMutexGuiLeave() { } @@ -521,9 +592,9 @@ inline void WXDLLEXPORT wxMutexGuiLeave() { } #define wxCRIT_SECT_DECLARE(cs) #define wxCRIT_SECT_LOCKER(name, cs) -#endif // wxUSE_THREADS +#endif // wxUSE_THREADS/!wxUSE_THREADS -// automatically unlock GUI mutex in dtor +// automatically lock GUI mutex in ctor and unlock it in dtor class WXDLLEXPORT wxMutexGuiLocker { public: @@ -537,7 +608,7 @@ public: #if wxUSE_THREADS -#if defined(__WXMSW__) +#if defined(__WXMSW__) || defined(__WXMAC__) || defined(__WXPM__) // 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 @@ -546,61 +617,17 @@ public: // returns TRUE if the main thread has GUI lock extern bool WXDLLEXPORT wxGuiOwnedByMainThread(); +#ifndef __WXPM__ // wakes up the main thread if it's sleeping inside ::GetMessage() extern void WXDLLEXPORT wxWakeUpMainThread(); +#endif // !OS/2 // return TRUE if the main thread is waiting for some other to terminate: // wxApp then should block all "dangerous" messages extern bool WXDLLEXPORT wxIsWaitingForThread(); -#elif defined(__WXMAC__) - extern void WXDLLEXPORT wxMutexGuiLeaveOrEnter(); - - // returns TRUE if the main thread has GUI lock - extern bool WXDLLEXPORT wxGuiOwnedByMainThread(); - - // wakes up the main thread if it's sleeping inside ::GetMessage() - extern void WXDLLEXPORT wxWakeUpMainThread(); - - // return TRUE if the main thread is waiting for some other to terminate: - // wxApp then should block all "dangerous" messages - extern bool WXDLLEXPORT wxIsWaitingForThread(); - - // implement wxCriticalSection using mutexes - inline wxCriticalSection::wxCriticalSection() : m_mutex() { } - inline wxCriticalSection::~wxCriticalSection() { } - - inline void wxCriticalSection::Enter() { (void)m_mutex.Lock(); } - inline void wxCriticalSection::Leave() { (void)m_mutex.Unlock(); } -#elif defined(__WXPM__) - // 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 - extern void WXDLLEXPORT wxMutexGuiLeaveOrEnter(); - - // returns TRUE if the main thread has GUI lock - extern bool WXDLLEXPORT wxGuiOwnedByMainThread(); - - // return TRUE if the main thread is waiting for some other to terminate: - // wxApp then should block all "dangerous" messages - extern bool WXDLLEXPORT wxIsWaitingForThread(); - -#else // !MSW && !PM - // implement wxCriticalSection using mutexes - inline wxCriticalSection::wxCriticalSection() { } - inline wxCriticalSection::~wxCriticalSection() { } - - inline void wxCriticalSection::Enter() { (void)m_mutex.Lock(); } - inline void wxCriticalSection::Leave() { (void)m_mutex.Unlock(); } -#endif // MSW/!MSW +#endif // MSW, Mac, OS/2 - // we can define these inline functions now (they should be defined after - // wxCriticalSection::Enter/Leave) - inline - wxCriticalSectionLocker:: wxCriticalSectionLocker(wxCriticalSection& cs) - : m_critsect(cs) { m_critsect.Enter(); } - inline - wxCriticalSectionLocker::~wxCriticalSectionLocker() { m_critsect.Leave(); } #endif // wxUSE_THREADS -#endif // __THREADH__ +#endif // _WX_THREAD_H_ diff --git a/include/wx/thrimpl.cpp b/include/wx/thrimpl.cpp new file mode 100644 index 0000000000..b01436a220 --- /dev/null +++ b/include/wx/thrimpl.cpp @@ -0,0 +1,175 @@ +///////////////////////////////////////////////////////////////////////////// +// Name: include/wx/thrimpl.cpp +// Purpose: common part of wxThread Implementations +// Author: Vadim Zeitlin +// Modified by: +// Created: 04.06.02 (extracted from src/*/thread.cpp files) +// RCS-ID: $Id$ +// Copyright: (c) Vadim Zeitlin (2002) +// Licence: wxWindows licence +///////////////////////////////////////////////////////////////////////////// + +// this file is supposed to be included only by the various thread.cpp + +// ---------------------------------------------------------------------------- +// wxMutex +// ---------------------------------------------------------------------------- + +wxMutex::wxMutex(wxMutexType mutexType) +{ + m_internal = new wxMutexInternal(mutexType); + + if ( !m_internal->IsOk() ) + { + delete m_internal; + m_internal = NULL; + } +} + +wxMutex::~wxMutex() +{ + delete m_internal; +} + +bool wxMutex::IsOk() const +{ + return m_internal != NULL; +} + +wxMutexError wxMutex::Lock() +{ + wxCHECK_MSG( m_internal, wxMUTEX_INVALID, + _T("wxMutex::Lock(): not initialized") ); + + return m_internal->Lock(); +} + +wxMutexError wxMutex::TryLock() +{ + wxCHECK_MSG( m_internal, wxMUTEX_INVALID, + _T("wxMutex::TryLock(): not initialized") ); + + return m_internal->TryLock(); +} + +wxMutexError wxMutex::Unlock() +{ + wxCHECK_MSG( m_internal, wxMUTEX_INVALID, + _T("wxMutex::Unlock(): not initialized") ); + + return m_internal->Unlock(); +} + +// ---------------------------------------------------------------------------- +// wxCondition +// ---------------------------------------------------------------------------- + +wxCondition::wxCondition(wxMutex& mutex) +{ + m_internal = new wxConditionInternal(mutex); + + if ( !m_internal->IsOk() ) + { + delete m_internal; + m_internal = NULL; + } +} + +wxCondition::~wxCondition() +{ + delete m_internal; +} + +bool wxCondition::IsOk() const +{ + return m_internal != NULL; +} + +wxCondError wxCondition::Wait() +{ + wxCHECK_MSG( m_internal, wxCOND_INVALID, + _T("wxCondition::Wait(): not initialized") ); + + return m_internal->Wait(); +} + +wxCondError wxCondition::WaitTimeout(unsigned long milliseconds) +{ + wxCHECK_MSG( m_internal, wxCOND_INVALID, + _T("wxCondition::Wait(): not initialized") ); + + return m_internal->WaitTimeout(milliseconds); +} + +wxCondError wxCondition::Signal() +{ + wxCHECK_MSG( m_internal, wxCOND_INVALID, + _T("wxCondition::Signal(): not initialized") ); + + return m_internal->Signal(); +} + +wxCondError wxCondition::Broadcast() +{ + wxCHECK_MSG( m_internal, wxCOND_INVALID, + _T("wxCondition::Broadcast(): not initialized") ); + + return m_internal->Broadcast(); +} + +// -------------------------------------------------------------------------- +// wxSemaphore +// -------------------------------------------------------------------------- + +wxSemaphore::wxSemaphore(int initialcount, int maxcount) +{ + m_internal = new wxSemaphoreInternal( initialcount, maxcount ); + if ( !m_internal->IsOk() ) + { + delete m_internal; + m_internal = NULL; + } +} + +wxSemaphore::~wxSemaphore() +{ + delete m_internal; +} + +bool wxSemaphore::IsOk() const +{ + return m_internal != NULL; +} + +wxSemaError wxSemaphore::Wait() +{ + wxCHECK_MSG( m_internal, wxSEMA_INVALID, + _T("wxSemaphore::Wait(): not initialized") ); + + return m_internal->Wait(); +} + +wxSemaError wxSemaphore::TryWait() +{ + wxCHECK_MSG( m_internal, wxSEMA_INVALID, + _T("wxSemaphore::TryWait(): not initialized") ); + + return m_internal->TryWait(); +} + +wxSemaError wxSemaphore::WaitTimeout(unsigned long milliseconds) +{ + wxCHECK_MSG( m_internal, wxSEMA_INVALID, + _T("wxSemaphore::WaitTimeout(): not initialized") ); + + return m_internal->WaitTimeout(milliseconds); +} + +wxSemaError wxSemaphore::Post() +{ + wxCHECK_MSG( m_internal, wxSEMA_INVALID, + _T("wxSemaphore::Post(): not initialized") ); + + return m_internal->Post(); +} + diff --git a/src/msw/thread.cpp b/src/msw/thread.cpp index f7e9f0517b..b3bc923c68 100644 --- a/src/msw/thread.cpp +++ b/src/msw/thread.cpp @@ -1,12 +1,12 @@ ///////////////////////////////////////////////////////////////////////////// -// Name: thread.cpp +// Name: src/msw/thread.cpp // Purpose: wxThread Implementation // Author: Original from Wolfram Gloger/Guilhem Lavaux // Modified by: Vadim Zeitlin to make it work :-) // Created: 04/22/98 // RCS-ID: $Id$ -// Copyright: (c) Wolfram Gloger (1996, 1997); Guilhem Lavaux (1998), -// Vadim Zeitlin (1999) +// Copyright: (c) Wolfram Gloger (1996, 1997), Guilhem Lavaux (1998); +// Vadim Zeitlin (1999-2002) // Licence: wxWindows licence ///////////////////////////////////////////////////////////////////////////// @@ -36,10 +36,6 @@ #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 @@ -127,128 +123,157 @@ static size_t gs_nWaitingForGui = 0; static bool gs_waitingForThread = FALSE; // ============================================================================ -// Windows implementation of thread classes +// Windows implementation of thread and related classes // ============================================================================ // ---------------------------------------------------------------------------- -// wxMutex implementation +// wxCriticalSection +// ---------------------------------------------------------------------------- + +wxCriticalSection::wxCriticalSection() +{ + wxCOMPILE_TIME_ASSERT( sizeof(CRITICAL_SECTION) <= sizeof(m_buffer), + wxCriticalSectionBufferTooSmall ); + + ::InitializeCriticalSection((CRITICAL_SECTION *)m_buffer); +} + +wxCriticalSection::~wxCriticalSection() +{ + ::DeleteCriticalSection((CRITICAL_SECTION *)m_buffer); +} + +void wxCriticalSection::Enter() +{ + ::EnterCriticalSection((CRITICAL_SECTION *)m_buffer); +} + +void wxCriticalSection::Leave() +{ + ::LeaveCriticalSection((CRITICAL_SECTION *)m_buffer); +} + +// ---------------------------------------------------------------------------- +// wxMutex // ---------------------------------------------------------------------------- class wxMutexInternal { public: - wxMutexInternal() - { - m_mutex = ::CreateMutex(NULL, FALSE, NULL); - if ( !m_mutex ) - { - wxLogSysError(_("Can not create mutex")); - } - } + wxMutexInternal(wxMutexType mutexType); + ~wxMutexInternal(); - ~wxMutexInternal() { if ( m_mutex ) ::CloseHandle(m_mutex); } + bool IsOk() const { return m_mutex != NULL; } + + wxMutexError Lock() { return LockTimeout(INFINITE); } + wxMutexError TryLock() { return LockTimeout(0); } + wxMutexError Unlock(); + +private: + wxMutexError LockTimeout(DWORD milliseconds); -public: HANDLE m_mutex; }; -wxMutex::wxMutex() +// all mutexes are recursive under Win32 so we don't use mutexType +wxMutexInternal::wxMutexInternal(wxMutexType WXUNUSED(mutexType)) { - m_internal = new wxMutexInternal; + // create a nameless (hence intra process and always private) mutex + m_mutex = ::CreateMutex + ( + NULL, // default secutiry attributes + FALSE, // not initially locked + NULL // no name + ); - m_locked = 0; + if ( !m_mutex ) + { + wxLogLastError(_T("CreateMutex()")); + } } -wxMutex::~wxMutex() +wxMutexInternal::~wxMutexInternal() { - if ( m_locked > 0 ) + if ( m_mutex ) { - wxLogDebug(_T("Warning: freeing a locked mutex (%d locks)."), m_locked); + if ( !::CloseHandle(m_mutex) ) + { + wxLogLastError(_T("CloseHandle(mutex)")); + } } - - delete m_internal; } -wxMutexError wxMutex::Lock() +wxMutexError wxMutexInternal::LockTimeout(DWORD milliseconds) { - DWORD ret; - - ret = WaitForSingleObject(m_internal->m_mutex, INFINITE); - switch ( ret ) + DWORD rc = ::WaitForSingleObject(m_mutex, milliseconds); + if ( rc == WAIT_ABANDONED ) { - case WAIT_ABANDONED: - return wxMUTEX_BUSY; + // the previous caller died without releasing the mutex, but now we can + // really lock it + wxLogDebug(_T("WaitForSingleObject() returned WAIT_ABANDONED")); + + // use 0 timeout, normally we should always get it + rc = ::WaitForSingleObject(m_mutex, 0); + } + switch ( rc ) + { case WAIT_OBJECT_0: // ok break; - case WAIT_FAILED: - wxLogSysError(_("Couldn't acquire a mutex lock")); - return wxMUTEX_MISC_ERROR; - case WAIT_TIMEOUT: + return wxMUTEX_BUSY; + + case WAIT_ABANDONED: // checked for above default: wxFAIL_MSG(wxT("impossible return value in wxMutex::Lock")); - } + // fall through - m_locked++; - return wxMUTEX_NO_ERROR; -} - -wxMutexError wxMutex::TryLock() -{ - DWORD ret; - - ret = WaitForSingleObject(m_internal->m_mutex, 0); - if (ret == WAIT_TIMEOUT || ret == WAIT_ABANDONED) - return wxMUTEX_BUSY; + case WAIT_FAILED: + wxLogLastError(_T("WaitForSingleObject(mutex)")); + return wxMUTEX_MISC_ERROR; + } - m_locked++; return wxMUTEX_NO_ERROR; } -wxMutexError wxMutex::Unlock() +wxMutexError wxMutexInternal::Unlock() { - if (m_locked > 0) - m_locked--; - - BOOL ret = ReleaseMutex(m_internal->m_mutex); - if ( ret == 0 ) + if ( !::ReleaseMutex(m_mutex) ) { - wxLogSysError(_("Couldn't release a mutex")); + wxLogLastError(_("ReleaseMutex()")); + return wxMUTEX_MISC_ERROR; } return wxMUTEX_NO_ERROR; } -// ========================================================================== -// wxSemaphore -// ========================================================================== - // -------------------------------------------------------------------------- -// wxSemaphoreInternal +// wxSemaphore // -------------------------------------------------------------------------- +// a trivial wrapper around Win32 semaphore class wxSemaphoreInternal { public: - wxSemaphoreInternal( int initialcount = 0, int maxcount = 0 ); + wxSemaphoreInternal(int initialcount, int maxcount); ~wxSemaphoreInternal(); - void Wait(); - bool TryWait(); + bool IsOk() const { return m_semaphore != NULL; } - bool Wait( unsigned long timeout_millis ); + wxSemaError Wait() { return WaitTimeout(INFINITE); } + wxSemaError TryWait() { return WaitTimeout(0); } + wxSemaError WaitTimeout(unsigned long milliseconds); - void Post(); + wxSemaError Post(); private: HANDLE m_semaphore; }; -wxSemaphoreInternal::wxSemaphoreInternal( int initialcount, int maxcount ) +wxSemaphoreInternal::wxSemaphoreInternal(int initialcount, int maxcount) { if ( maxcount == 0 ) { @@ -256,7 +281,14 @@ wxSemaphoreInternal::wxSemaphoreInternal( int initialcount, int maxcount ) maxcount = INT_MAX; } - m_semaphore = ::CreateSemaphore( NULL, initialcount, maxcount, NULL ); + m_semaphore = ::CreateSemaphore + ( + NULL, // default security attributes + initialcount, + maxcount, + NULL // no name + ); + if ( !m_semaphore ) { wxLogLastError(_T("CreateSemaphore()")); @@ -265,129 +297,89 @@ wxSemaphoreInternal::wxSemaphoreInternal( int initialcount, int maxcount ) wxSemaphoreInternal::~wxSemaphoreInternal() { - CloseHandle( m_semaphore ); -} - -void wxSemaphoreInternal::Wait() -{ - if ( ::WaitForSingleObject( m_semaphore, INFINITE ) != WAIT_OBJECT_0 ) + if ( m_semaphore ) { - wxLogLastError(_T("WaitForSingleObject")); + if ( !::CloseHandle(m_semaphore) ) + { + wxLogLastError(_T("CloseHandle(semaphore)")); + } } } -bool wxSemaphoreInternal::TryWait() -{ - return Wait(0); -} - -bool wxSemaphoreInternal::Wait( unsigned long timeout_millis ) +wxSemaError wxSemaphoreInternal::WaitTimeout(unsigned long milliseconds) { - DWORD result = ::WaitForSingleObject( m_semaphore, timeout_millis ); + DWORD rc = ::WaitForSingleObject( m_semaphore, milliseconds ); - switch ( result ) + switch ( rc ) { case WAIT_OBJECT_0: - return TRUE; + return wxSEMA_NO_ERROR; case WAIT_TIMEOUT: - break; + return wxSEMA_BUSY; default: - wxLogLastError(_T("WaitForSingleObject()")); + wxLogLastError(_T("WaitForSingleObject(semaphore)")); } - return FALSE; + return wxSEMA_MISC_ERROR; } -void wxSemaphoreInternal::Post() +wxSemaError wxSemaphoreInternal::Post() { - if ( !::ReleaseSemaphore( m_semaphore, 1, NULL ) ) + if ( !::ReleaseSemaphore(m_semaphore, 1, NULL /* ptr to previous count */) ) { wxLogLastError(_T("ReleaseSemaphore")); - } -} - -// -------------------------------------------------------------------------- -// wxSemaphore -// -------------------------------------------------------------------------- -wxSemaphore::wxSemaphore( int initialcount, int maxcount ) -{ - m_internal = new wxSemaphoreInternal( initialcount, maxcount ); -} - -wxSemaphore::~wxSemaphore() -{ - delete m_internal; -} - -void wxSemaphore::Wait() -{ - m_internal->Wait(); -} - -bool wxSemaphore::TryWait() -{ - return m_internal->TryWait(); -} - -bool wxSemaphore::Wait( unsigned long timeout_millis ) -{ - return m_internal->Wait( timeout_millis ); -} + return wxSEMA_MISC_ERROR; + } -void wxSemaphore::Post() -{ - m_internal->Post(); + return wxSEMA_NO_ERROR; } - -// ========================================================================== -// wxCondition -// ========================================================================== - // -------------------------------------------------------------------------- -// wxConditionInternal +// wxCondition // -------------------------------------------------------------------------- +// Win32 doesn't have explicit support for the POSIX condition variables and +// the Win32 events have quite different semantics, so we reimplement the +// conditions from scratch using the mutexes and semaphores class wxConditionInternal { public: wxConditionInternal(wxMutex& mutex); - void Wait(); + bool IsOk() const { return m_mutex.IsOk() && m_semaphore.IsOk(); } - bool Wait( unsigned long timeout_millis ); + wxCondError Wait(); + wxCondError WaitTimeout(unsigned long milliseconds); - void Signal(); - - void Broadcast(); + wxCondError Signal(); + wxCondError Broadcast(); private: - int m_numWaiters; - wxMutex m_mutexNumWaiters; + // the number of threads currently waiting for this condition + LONG m_numWaiters; - wxMutex& m_mutex; + // the critical section protecting m_numWaiters + wxCriticalSection m_csWaiters; + wxMutex& m_mutex; wxSemaphore m_semaphore; - - DECLARE_NO_COPY_CLASS(wxConditionInternal) }; wxConditionInternal::wxConditionInternal(wxMutex& mutex) : m_mutex(mutex) { - + // another thread can't access it until we return from ctor, so no need to + // protect access to m_numWaiters here m_numWaiters = 0; } -void wxConditionInternal::Wait() +wxCondError wxConditionInternal::Wait() { // increment the number of waiters - m_mutexNumWaiters.Lock(); - m_numWaiters++; - m_mutexNumWaiters.Unlock(); + ::InterlockedIncrement(&m_numWaiters); m_mutex.Unlock(); @@ -402,16 +394,15 @@ void wxConditionInternal::Wait() // can 'remember' signals the race condition will not occur // wait ( if necessary ) and decrement semaphore - m_semaphore.Wait(); - + wxSemaError err = m_semaphore.Wait(); m_mutex.Lock(); + + return err == wxSEMA_NO_ERROR ? wxCOND_NO_ERROR : wxCOND_MISC_ERROR; } -bool wxConditionInternal::Wait( unsigned long timeout_millis ) +wxCondError wxConditionInternal::WaitTimeout(unsigned long milliseconds) { - m_mutexNumWaiters.Lock(); - m_numWaiters++; - m_mutexNumWaiters.Unlock(); + ::InterlockedIncrement(&m_numWaiters); m_mutex.Unlock(); @@ -419,11 +410,9 @@ bool wxConditionInternal::Wait( unsigned long timeout_millis ) // // please see the comments in Wait(), for details - bool success = TRUE; + wxSemaError err = m_semaphore.WaitTimeout(milliseconds); - bool result = m_semaphore.Wait( timeout_millis ); - - if ( !result ) + if ( err == wxSEMA_BUSY ) { // another potential race condition exists here it is caused when a // 'waiting' thread timesout, and returns from WaitForSingleObject, but @@ -437,116 +426,50 @@ bool wxConditionInternal::Wait( unsigned long timeout_millis ) // 'nwaiters_mutex'. this call does not block because of the zero // timeout, but will allow the waiting thread to catch the missed // signals. - m_mutexNumWaiters.Lock(); - result = m_semaphore.Wait( 0 ); + wxCriticalSectionLocker lock(m_csWaiters); + + err = m_semaphore.WaitTimeout(0); - if ( !result ) + if ( err != wxSEMA_NO_ERROR ) { m_numWaiters--; - success = FALSE; } - - m_mutexNumWaiters.Unlock(); } m_mutex.Lock(); - return success; + return err == wxSEMA_NO_ERROR ? wxCOND_NO_ERROR : wxCOND_MISC_ERROR; } -void wxConditionInternal::Signal() +wxCondError wxConditionInternal::Signal() { - m_mutexNumWaiters.Lock(); + wxCriticalSectionLocker lock(m_csWaiters); if ( m_numWaiters > 0 ) { // increment the semaphore by 1 - m_semaphore.Post(); + if ( m_semaphore.Post() != wxSEMA_NO_ERROR ) + return wxCOND_MISC_ERROR; m_numWaiters--; } - m_mutexNumWaiters.Unlock(); + return wxCOND_NO_ERROR; } -void wxConditionInternal::Broadcast() +wxCondError wxConditionInternal::Broadcast() { - m_mutexNumWaiters.Lock(); + wxCriticalSectionLocker lock(m_csWaiters); while ( m_numWaiters > 0 ) { - m_semaphore.Post(); + if ( m_semaphore.Post() != wxSEMA_NO_ERROR ) + return wxCOND_MISC_ERROR; + m_numWaiters--; } - m_mutexNumWaiters.Unlock(); -} - -// ---------------------------------------------------------------------------- -// wxCondition implementation -// ---------------------------------------------------------------------------- - -wxCondition::wxCondition(wxMutex& mutex) -{ - m_internal = new wxConditionInternal( mutex ); -} - -wxCondition::~wxCondition() -{ - delete m_internal; -} - -void wxCondition::Wait() -{ - m_internal->Wait(); -} - -bool wxCondition::Wait( unsigned long timeout_millis ) -{ - return m_internal->Wait(timeout_millis); -} - -void wxCondition::Signal() -{ - m_internal->Signal(); -} - -void wxCondition::Broadcast() -{ - m_internal->Broadcast(); -} - -// ---------------------------------------------------------------------------- -// wxCriticalSection implementation -// ---------------------------------------------------------------------------- - -wxCriticalSection::wxCriticalSection() -{ -#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); -} - -wxCriticalSection::~wxCriticalSection() -{ - ::DeleteCriticalSection((CRITICAL_SECTION *)m_buffer); -} - -void wxCriticalSection::Enter() -{ - ::EnterCriticalSection((CRITICAL_SECTION *)m_buffer); -} - -void wxCriticalSection::Leave() -{ - ::LeaveCriticalSection((CRITICAL_SECTION *)m_buffer); + return wxCOND_NO_ERROR; } // ---------------------------------------------------------------------------- @@ -1429,6 +1352,11 @@ bool WXDLLEXPORT wxIsWaitingForThread() return gs_waitingForThread; } +// ---------------------------------------------------------------------------- +// include common implementation code +// ---------------------------------------------------------------------------- + +#include "wx/thrimpl.cpp" + #endif // wxUSE_THREADS -// vi:sts=4:sw=4:et diff --git a/src/unix/threadpsx.cpp b/src/unix/threadpsx.cpp index 72be919335..36d6ee7d0e 100644 --- a/src/unix/threadpsx.cpp +++ b/src/unix/threadpsx.cpp @@ -71,9 +71,12 @@ enum wxThreadState // the exit value of a thread which has been cancelled static const wxThread::ExitCode EXITCODE_CANCELLED = (wxThread::ExitCode)-1; -// our trace mask +// trace mask for wxThread operations #define TRACE_THREADS _T("thread") +// you can get additional debugging messages for the semaphore operations +#define TRACE_SEMA _T("semaphore") + // ---------------------------------------------------------------------------- // private functions // ---------------------------------------------------------------------------- @@ -82,11 +85,14 @@ static void ScheduleThreadForDeletion(); static void DeleteThread(wxThread *This); // ---------------------------------------------------------------------------- -// types +// private classes // ---------------------------------------------------------------------------- +// an (non owning) array of pointers to threads WX_DEFINE_ARRAY(wxThread *, wxArrayThread); +// an entry for a thread we can wait for + // ----------------------------------------------------------------------------- // global data // ----------------------------------------------------------------------------- @@ -118,6 +124,15 @@ static wxCondition *gs_condAllDeleted = (wxCondition *)NULL; static wxMutex *gs_mutexGui; #endif // wxUSE_GUI +// when we wait for a thread to exit, we're blocking on a condition which the +// thread signals in its SignalExit() method -- but this condition can't be a +// member of the thread itself as a detached thread may delete itself at any +// moment and accessing the condition member of the thread after this would +// result in a disaster +// +// so instead we maintain a global list of the structs below for the threads +// we're interested in waiting on + // ============================================================================ // wxMutex implementation // ============================================================================ @@ -126,51 +141,86 @@ static wxCondition *gs_condAllDeleted = (wxCondition *)NULL; // wxMutexInternal // ---------------------------------------------------------------------------- +// this is a simple wrapper around pthread_mutex_t which provides error +// checking class wxMutexInternal { public: - wxMutexInternal(); + wxMutexInternal(wxMutexType mutexType); ~wxMutexInternal(); wxMutexError Lock(); wxMutexError TryLock(); wxMutexError Unlock(); + bool IsOk() const { return m_isOk; } + private: pthread_mutex_t m_mutex; + bool m_isOk; + // wxConditionInternal uses our m_mutex friend class wxConditionInternal; }; -wxMutexInternal::wxMutexInternal() +wxMutexInternal::wxMutexInternal(wxMutexType mutexType) { - // support recursive locks like Win32, i.e. a thread can lock a mutex which - // it had itself already locked - // - // but initialization of recursive mutexes is non portable , so try - // several methods + int err; + switch ( mutexType ) + { + case wxMUTEX_RECURSIVE: + // support recursive locks like Win32, i.e. a thread can lock a + // mutex which it had itself already locked + // + // unfortunately initialization of recursive mutexes is non + // portable, so try several methods #ifdef HAVE_PTHREAD_MUTEXATTR_T - pthread_mutexattr_t attr; - pthread_mutexattr_init(&attr); - pthread_mutexattr_settype(&attr, PTHREAD_MUTEX_RECURSIVE); + { + pthread_mutexattr_t attr; + pthread_mutexattr_init(&attr); + pthread_mutexattr_settype(&attr, PTHREAD_MUTEX_RECURSIVE); - pthread_mutex_init(&m_mutex, &attr); + err = pthread_mutex_init(&m_mutex, &attr); + } #elif defined(HAVE_PTHREAD_RECURSIVE_MUTEX_INITIALIZER) - // we can use this only as initializer so we have to assign it first to a - // temp var - assigning directly to m_mutex wouldn't even compile - pthread_mutex_t mutex = PTHREAD_RECURSIVE_MUTEX_INITIALIZER_NP; - m_mutex = mutex; + // we can use this only as initializer so we have to assign it + // first to a temp var - assigning directly to m_mutex wouldn't + // even compile + { + pthread_mutex_t mutex = PTHREAD_RECURSIVE_MUTEX_INITIALIZER_NP; + m_mutex = mutex; + } #else // no recursive mutexes - pthread_mutex_init(&m_mutex, NULL); - - // used by TryLock() below - #define NO_RECURSIVE_MUTEXES + err = EINVAL; #endif // HAVE_PTHREAD_MUTEXATTR_T/... + break; + + default: + wxFAIL_MSG( _T("unknown mutex type") ); + // fall through + + case wxMUTEX_DEFAULT: + err = pthread_mutex_init(&m_mutex, NULL); + break; + } + + m_isOk = err == 0; + if ( !m_isOk ) + { + wxLogApiError("pthread_mutex_init()", err); + } } wxMutexInternal::~wxMutexInternal() { - pthread_mutex_destroy(&m_mutex); + if ( m_isOk ) + { + int err = pthread_mutex_destroy(&m_mutex); + if ( err != 0 ) + { + wxLogApiError("pthread_mutex_destroy()", err); + } + } } wxMutexError wxMutexInternal::Lock() @@ -179,20 +229,23 @@ wxMutexError wxMutexInternal::Lock() switch ( err ) { case EDEADLK: - wxLogDebug(wxT("Locking this mutex would lead to deadlock!")); + // only error checking mutexes return this value and so it's an + // unexpected situation -- hence use assert, not wxLogDebug + wxFAIL_MSG( _T("mutex deadlock prevented") ); return wxMUTEX_DEAD_LOCK; - default: - wxFAIL_MSG( _T("unexpected pthread_mutex_lock() return") ); - // fall through - case EINVAL: - wxLogDebug(_T("Failed to lock the mutex.")); - return wxMUTEX_MISC_ERROR; + wxLogDebug(_T("pthread_mutex_lock(): mutex not initialized.")); + break; case 0: return wxMUTEX_NO_ERROR; + + default: + wxLogApiError(_T("pthread_mutex_lock()"), err); } + + return wxMUTEX_MISC_ERROR; } wxMutexError wxMutexInternal::TryLock() @@ -201,19 +254,22 @@ wxMutexError wxMutexInternal::TryLock() switch ( err ) { case EBUSY: + // not an error: mutex is already locked, but we're prepared for + // this return wxMUTEX_BUSY; - default: - wxFAIL_MSG( _T("unexpected pthread_mutex_trylock() return") ); - // fall through - case EINVAL: - wxLogDebug(_T("Failed to try to lock the mutex.")); - return wxMUTEX_MISC_ERROR; + wxLogDebug(_T("pthread_mutex_trylock(): mutex not initialized.")); + break; case 0: return wxMUTEX_NO_ERROR; + + default: + wxLogApiError(_T("pthread_mutex_trylock()"), err); } + + return wxMUTEX_MISC_ERROR; } wxMutexError wxMutexInternal::Unlock() @@ -225,87 +281,18 @@ wxMutexError wxMutexInternal::Unlock() // we don't own the mutex return wxMUTEX_UNLOCKED; - default: - wxFAIL_MSG( _T("unexpected pthread_mutex_unlock() return") ); - // fall through - case EINVAL: - wxLogDebug(_T("Failed to unlock the mutex.")); - return wxMUTEX_MISC_ERROR; + wxLogDebug(_T("pthread_mutex_unlock(): mutex not initialized.")); + break; case 0: return wxMUTEX_NO_ERROR; - } -} - -// ---------------------------------------------------------------------------- -// wxMutex -// ---------------------------------------------------------------------------- - -// TODO: this is completely generic, move it to common code? - -wxMutex::wxMutex() -{ - m_internal = new wxMutexInternal; - - m_locked = 0; -} - -wxMutex::~wxMutex() -{ - if ( m_locked > 0 ) - wxLogDebug(wxT("Freeing a locked mutex (%d locks)"), m_locked); - - delete m_internal; -} - -wxMutexError wxMutex::Lock() -{ - wxMutexError err = m_internal->Lock(); - - if ( !err ) - { - m_locked++; - } - - return err; -} - -wxMutexError wxMutex::TryLock() -{ - if ( m_locked ) - { -#ifdef NO_RECURSIVE_MUTEXES - return wxMUTEX_DEAD_LOCK; -#else // have recursive mutexes on this platform - // we will succeed in locking it when we have it already locked - return wxMUTEX_NO_ERROR; -#endif // recursive/non-recursive mutexes - } - - wxMutexError err = m_internal->TryLock(); - if ( !err ) - { - m_locked++; - } - return err; -} - -wxMutexError wxMutex::Unlock() -{ - if ( m_locked > 0 ) - { - m_locked--; - } - else - { - wxLogDebug(wxT("Unlocking not locked mutex.")); - - return wxMUTEX_UNLOCKED; + default: + wxLogApiError(_T("pthread_mutex_unlock()"), err); } - return m_internal->Unlock(); + return wxMUTEX_MISC_ERROR; } // =========================================================================== @@ -316,112 +303,77 @@ wxMutexError wxMutex::Unlock() // wxConditionInternal // --------------------------------------------------------------------------- +// this is a wrapper around pthread_cond_t associated with a wxMutex (and hence +// with a pthread_mutex_t) class wxConditionInternal { public: wxConditionInternal(wxMutex& mutex); ~wxConditionInternal(); - void Wait(); - - bool Wait( const timespec *ts ); + bool IsOk() const { return m_isOk && m_mutex.IsOk(); } - void Signal(); + wxCondError Wait(); + wxCondError WaitTimeout(unsigned long milliseconds); - void Broadcast(); + wxCondError Signal(); + wxCondError Broadcast(); private: // get the POSIX mutex associated with us - pthread_mutex_t *GetMutex() const { return &m_mutex.m_internal->m_mutex; } + pthread_mutex_t *GetPMutex() const { return &m_mutex.m_internal->m_mutex; } wxMutex& m_mutex; pthread_cond_t m_cond; + + bool m_isOk; }; wxConditionInternal::wxConditionInternal(wxMutex& mutex) : m_mutex(mutex) { - if ( pthread_cond_init( &m_cond, NULL ) != 0 ) - { - wxLogDebug(_T("pthread_cond_init() failed")); - } -} + int err = pthread_cond_init(&m_cond, NULL /* default attributes */); -wxConditionInternal::~wxConditionInternal() -{ - if ( pthread_cond_destroy( &m_cond ) != 0 ) - { - wxLogDebug(_T("pthread_cond_destroy() failed")); - } -} + m_isOk = err == 0; -void wxConditionInternal::Wait() -{ - if ( pthread_cond_wait( &m_cond, GetMutex() ) != 0 ) + if ( !m_isOk ) { - wxLogDebug(_T("pthread_cond_wait() failed")); + wxLogApiError(_T("pthread_cond_init()"), err); } } -bool wxConditionInternal::Wait( const timespec *ts ) +wxConditionInternal::~wxConditionInternal() { - int result = pthread_cond_timedwait( &m_cond, GetMutex(), ts ); - if ( result == ETIMEDOUT ) - return FALSE; - - if ( result != 0 ) + if ( m_isOk ) { - wxLogDebug(_T("pthread_cond_timedwait() failed")); + int err = pthread_cond_destroy(&m_cond); + if ( err != 0 ) + { + wxLogApiError(_T("pthread_cond_destroy()"), err); + } } - - return TRUE; } -void wxConditionInternal::Signal() -{ - int result = pthread_cond_signal( &m_cond ); - if ( result != 0 ) +wxCondError wxConditionInternal::Wait() { - wxFAIL_MSG( _T("pthread_cond_signal() failed") ); - } -} + int err = pthread_cond_wait(&m_cond, GetPMutex()); + if ( err != 0 ) + { + wxLogApiError(_T("pthread_cond_wait()"), err); -void wxConditionInternal::Broadcast() -{ - int result = pthread_cond_broadcast( &m_cond ); - if ( result != 0 ) -{ - wxFAIL_MSG( _T("pthread_cond_broadcast() failed") ); + return wxCOND_MISC_ERROR; } -} - -// --------------------------------------------------------------------------- -// wxCondition -// --------------------------------------------------------------------------- - -wxCondition::wxCondition(wxMutex& mutex) -{ - m_internal = new wxConditionInternal( mutex ); -} - -wxCondition::~wxCondition() -{ - delete m_internal; + return wxCOND_NO_ERROR; } -void wxCondition::Wait() -{ - m_internal->Wait(); -} - -bool wxCondition::Wait( unsigned long timeout_millis ) +wxCondError wxConditionInternal::WaitTimeout(unsigned long milliseconds) { wxLongLong curtime = wxGetLocalTimeMillis(); - curtime += timeout_millis; + curtime += milliseconds; wxLongLong temp = curtime / 1000; int sec = temp.GetLo(); - temp = temp * 1000; + temp *= 1000; temp = curtime - temp; int millis = temp.GetLo(); @@ -430,17 +382,46 @@ bool wxCondition::Wait( unsigned long timeout_millis ) tspec.tv_sec = sec; tspec.tv_nsec = millis * 1000L * 1000L; - return m_internal->Wait(&tspec); + int err = pthread_cond_timedwait( &m_cond, GetPMutex(), &tspec ); + switch ( err ) + { + case ETIMEDOUT: + return wxCOND_TIMEOUT; + + case 0: + return wxCOND_NO_ERROR; + + default: + wxLogApiError(_T("pthread_cond_timedwait()"), err); + } + + return wxCOND_MISC_ERROR; } -void wxCondition::Signal() +wxCondError wxConditionInternal::Signal() { - m_internal->Signal(); + int err = pthread_cond_signal(&m_cond); + if ( err != 0 ) + { + wxLogApiError(_T("pthread_cond_signal()"), err); + + return wxCOND_MISC_ERROR; + } + + return wxCOND_NO_ERROR; } -void wxCondition::Broadcast() +wxCondError wxConditionInternal::Broadcast() { - m_internal->Broadcast(); + int err = pthread_cond_broadcast(&m_cond); + if ( err != 0 ) + { + wxLogApiError(_T("pthread_cond_broadcast()"), err); + + return wxCOND_MISC_ERROR; + } + + return wxCOND_NO_ERROR; } // =========================================================================== @@ -451,222 +432,131 @@ void wxCondition::Broadcast() // wxSemaphoreInternal // --------------------------------------------------------------------------- +// we implement the semaphores using mutexes and conditions instead of using +// the sem_xxx() POSIX functions because they're not widely available and also +// because it's impossible to implement WaitTimeout() using them class wxSemaphoreInternal { public: - wxSemaphoreInternal( int initialcount, int maxcount ); + wxSemaphoreInternal(int initialcount, int maxcount); - void Wait(); - bool TryWait(); + bool IsOk() const { return m_isOk; } - bool Wait( unsigned long timeout_millis ); + wxSemaError Wait(); + wxSemaError TryWait(); + wxSemaError WaitTimeout(unsigned long milliseconds); - void Post(); + wxSemaError Post(); private: wxMutex m_mutex; wxCondition m_cond; - int count, - maxcount; + size_t m_count, + m_maxcount; + + bool m_isOk; }; -wxSemaphoreInternal::wxSemaphoreInternal( int initialcount, int maxcount ) +wxSemaphoreInternal::wxSemaphoreInternal(int initialcount, int maxcount) : m_cond(m_mutex) { - if ( (initialcount < 0) || ((maxcount > 0) && (initialcount > maxcount)) ) + if ( (initialcount < 0 || maxcount < 0) || + ((maxcount > 0) && (initialcount > maxcount)) ) + { + wxFAIL_MSG( _T("wxSemaphore: invalid initial or maximal count") ); + + m_isOk = FALSE; + } + else { - wxFAIL_MSG( _T("wxSemaphore: invalid initial count") ); + m_maxcount = (size_t)maxcount; + m_count = (size_t)initialcount; } - maxcount = maxcount; - count = initialcount; + m_isOk = m_mutex.IsOk() && m_cond.IsOk(); } -void wxSemaphoreInternal::Wait() +wxSemaError wxSemaphoreInternal::Wait() { wxMutexLocker locker(m_mutex); - while ( count <= 0 ) + while ( m_count == 0 ) { - m_cond.Wait(); + wxLogTrace(TRACE_SEMA, + "Thread %ld waiting for semaphore to become signalled", + wxThread::GetCurrentId()); + + if ( m_cond.Wait() != wxCOND_NO_ERROR ) + return wxSEMA_MISC_ERROR; + + wxLogTrace(TRACE_SEMA, + "Thread %ld finished waiting for semaphore, count = %u", + wxThread::GetCurrentId(), m_count); } - count--; + m_count--; + + return wxSEMA_NO_ERROR; } -bool wxSemaphoreInternal::TryWait() +wxSemaError wxSemaphoreInternal::TryWait() { wxMutexLocker locker(m_mutex); - if ( count <= 0 ) - return FALSE; + if ( m_count == 0 ) + return wxSEMA_BUSY; - count--; + m_count--; - return TRUE; + return wxSEMA_NO_ERROR; } -bool wxSemaphoreInternal::Wait( unsigned long timeout_millis ) +wxSemaError wxSemaphoreInternal::WaitTimeout(unsigned long milliseconds) { wxMutexLocker locker(m_mutex); wxLongLong startTime = wxGetLocalTimeMillis(); - while ( count <= 0 ) + while ( m_count == 0 ) { wxLongLong elapsed = wxGetLocalTimeMillis() - startTime; - long remainingTime = (long)timeout_millis - (long)elapsed.GetLo(); + long remainingTime = (long)milliseconds - (long)elapsed.GetLo(); if ( remainingTime <= 0 ) - return FALSE; + { + // timeout + return wxSEMA_TIMEOUT; + } - bool result = m_cond.Wait( remainingTime ); - if ( !result ) - return FALSE; + if ( m_cond.Wait(remainingTime) != wxCOND_NO_ERROR ) + return wxSEMA_MISC_ERROR; } - count--; + m_count--; - return TRUE; + return wxSEMA_NO_ERROR; } -void wxSemaphoreInternal::Post() +wxSemaError wxSemaphoreInternal::Post() { wxMutexLocker locker(m_mutex); - if ( maxcount > 0 && count == maxcount ) + if ( m_maxcount > 0 && m_count == m_maxcount ) { - wxFAIL_MSG( _T("wxSemaphore::Post() overflow") ); + return wxSEMA_OVERFLOW; } - count++; - - m_cond.Signal(); -} - -// -------------------------------------------------------------------------- -// wxSemaphore -// -------------------------------------------------------------------------- - -wxSemaphore::wxSemaphore( int initialcount, int maxcount ) -{ - m_internal = new wxSemaphoreInternal( initialcount, maxcount ); -} - -wxSemaphore::~wxSemaphore() -{ - delete m_internal; -} - -void wxSemaphore::Wait() -{ - m_internal->Wait(); -} - -bool wxSemaphore::TryWait() -{ - return m_internal->TryWait(); -} - -bool wxSemaphore::Wait( unsigned long timeout_millis ) -{ - return m_internal->Wait( timeout_millis ); -} + m_count++; -void wxSemaphore::Post() -{ - m_internal->Post(); + wxLogTrace(TRACE_SEMA, + "Thread %ld about to signal semaphore, count = %u", + wxThread::GetCurrentId(), m_count); + + return m_cond.Signal() == wxCOND_NO_ERROR ? wxSEMA_NO_ERROR + : wxSEMA_MISC_ERROR; } -// This class is used by wxThreadInternal to support Delete() on -// a detached thread -class wxRefCountedCondition -{ -public: - // start with a initial reference count of 1 - wxRefCountedCondition() - { - m_refCount = 1; - m_signaled = FALSE; - - m_mutex = new wxMutex(); - m_cond = new wxCondition( *m_mutex ); - } - - // increment the reference count - void AddRef() - { - wxMutexLocker locker( *m_mutex ); - - m_refCount++; - } - - // decrement the reference count if reference count is zero then delete the - // object - void DeleteRef() - { - bool shouldDelete = FALSE; - - m_mutex->Lock(); - - if ( --m_refCount == 0 ) - { - shouldDelete = TRUE; - } - - m_mutex->Unlock(); - - if ( shouldDelete ) - { - delete this; - } - } - - - // sets the object to signaled this signal will be a persistent signal all - // further Wait()s on the object will return without blocking - void SetSignaled() - { - wxMutexLocker locker( *m_mutex ); - - m_signaled = TRUE; - - m_cond->Broadcast(); - } - - // wait till the object is signaled if the object was already signaled then - // return immediately - void Wait() - { - wxMutexLocker locker( *m_mutex ); - - if ( !m_signaled ) - { - m_cond->Wait(); - } - } - -private: - int m_refCount; - - wxMutex *m_mutex; - wxCondition *m_cond; - - bool m_signaled; - - // Cannot delete this object directly, call DeleteRef() instead - ~wxRefCountedCondition() - { - delete m_cond; - delete m_mutex; - } - - // suppress gcc warning about the class having private dtor and not having - // friend (so what??) - friend class wxDummyFriend; -}; - // =========================================================================== // wxThread implementation // =========================================================================== @@ -700,12 +590,10 @@ public: // thread actions // start the thread wxThreadError Run(); + // unblock the thread allowing it to run + void SignalRun() { m_semRun.Post(); } // ask the thread to terminate void Wait(); - // wake up threads waiting for our termination - void SignalExit(); - // wake up threads waiting for our start - void SignalRun() { m_semRun.Post(); } // go to sleep until Resume() is called void Pause(); // resume the thread @@ -717,7 +605,23 @@ public: void SetPriority(int prio) { m_prio = prio; } // state wxThreadState GetState() const { return m_state; } - void SetState(wxThreadState state) { m_state = state; } + void SetState(wxThreadState state) + { +#ifdef __WXDEBUG__ + static const wxChar *stateNames[] = + { + _T("NEW"), + _T("RUNNING"), + _T("PAUSED"), + _T("EXITED"), + }; + + wxLogTrace(TRACE_THREADS, _T("Thread %ld: %s => %s."), + GetId(), stateNames[m_state], stateNames[state]); +#endif // __WXDEBUG__ + + m_state = state; + } // id pthread_t GetId() const { return m_threadId; } pthread_t *GetIdPtr() { return &m_threadId; } @@ -735,12 +639,11 @@ public: // tell the thread that it is a detached one void Detach() { - m_shouldBeJoined = m_shouldBroadcast = FALSE; + wxCriticalSectionLocker lock(m_csJoinFlag); + + m_shouldBeJoined = FALSE; m_isDetached = TRUE; } - // but even detached threads need to notifyus about their termination - // sometimes - tell the thread that it should do it - void Notify() { m_shouldBroadcast = TRUE; } #if HAVE_THREAD_CLEANUP_FUNCTIONS // this is used by wxPthreadCleanup() only @@ -766,7 +669,6 @@ private: // pthread_join(), so we have to keep track of this wxCriticalSection m_csJoinFlag; bool m_shouldBeJoined; - bool m_shouldBroadcast; bool m_isDetached; // this semaphore is posted by Run() and the threads Entry() is not @@ -776,11 +678,6 @@ private: // this one is signaled when the thread should resume after having been // Pause()d wxSemaphore m_semSuspend; - - // finally this one is signalled when the thread exits - // we are using a reference counted condition to support - // Delete() for a detached thread - wxRefCountedCondition *m_condEnd; }; // ---------------------------------------------------------------------------- @@ -833,17 +730,17 @@ void *wxThreadInternal::PthreadStart(wxThread *thread) if ( !dontRunAtAll ) { // call the main entry + wxLogTrace(TRACE_THREADS, _T("Thread %ld about to enter its Entry()."), + pthread->GetId()); + pthread->m_exitcode = thread->Entry(); - wxLogTrace(TRACE_THREADS, _T("Thread %ld left its Entry()."), - pthread->GetId()); + wxLogTrace(TRACE_THREADS, _T("Thread %ld Entry() returned %lu."), + pthread->GetId(), (unsigned long)pthread->m_exitcode); { wxCriticalSectionLocker lock(thread->m_critsect); - wxLogTrace(TRACE_THREADS, _T("Thread %ld changes state to EXITED."), - pthread->GetId()); - // change the state of the thread to "exited" so that // wxPthreadCleanup handler won't do anything from now (if it's // called before we do pthread_cleanup_pop below) @@ -861,6 +758,7 @@ void *wxThreadInternal::PthreadStart(wxThread *thread) if ( dontRunAtAll ) { + // FIXME: deleting a possibly joinable thread here??? delete thread; return EXITCODE_CANCELLED; @@ -918,15 +816,11 @@ wxThreadInternal::wxThreadInternal() // defaults for joinable threads m_shouldBeJoined = TRUE; - m_shouldBroadcast = TRUE; m_isDetached = FALSE; - - m_condEnd = new wxRefCountedCondition(); } wxThreadInternal::~wxThreadInternal() { - m_condEnd->DeleteRef(); } wxThreadError wxThreadInternal::Run() @@ -934,46 +828,29 @@ wxThreadError wxThreadInternal::Run() wxCHECK_MSG( GetState() == STATE_NEW, wxTHREAD_RUNNING, wxT("thread may only be started once after Create()") ); - SignalRun(); - SetState(STATE_RUNNING); + // wake up threads waiting for our start + SignalRun(); + return wxTHREAD_NO_ERROR; } void wxThreadInternal::Wait() { + wxCHECK_RET( !m_isDetached, _T("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() ) wxMutexGuiLeave(); - bool isDetached = m_isDetached; - wxThreadIdType id = (wxThreadIdType) GetId(); - wxLogTrace(TRACE_THREADS, - _T("Starting to wait for thread %ld to exit."), id); - - // wait until the thread terminates (we're blocking in _another_ thread, - // of course) - - // a reference counting condition is used to handle the - // case where a detached thread deletes itself - // before m_condEnd->Wait() returns - // in this case the deletion of the condition object is deferred until - // all Wait()ing threads have finished calling DeleteRef() - m_condEnd->AddRef(); - m_condEnd->Wait(); - m_condEnd->DeleteRef(); + _T("Starting to wait for thread %ld to exit."), GetId()); - wxLogTrace(TRACE_THREADS, _T("Finished waiting for thread %ld."), id); - - // we can't use any member variables any more if the thread is detached - // because it could be already deleted - if ( !isDetached ) + // to avoid memory leaks we should call pthread_join(), but it must only be + // done once so use a critical section to serialize the code below { - // to avoid memory leaks we should call pthread_join(), but it must - // only be done once wxCriticalSectionLocker lock(m_csJoinFlag); if ( m_shouldBeJoined ) @@ -982,8 +859,12 @@ void wxThreadInternal::Wait() // we're cancelled inside pthread_join(), things will almost // certainly break - but if we disable the cancellation, we // might deadlock - if ( pthread_join((pthread_t)id, &m_exitcode) != 0 ) + if ( pthread_join((pthread_t)GetId(), &m_exitcode) != 0 ) { + // this is a serious problem, so use wxLogError and not + // wxLogDebug: it is possible to bring the system to its knees + // by creating too many threads and not joining them quite + // easily wxLogError(_("Failed to join a thread, potential memory leak " "detected - please restart the program")); } @@ -997,22 +878,6 @@ void wxThreadInternal::Wait() wxMutexGuiEnter(); } -void wxThreadInternal::SignalExit() -{ - wxLogTrace(TRACE_THREADS, _T("Thread %ld about to exit."), GetId()); - - SetState(STATE_EXITED); - - // wake up all the threads waiting for our termination - if there are any - if ( m_shouldBroadcast ) - { - wxLogTrace(TRACE_THREADS, _T("Thread %ld signals end condition."), - GetId()); - - m_condEnd->SetSignaled(); - } -} - void wxThreadInternal::Pause() { // the state is set from the thread which pauses us first, this function @@ -1356,9 +1221,6 @@ wxThreadError wxThread::Pause() return wxTHREAD_NOT_RUNNING; } - wxLogTrace(TRACE_THREADS, _T("Asking thread %ld to pause."), - GetId()); - // just set a flag, the thread will be really paused only during the next // call to TestDestroy() m_internal->SetState(STATE_PAUSED); @@ -1419,27 +1281,22 @@ wxThreadError wxThread::Delete(ExitCode *rc) wxCHECK_MSG( This() != this, wxTHREAD_MISC_ERROR, _T("a thread can't delete itself") ); + bool isDetached = m_isDetached; + m_critsect.Enter(); wxThreadState state = m_internal->GetState(); // ask the thread to stop m_internal->SetCancelFlag(); - if ( m_isDetached ) - { - // detached threads won't broadcast about their termination by default - // because usually nobody waits for them - but here we do, so ask the - // thread to notify us - m_internal->Notify(); - } - m_critsect.Leave(); switch ( state ) { case STATE_NEW: // we need to wake up the thread so that PthreadStart() will - // terminate - right now it's blocking on m_semRun + // terminate - right now it's blocking on run semaphore in + // PthreadStart() m_internal->SignalRun(); // fall through @@ -1449,24 +1306,24 @@ wxThreadError wxThread::Delete(ExitCode *rc) break; case STATE_PAUSED: - // resume the thread first (don't call our Resume() because this - // would dead lock when it tries to enter m_critsect) + // resume the thread first m_internal->Resume(); // fall through default: - // wait until the thread stops - m_internal->Wait(); - - if ( rc ) + if ( !isDetached ) { - wxASSERT_MSG( !m_isDetached, - _T("no return code for detached threads") ); + // wait until the thread stops + m_internal->Wait(); - // if it's a joinable thread, it's not deleted yet - *rc = m_internal->GetExitCode(); + if ( rc ) + { + // return the exit code of the thread + *rc = m_internal->GetExitCode(); + } } + //else: can't wait for detached threads } return wxTHREAD_NO_ERROR; @@ -1527,10 +1384,13 @@ void wxThread::Exit(ExitCode status) _T("wxThread::Exit() can only be called in the " "context of the same thread") ); - // from the moment we call OnExit(), the main program may terminate at any - // moment, so mark this thread as being already in process of being - // deleted or wxThreadModule::OnExit() will try to delete it again - ScheduleThreadForDeletion(); + if ( m_isDetached ) + { + // from the moment we call OnExit(), the main program may terminate at + // any moment, so mark this thread as being already in process of being + // deleted or wxThreadModule::OnExit() will try to delete it again + ScheduleThreadForDeletion(); + } // don't enter m_critsect before calling OnExit() because the user code // might deadlock if, for example, it signals a condition in OnExit() (a @@ -1538,17 +1398,6 @@ void wxThread::Exit(ExitCode status) // m_critsect on us (almost all of them do) OnExit(); - // now do enter it because SignalExit() will change our state - m_critsect.Enter(); - - // next wake up the threads waiting for us (OTOH, this function won't return - // until someone waited for us!) - m_internal->SignalExit(); - - // leave the critical section before entering the dtor which tries to - // enter it - m_critsect.Leave(); - // delete C++ thread object if this is a detached thread - user is // responsible for doing this for joinable ones if ( m_isDetached ) @@ -1617,18 +1466,6 @@ wxThread::~wxThread() // remove this thread from the global array gs_allThreads.Remove(this); - - // detached thread will decrement this counter in DeleteThread(), but it - // is not called for the joinable threads, so do it here - if ( !m_isDetached ) - { - wxMutexLocker lock( *gs_mutexDeleteThread ); - - gs_nThreadsBeingDeleted--; - - wxLogTrace(TRACE_THREADS, _T("%u scheduled for deletion threads left."), - gs_nThreadsBeingDeleted - 1); - } } // ----------------------------------------------------------------------------- @@ -1717,9 +1554,9 @@ void wxThreadModule::OnExit() nThreadsBeingDeleted = gs_nThreadsBeingDeleted; if ( nThreadsBeingDeleted > 0 ) -{ + { wxLogTrace(TRACE_THREADS, _T("Waiting for %u threads to disappear"), - nThreadsBeingDeleted); + nThreadsBeingDeleted); // have to wait until all of them disappear gs_condAllDeleted->Wait(); @@ -1781,8 +1618,7 @@ static void DeleteThread(wxThread *This) delete This; wxCHECK_RET( gs_nThreadsBeingDeleted > 0, - _T("no threads scheduled for deletion, yet we delete " - "one?") ); + _T("no threads scheduled for deletion, yet we delete one?") ); wxLogTrace(TRACE_THREADS, _T("%u scheduled for deletion threads left."), gs_nThreadsBeingDeleted - 1); @@ -1808,5 +1644,11 @@ void wxMutexGuiLeave() #endif // wxUSE_GUI } -#endif - // wxUSE_THREADS +// ---------------------------------------------------------------------------- +// include common implementation code +// ---------------------------------------------------------------------------- + +#include "wx/thrimpl.cpp" + +#endif // wxUSE_THREADS + -- 2.49.0