/////////////////////////////////////////////////////////////////////////////
-// Name: thread.h
+// Name: wx/thread.h
// Purpose: Thread API
// Author: Guilhem Lavaux
// Modified by: Vadim Zeitlin (modifications partly inspired by omnithreads
// 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
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
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
// 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:
// 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
{
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)
};
// ----------------------------------------------------------------------------
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
//
// 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
//
// 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
//
// 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)
};
// ----------------------------------------------------------------------------
// 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
// 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)
};
// ----------------------------------------------------------------------------
typedef unsigned long wxThreadIdType;
#endif
-class wxThreadInternal;
class WXDLLEXPORT wxThread
{
public:
#else // !wxUSE_THREADS
-#include "wx/defs.h" // for WXDLLEXPORT
-
// no thread support
inline void WXDLLEXPORT wxMutexGuiEnter() { }
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:
#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
// 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_
--- /dev/null
+/////////////////////////////////////////////////////////////////////////////
+// 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();
+}
+
/////////////////////////////////////////////////////////////////////////////
-// 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
/////////////////////////////////////////////////////////////////////////////
#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
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 )
{
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()"));
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();
// 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();
//
// 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
// '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;
}
// ----------------------------------------------------------------------------
return gs_waitingForThread;
}
+// ----------------------------------------------------------------------------
+// include common implementation code
+// ----------------------------------------------------------------------------
+
+#include "wx/thrimpl.cpp"
+
#endif // wxUSE_THREADS
-// vi:sts=4:sw=4:et
// 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
// ----------------------------------------------------------------------------
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
// -----------------------------------------------------------------------------
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
// ============================================================================
// 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 <sigh>, 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()
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()
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()
// 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;
}
// ===========================================================================
// 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();
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;
}
// ===========================================================================
// 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
// ===========================================================================
// 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
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; }
// 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
// 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
// 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;
};
// ----------------------------------------------------------------------------
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)
if ( dontRunAtAll )
{
+ // FIXME: deleting a possibly joinable thread here???
delete thread;
return EXITCODE_CANCELLED;
// 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()
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 )
// 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"));
}
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
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);
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
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;
_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
// 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 )
// 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);
- }
}
// -----------------------------------------------------------------------------
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();
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);
#endif // wxUSE_GUI
}
-#endif
- // wxUSE_THREADS
+// ----------------------------------------------------------------------------
+// include common implementation code
+// ----------------------------------------------------------------------------
+
+#include "wx/thrimpl.cpp"
+
+#endif // wxUSE_THREADS
+