X-Git-Url: https://git.saurik.com/wxWidgets.git/blobdiff_plain/e75491071dbefcada61175e3eb89ce4edf335983..aacd14428971b5e199f88597f76a895c68dd298f:/src/mac/carbon/thread.cpp diff --git a/src/mac/carbon/thread.cpp b/src/mac/carbon/thread.cpp index 3dca96a6fa..cb8ecb4c2d 100644 --- a/src/mac/carbon/thread.cpp +++ b/src/mac/carbon/thread.cpp @@ -1,17 +1,17 @@ ///////////////////////////////////////////////////////////////////////////// -// Name: 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) -// Licence: wxWindows licence +// Name: thread.cpp +// Purpose: wxThread Implementation +// Author: Original from Wolfram Gloger/Guilhem Lavaux/Vadim Zeitlin +// Modified by: Aj Lavin, Stefan Csomor +// Created: 04/22/98 +// RCS-ID: $Id$ +// Copyright: (c) Wolfram Gloger (1996, 1997); Guilhem Lavaux (1998), +// Vadim Zeitlin (1999) , Stefan Csomor (2000) +// Licence: wxWindows licence ///////////////////////////////////////////////////////////////////////////// -#ifdef __GNUG__ - #pragma implementation "thread.h" +#if defined(__GNUG__) && !defined(NO_GCC_PRAGMA) +#pragma implementation "thread.h" #endif // ---------------------------------------------------------------------------- @@ -22,255 +22,879 @@ #include "wx/wxprec.h" #if defined(__BORLANDC__) - #pragma hdrstop +#pragma hdrstop #endif #ifndef WX_PRECOMP - #include "wx/wx.h" +#include "wx/wx.h" #endif #if wxUSE_THREADS -#include - -#include - #include "wx/module.h" #include "wx/thread.h" +#ifdef __WXMAC__ +#if TARGET_API_MAC_OSX +#include +#else +#include +#include +#include "wx/math.h" +#endif +#include "wx/mac/uma.h" +#endif + +#include "wx/mac/macnotfy.h" + +// ---------------------------------------------------------------------------- +// constants +// ---------------------------------------------------------------------------- + // the possible states of the thread ("=>" shows all possible transitions from // this state) enum wxThreadState { - STATE_NEW, // didn't start execution yet (=> RUNNING) - STATE_RUNNING, // thread is running (=> PAUSED, CANCELED) - STATE_PAUSED, // thread is temporarily suspended (=> RUNNING) - STATE_CANCELED, // thread should terminate a.s.a.p. (=> EXITED) - STATE_EXITED // thread is terminating + STATE_NEW, // didn't start execution yet (=> RUNNING) + STATE_RUNNING, // thread is running (=> PAUSED, CANCELED) + STATE_PAUSED, // thread is temporarily suspended (=> RUNNING) + STATE_CANCELED, // thread should terminate a.s.a.p. (=> EXITED) + STATE_EXITED // thread is terminating }; // ---------------------------------------------------------------------------- -// static variables +// this module globals // ---------------------------------------------------------------------------- -// if it's FALSE, some secondary thread is holding the GUI lock -static bool s_bGuiOwnedByMainThread = TRUE; + +// the task ID of the main thread +static wxThreadIdType gs_idMainThread = kInvalidID; + +// this is the Per-Task Storage for the pointer to the appropriate wxThread +TaskStorageIndex gs_tlsForWXThread = 0 ; + +// if it's false, some secondary thread is holding the GUI lock +static bool gs_bGuiOwnedByMainThread = true; // critical section which controls access to all GUI functions: any secondary // thread (i.e. except the main one) must enter this crit section before doing // any GUI calls -static wxCriticalSection *s_critsectGui = NULL; +static wxCriticalSection *gs_critsectGui = NULL; -// critical section which protects s_nWaitingForGui variable -static wxCriticalSection *s_critsectWaitingForGui = NULL; +// critical section which protects gs_nWaitingForGui variable +static wxCriticalSection *gs_critsectWaitingForGui = NULL; // number of threads waiting for GUI in wxMutexGuiEnter() -static size_t s_nWaitingForGui = 0; +static size_t gs_nWaitingForGui = 0; + +// overall number of threads, needed for determining the sleep value of the main +// event loop +size_t g_numberOfThreads = 0; + -// are we waiting for a thread termination? -static bool s_waitingForThread = FALSE; + +#if wxUSE_GUI + +MPCriticalRegionID gs_guiCritical = kInvalidID; + +#endif // ============================================================================ -// Windows implementation of thread classes +// MacOS implementation of thread classes // ============================================================================ +/* + Notes : + + The implementation is very close to the phtreads implementation, the reason for + using MPServices is the fact that these are also available under OS 9. Thus allowing + for one common API for all current builds. + + As soon as wxThreads are on a 64 bit address space, the TLS must be extended + to use two indices one for each 32 bit part as the MP implementation is limited + to longs. + + I have three implementations for mutexes : + version A based on a binary semaphore, problem - not reentrant, version B based + on a critical region, allows for reentrancy, performance implications not + yet tested, and third a plain pthreads implementation + + The same for condition internal, one implementation by Aj Lavin and the other one + copied from the thrimpl.cpp which I assume has been more broadly tested, I've just + replaced the interlock increment with the appropriate PPC calls +*/ + +// ---------------------------------------------------------------------------- +// wxCriticalSection +// ---------------------------------------------------------------------------- + +wxCriticalSection::wxCriticalSection() +{ + MPCreateCriticalRegion( (MPCriticalRegionID*) &m_critRegion ) ; +} + +wxCriticalSection::~wxCriticalSection() +{ + MPDeleteCriticalRegion( (MPCriticalRegionID) m_critRegion ) ; +} + +void wxCriticalSection::Enter() +{ + MPEnterCriticalRegion( (MPCriticalRegionID) m_critRegion , kDurationForever ) ; +} + +void wxCriticalSection::Leave() +{ + MPExitCriticalRegion((MPCriticalRegionID) m_critRegion ) ; +} + // ---------------------------------------------------------------------------- // wxMutex implementation // ---------------------------------------------------------------------------- + +#if TARGET_API_MAC_OSX +#define wxUSE_MAC_SEMAPHORE_MUTEX 0 +#define wxUSE_MAC_CRITICAL_REGION_MUTEX 1 +#define wxUSE_MAC_PTHREADS_MUTEX 0 +#else +#define wxUSE_MAC_SEMAPHORE_MUTEX 0 +#define wxUSE_MAC_CRITICAL_REGION_MUTEX 1 +#define wxUSE_MAC_PTHREADS_MUTEX 0 +#endif + +#if wxUSE_MAC_PTHREADS_MUTEX + +#include + class wxMutexInternal { public: - Handle p_mutex; + 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; }; -wxMutex::wxMutex() +#ifdef HAVE_PTHREAD_MUTEXATTR_T +// on some systems pthread_mutexattr_settype() is not in the headers (but it is +// in the library, otherwise we wouldn't compile this code at all) +extern "C" int pthread_mutexattr_settype(pthread_mutexattr_t *, int); +#endif + +wxMutexInternal::wxMutexInternal(wxMutexType mutexType) { - p_internal = new wxMutexInternal; -// p_internal->p_mutex = CreateMutex(NULL, FALSE, NULL); - if ( !p_internal->p_mutex ) + int err; + switch ( mutexType ) { - wxLogSysError(_("Can not create mutex.")); + 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); + + 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; + } +#else // 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_locked = 0; + m_isOk = err == 0; + if ( !m_isOk ) + { + wxLogApiError( wxT("pthread_mutex_init()"), err); + } } -wxMutex::~wxMutex() +wxMutexInternal::~wxMutexInternal() { - if (m_locked > 0) - wxLogDebug(wxT("Warning: freeing a locked mutex (%d locks)."), m_locked); -// CloseHandle(p_internal->p_mutex); + if ( m_isOk ) + { + int err = pthread_mutex_destroy(&m_mutex); + if ( err != 0 ) + { + wxLogApiError( wxT("pthread_mutex_destroy()"), err); + } + } } -wxMutexError wxMutex::Lock() +wxMutexError wxMutexInternal::Lock() { -/* - DWORD ret; + int err = pthread_mutex_lock(&m_mutex); + switch ( err ) + { + case EDEADLK: + // 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; + + case EINVAL: + wxLogDebug(_T("pthread_mutex_lock(): mutex not initialized.")); + break; - ret = WaitForSingleObject(p_internal->p_mutex, INFINITE); - switch ( ret ) + case 0: + return wxMUTEX_NO_ERROR; + + default: + wxLogApiError(_T("pthread_mutex_lock()"), err); + } + + return wxMUTEX_MISC_ERROR; +} + +wxMutexError wxMutexInternal::TryLock() +{ + int err = pthread_mutex_trylock(&m_mutex); + switch ( err ) { - case WAIT_ABANDONED: + case EBUSY: + // not an error: mutex is already locked, but we're prepared for + // this return wxMUTEX_BUSY; - case WAIT_OBJECT_0: - // ok + case EINVAL: + wxLogDebug(_T("pthread_mutex_trylock(): mutex not initialized.")); break; - case WAIT_FAILED: - wxLogSysError(_("Couldn't acquire a mutex lock")); - return wxMUTEX_MISC_ERROR; + case 0: + return wxMUTEX_NO_ERROR; - case WAIT_TIMEOUT: default: - wxFAIL_MSG(wxT("impossible return value in wxMutex::Lock")); + wxLogApiError(_T("pthread_mutex_trylock()"), err); } -*/ - m_locked++; - return wxMUTEX_NO_ERROR; + + return wxMUTEX_MISC_ERROR; } -wxMutexError wxMutex::TryLock() +wxMutexError wxMutexInternal::Unlock() { -/* - DWORD ret; + int err = pthread_mutex_unlock(&m_mutex); + switch ( err ) + { + case EPERM: + // we don't own the mutex + return wxMUTEX_UNLOCKED; - ret = WaitForSingleObject(p_internal->p_mutex, 0); - if (ret == WAIT_TIMEOUT || ret == WAIT_ABANDONED) - return wxMUTEX_BUSY; + case EINVAL: + wxLogDebug(_T("pthread_mutex_unlock(): mutex not initialized.")); + break; - m_locked++; -*/ - return wxMUTEX_NO_ERROR; + case 0: + return wxMUTEX_NO_ERROR; + + default: + wxLogApiError(_T("pthread_mutex_unlock()"), err); + } + + return wxMUTEX_MISC_ERROR; } -wxMutexError wxMutex::Unlock() + +#endif + +#if wxUSE_MAC_SEMAPHORE_MUTEX + +class wxMutexInternal { - if (m_locked > 0) - m_locked--; -/* - BOOL ret = ReleaseMutex(p_internal->p_mutex); - if ( ret == 0 ) +public: + wxMutexInternal(wxMutexType mutexType) ; + ~wxMutexInternal() ; + bool IsOk() const { return m_isOk; } + + wxMutexError Lock() ; + wxMutexError TryLock() ; + wxMutexError Unlock(); +private: + MPSemaphoreID m_semaphore; + bool m_isOk ; +}; + +wxMutexInternal::wxMutexInternal(wxMutexType mutexType ) +{ + m_isOk = false ; + m_semaphore = kInvalidID ; + + OSStatus err = noErr ; + switch( mutexType ) { - wxLogSysError(_("Couldn't release a mutex")); - return wxMUTEX_MISC_ERROR; + case wxMUTEX_DEFAULT : + { + verify_noerr( MPCreateBinarySemaphore( & m_semaphore) ); + m_isOk = ( m_semaphore != kInvalidID ) ; + } + break ; + case wxMUTEX_RECURSIVE : + wxFAIL_MSG(wxT("Recursive Mutex not supported yet") ) ; + break ; + default : + wxFAIL_MSG(wxT("Unknown mutex type") ) ; + break ; } -*/ - return wxMUTEX_NO_ERROR; } -// ---------------------------------------------------------------------------- -// wxCondition implementation -// ---------------------------------------------------------------------------- +wxMutexInternal::~wxMutexInternal() +{ + if ( m_semaphore != kInvalidID ) + MPDeleteSemaphore( m_semaphore); + MPYield() ; +} -class wxConditionInternal +wxMutexError wxMutexInternal::Lock() { -public: - Handle event; - int waiters; -}; + wxCHECK_MSG( m_isOk , wxMUTEX_MISC_ERROR , wxT("Invalid Mutex") ) ; + OSStatus err = MPWaitOnSemaphore( m_semaphore, kDurationForever); + if ( err) + { + wxLogSysError(wxT("Could not lock mutex")); + return wxMUTEX_MISC_ERROR; + } + + return wxMUTEX_NO_ERROR; +} -wxCondition::wxCondition() +wxMutexError wxMutexInternal::TryLock() { - p_internal = new wxConditionInternal; -// p_internal->event = CreateEvent(NULL, FALSE, FALSE, NULL); - if ( !p_internal->event ) + wxCHECK_MSG( m_isOk , wxMUTEX_MISC_ERROR , wxT("Invalid Mutex") ) ; + OSStatus err = MPWaitOnSemaphore( m_semaphore, kDurationImmediate); + if ( err) { - wxLogSysError(_("Can not create event object.")); + if ( err == kMPTimeoutErr) + { + return wxMUTEX_BUSY; + } + wxLogSysError(wxT("Could not try lock mutex")); + return wxMUTEX_MISC_ERROR; } + + return wxMUTEX_NO_ERROR; +} - p_internal->waiters = 0; +wxMutexError wxMutexInternal::Unlock() +{ + wxCHECK_MSG( m_isOk , wxMUTEX_MISC_ERROR , wxT("Invalid Mutex") ) ; + OSStatus err = MPSignalSemaphore( m_semaphore); + MPYield() ; + if ( err) + { + wxLogSysError(_("Could not unlock mutex")); + return wxMUTEX_MISC_ERROR; + } + return wxMUTEX_NO_ERROR; +} + +#endif + +#if wxUSE_MAC_CRITICAL_REGION_MUTEX + +class wxMutexInternal +{ +public: + wxMutexInternal(wxMutexType mutexType) ; + ~wxMutexInternal() ; + bool IsOk() const { return m_isOk; } + + wxMutexError Lock() ; + wxMutexError TryLock() ; + wxMutexError Unlock(); +private: + MPCriticalRegionID m_critRegion ; + bool m_isOk ; +}; + +wxMutexInternal::wxMutexInternal(wxMutexType mutexType ) +{ + m_isOk = false ; + m_critRegion = kInvalidID ; + + verify_noerr( MPCreateCriticalRegion( & m_critRegion) ); + m_isOk = ( m_critRegion != kInvalidID ) ; + + if ( !IsOk() ) + wxFAIL_MSG(wxT("Error when creating mutex") ) ; } -wxCondition::~wxCondition() +wxMutexInternal::~wxMutexInternal() { -// CloseHandle(p_internal->event); + if ( m_critRegion != kInvalidID ) + MPDeleteCriticalRegion( m_critRegion); + MPYield() ; } -void wxCondition::Wait(wxMutex& mutex) +wxMutexError wxMutexInternal::Lock() { - mutex.Unlock(); - p_internal->waiters++; -// WaitForSingleObject(p_internal->event, INFINITE); - p_internal->waiters--; - mutex.Lock(); + wxCHECK_MSG( m_isOk , wxMUTEX_MISC_ERROR , wxT("Invalid Mutex") ) ; + OSStatus err = MPEnterCriticalRegion( m_critRegion, kDurationForever); + if ( err) + { + wxLogSysError(wxT("Could not lock mutex")); + return wxMUTEX_MISC_ERROR; + } + + return wxMUTEX_NO_ERROR; +} + +wxMutexError wxMutexInternal::TryLock() +{ + wxCHECK_MSG( m_isOk , wxMUTEX_MISC_ERROR , wxT("Invalid Mutex") ) ; + OSStatus err = MPEnterCriticalRegion( m_critRegion, kDurationImmediate); + if ( err) + { + if ( err == kMPTimeoutErr) + { + return wxMUTEX_BUSY; + } + wxLogSysError(wxT("Could not try lock mutex")); + return wxMUTEX_MISC_ERROR; + } + + return wxMUTEX_NO_ERROR; } -bool wxCondition::Wait(wxMutex& mutex, - unsigned long sec, - unsigned long nsec) +wxMutexError wxMutexInternal::Unlock() { -// DWORD ret; + wxCHECK_MSG( m_isOk , wxMUTEX_MISC_ERROR , wxT("Invalid Mutex") ) ; + OSStatus err = MPExitCriticalRegion( m_critRegion); + MPYield() ; + if ( err) + { + wxLogSysError(_("Could not unlock mutex")); + return wxMUTEX_MISC_ERROR; + } + + return wxMUTEX_NO_ERROR; +} - mutex.Unlock(); - p_internal->waiters++; - // ret = WaitForSingleObject(p_internal->event, (sec*1000)+(nsec/1000000)); - p_internal->waiters--; - mutex.Lock(); +#endif + +// -------------------------------------------------------------------------- +// wxSemaphore +// -------------------------------------------------------------------------- + +class wxSemaphoreInternal +{ +public: + wxSemaphoreInternal(int initialcount, int maxcount); + ~wxSemaphoreInternal(); + + bool IsOk() const { return m_isOk; } + + wxSemaError WaitTimeout(unsigned long milliseconds); + + wxSemaError Wait() { return WaitTimeout( kDurationForever); } + + wxSemaError TryWait() + { + wxSemaError err = WaitTimeout(kDurationImmediate); + if ( err == wxSEMA_TIMEOUT ) + err = wxSEMA_BUSY ; + return err ; + } + wxSemaError Post(); + +private: + MPSemaphoreID m_semaphore; + bool m_isOk ; +}; - return TRUE; // false for timeout +wxSemaphoreInternal::wxSemaphoreInternal(int initialcount, int maxcount) +{ + m_isOk = false ; + m_semaphore = kInvalidID ; + if ( maxcount == 0 ) + { + // make it practically infinite + maxcount = INT_MAX; + } + verify_noerr( MPCreateSemaphore( maxcount, initialcount, & m_semaphore) ); + m_isOk = ( m_semaphore != kInvalidID ) ; + + if ( !IsOk() ) + wxFAIL_MSG(wxT("Error when creating semaphore") ) ; } -void wxCondition::Signal() +wxSemaphoreInternal::~wxSemaphoreInternal() { -// SetEvent(p_internal->event); + if( m_semaphore != kInvalidID ) + MPDeleteSemaphore( m_semaphore); + MPYield() ; } -void wxCondition::Broadcast() +wxSemaError wxSemaphoreInternal::WaitTimeout(unsigned long milliseconds) { - int i; + OSStatus err = MPWaitOnSemaphore( m_semaphore, milliseconds); + if ( err) + { + if ( err == kMPTimeoutErr) + { + return wxSEMA_TIMEOUT; + } + return wxSEMA_MISC_ERROR; + } + return wxSEMA_NO_ERROR; +} - for (i=0;iwaiters;i++) +wxSemaError wxSemaphoreInternal::Post() +{ + OSStatus err = MPSignalSemaphore( m_semaphore); + MPYield() ; + if ( err) { -// if ( SetEvent(p_internal->event) == 0 ) - { - wxLogSysError(_("Couldn't change the state of event object.")); - } + return wxSEMA_MISC_ERROR; } + return wxSEMA_NO_ERROR; } // ---------------------------------------------------------------------------- -// wxCriticalSection implementation +// wxCondition implementation // ---------------------------------------------------------------------------- -class wxCriticalSectionInternal +#if 0 + +class wxConditionInternal { public: - // init the critical section object - wxCriticalSectionInternal() - { /* ::InitializeCriticalSection(&m_data);*/ } + + wxConditionInternal(wxMutex& mutex) + : m_mutex( mutex), + m_semaphore( 0, 1), + m_gate( 1, 1) + { + m_waiters = 0; + m_signals = 0; + m_canceled = 0; + } + + ~wxConditionInternal() + { + } + + bool IsOk() const { return m_mutex.IsOk() ; } + + wxCondError Wait() + { + return WaitTimeout( kDurationForever); + } + + wxCondError WaitTimeout(unsigned long msectimeout); + + wxCondError Signal() + { + return DoSignal( false); + } + + wxCondError Broadcast() + { + return DoSignal( true); + } + +private: + + wxCondError DoSignal( bool signalAll); + + wxMutex& m_mutex; + wxSemaphoreInternal m_semaphore; // Signals the waiting threads. + wxSemaphoreInternal m_gate; + wxCriticalSection m_varSection; + size_t m_waiters; // Number of threads waiting for a signal. + size_t m_signals; // Number of signals to send. + size_t m_canceled; // Number of canceled waiters in m_waiters. +}; - // implicit cast to the associated data - operator Handle *() { return &m_data; } - // free the associated ressources - ~wxCriticalSectionInternal() - { /* ::DeleteCriticalSection(&m_data); */} +wxCondError wxConditionInternal::WaitTimeout(unsigned long msectimeout) +{ + m_gate.Wait(); + if ( ++ m_waiters == INT_MAX) + { + m_varSection.Enter(); + m_waiters -= m_canceled; + m_signals -= m_canceled; + m_canceled = 0; + m_varSection.Leave(); + } + m_gate.Post(); + + m_mutex.Unlock(); + + wxSemaError err = m_semaphore.WaitTimeout( msectimeout); + wxASSERT( err == wxSEMA_NO_ERROR || err == wxSEMA_TIMEOUT); + + m_varSection.Enter(); + if ( err != wxSEMA_NO_ERROR) + { + if ( m_signals > m_canceled) + { + // A signal is being sent after we timed out. + + if ( m_waiters == m_signals) + { + // There are no excess waiters to catch the signal, so + // we must throw it away. + + wxSemaError err2 = m_semaphore.Wait(); + if ( err2 != wxSEMA_NO_ERROR) + { + wxLogSysError(_("Error while waiting on semaphore")); + } + wxASSERT( err2 == wxSEMA_NO_ERROR); + -- m_waiters; + if ( -- m_signals == m_canceled) + { + // This was the last signal. open the gate. + wxASSERT( m_waiters == m_canceled); + m_gate.Post(); + } + } + else + { + // There are excess waiters to catch the signal, leave + // it be. + -- m_waiters; + } + } + else + { + // No signals is being sent. + // The gate may be open or closed, so we can't touch m_waiters. + ++ m_canceled; + ++ m_signals; + } + } + else + { + // We caught a signal. + wxASSERT( m_signals > m_canceled); + -- m_waiters; + if ( -- m_signals == m_canceled) + { + // This was the last signal. open the gate. + wxASSERT( m_waiters == m_canceled); + m_gate.Post(); + } + } + m_varSection.Leave(); + + m_mutex.Lock(); + + if ( err) + { + return err == wxSEMA_TIMEOUT ? wxCOND_TIMEOUT : wxCOND_MISC_ERROR; + } + + return wxCOND_NO_ERROR; +} + + +wxCondError wxConditionInternal::DoSignal( bool signalAll) +{ + m_gate.Wait(); + m_varSection.Enter(); + + wxASSERT( m_signals == m_canceled); + + if ( m_waiters == m_canceled) + { + m_varSection.Leave(); + m_gate.Post(); + return wxCOND_NO_ERROR; + } + + if ( m_canceled > 0) + { + m_waiters -= m_canceled; + m_signals = 0; + m_canceled = 0; + } + + m_signals = signalAll ? m_waiters : 1; + size_t n = m_signals; + + m_varSection.Leave(); + + // Let the waiters inherit the gate lock. + + do + { + wxSemaError err = m_semaphore.Post(); + wxASSERT( err == wxSEMA_NO_ERROR); + } while ( -- n); + + return wxCOND_NO_ERROR; +} + +#else +class wxConditionInternal +{ +public: + wxConditionInternal(wxMutex& mutex); + + bool IsOk() const { return m_mutex.IsOk() && m_semaphore.IsOk(); } + + wxCondError Wait(); + wxCondError WaitTimeout(unsigned long milliseconds); + + wxCondError Signal(); + wxCondError Broadcast(); private: - Handle m_data; + // the number of threads currently waiting for this condition + SInt32 m_numWaiters; + + // the critical section protecting m_numWaiters + wxCriticalSection m_csWaiters; + + wxMutex& m_mutex; + wxSemaphore m_semaphore; + + DECLARE_NO_COPY_CLASS(wxConditionInternal) }; -wxCriticalSection::wxCriticalSection() +wxConditionInternal::wxConditionInternal(wxMutex& mutex) + : m_mutex(mutex) { - m_critsect = new wxCriticalSectionInternal; + // another thread can't access it until we return from ctor, so no need to + // protect access to m_numWaiters here + m_numWaiters = 0; } -wxCriticalSection::~wxCriticalSection() +wxCondError wxConditionInternal::Wait() { - delete m_critsect; + // increment the number of waiters + IncrementAtomic(&m_numWaiters); + + m_mutex.Unlock(); + + // a potential race condition can occur here + // + // after a thread increments nwaiters, and unlocks the mutex and before the + // semaphore.Wait() is called, if another thread can cause a signal to be + // generated + // + // this race condition is handled by using a semaphore and incrementing the + // semaphore only if 'nwaiters' is greater that zero since the semaphore, + // can 'remember' signals the race condition will not occur + + // wait ( if necessary ) and decrement semaphore + wxSemaError err = m_semaphore.Wait(); + m_mutex.Lock(); + + return err == wxSEMA_NO_ERROR ? wxCOND_NO_ERROR : wxCOND_MISC_ERROR; } -void wxCriticalSection::Enter() +wxCondError wxConditionInternal::WaitTimeout(unsigned long milliseconds) { -// ::EnterCriticalSection(*m_critsect); + IncrementAtomic(&m_numWaiters); + + m_mutex.Unlock(); + + // a race condition can occur at this point in the code + // + // please see the comments in Wait(), for details + + wxSemaError err = m_semaphore.WaitTimeout(milliseconds); + + if ( err == wxSEMA_BUSY ) + { + // another potential race condition exists here it is caused when a + // 'waiting' thread timesout, and returns from WaitForSingleObject, but + // has not yet decremented 'nwaiters'. + // + // at this point if another thread calls signal() then the semaphore + // will be incremented, but the waiting thread will miss it. + // + // to handle this particular case, the waiting thread calls + // WaitForSingleObject again with a timeout of 0, after locking + // 'nwaiters_mutex'. this call does not block because of the zero + // timeout, but will allow the waiting thread to catch the missed + // signals. + wxCriticalSectionLocker lock(m_csWaiters); + + err = m_semaphore.WaitTimeout(0); + + if ( err != wxSEMA_NO_ERROR ) + { + m_numWaiters--; + } + } + + m_mutex.Lock(); + + return err == wxSEMA_NO_ERROR ? wxCOND_NO_ERROR : wxCOND_MISC_ERROR; } -void wxCriticalSection::Leave() +wxCondError wxConditionInternal::Signal() { -// ::LeaveCriticalSection(*m_critsect); + wxCriticalSectionLocker lock(m_csWaiters); + + if ( m_numWaiters > 0 ) + { + // increment the semaphore by 1 + if ( m_semaphore.Post() != wxSEMA_NO_ERROR ) + return wxCOND_MISC_ERROR; + + m_numWaiters--; + } + + return wxCOND_NO_ERROR; } +wxCondError wxConditionInternal::Broadcast() +{ + wxCriticalSectionLocker lock(m_csWaiters); + + while ( m_numWaiters > 0 ) + { + if ( m_semaphore.Post() != wxSEMA_NO_ERROR ) + return wxCOND_MISC_ERROR; + + m_numWaiters--; + } + + return wxCOND_NO_ERROR; +} +#endif + +// ---------------------------------------------------------------------------- +// wxCriticalSection implementation +// ---------------------------------------------------------------------------- + +// XXX currently implemented as mutex in headers. Change to critical section. + // ---------------------------------------------------------------------------- // wxThread implementation // ---------------------------------------------------------------------------- @@ -278,140 +902,316 @@ void wxCriticalSection::Leave() // wxThreadInternal class // ---------------------- -/* class wxThreadInternal { public: wxThreadInternal() { - m_hThread = 0; - m_state = STATE_NEW; - m_priority = WXTHREAD_DEFAULT_PRIORITY; + m_tid = kInvalidID; + m_state = STATE_NEW; + m_prio = WXTHREAD_DEFAULT_PRIORITY; + m_notifyQueueId = kInvalidID; + m_exitcode = 0; + m_cancelled = FALSE ; + + // set to TRUE only when the thread starts waiting on m_semSuspend + m_isPaused = FALSE; + + // defaults for joinable threads + m_shouldBeJoined = TRUE; + m_isDetached = FALSE; } + ~wxThreadInternal() + { + if ( m_notifyQueueId) + { + MPDeleteQueue( m_notifyQueueId); + m_notifyQueueId = kInvalidID ; + } + } + + // thread function + static OSStatus MacThreadStart(void* arg); // create a new (suspended) thread (for the given thread object) - bool Create(wxThread *thread); + bool Create(wxThread *thread, unsigned int stackSize); + + // 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(); + // go to sleep until Resume() is called + void Pause(); + // resume the thread + void Resume(); + + // accessors + // priority + int GetPriority() const { return m_prio; } + void SetPriority(int prio) ; + // state + wxThreadState GetState() const { return m_state; } + void SetState(wxThreadState state) { m_state = state; } - // suspend/resume/terminate - bool Suspend(); - bool Resume(); - void Cancel() { m_state = STATE_CANCELED; } + // Get the ID of this thread's underlying MP Services task. + MPTaskID GetId() const { return m_tid; } - // thread state - void SetState(wxThreadState state) { m_state = state; } - wxThreadState GetState() const { return m_state; } + void SetCancelFlag() { m_cancelled = TRUE; } + bool WasCancelled() const { return m_cancelled; } + // exit code + void SetExitCode(wxThread::ExitCode exitcode) { m_exitcode = exitcode; } + wxThread::ExitCode GetExitCode() const { return m_exitcode; } - // thread priority - void SetPriority(unsigned int priority) { m_priority = priority; } - unsigned int GetPriority() const { return m_priority; } + // the pause flag + void SetReallyPaused(bool paused) { m_isPaused = paused; } + bool IsReallyPaused() const { return m_isPaused; } - // thread handle and id - HANDLE GetHandle() const { return m_hThread; } - DWORD GetId() const { return m_tid; } + // tell the thread that it is a detached one + void Detach() + { + wxCriticalSectionLocker lock(m_csJoinFlag); - // thread function - static DWORD WinThreadStart(wxThread *thread); + m_shouldBeJoined = FALSE; + m_isDetached = TRUE; + } private: - Handle m_hThread; // handle of the thread - wxThreadState m_state; // state, see wxThreadState enum - unsigned int m_priority; // thread priority in "wx" units - ThreadId m_tid; // thread id + // the thread we're associated with + wxThread * m_thread; + + MPTaskID m_tid; // thread id + MPQueueID m_notifyQueueId; // its notification queue + + wxThreadState m_state; // see wxThreadState enum + int m_prio; // in wxWidgets units: from 0 to 100 + + // this flag is set when the thread should terminate + bool m_cancelled; + + // this flag is set when the thread is blocking on m_semSuspend + bool m_isPaused; + + // the thread exit code - only used for joinable (!detached) threads and + // is only valid after the thread termination + wxThread::ExitCode m_exitcode; + + // many threads may call Wait(), but only one of them should call + // pthread_join(), so we have to keep track of this + wxCriticalSection m_csJoinFlag; + bool m_shouldBeJoined; + bool m_isDetached; + + // this semaphore is posted by Run() and the threads Entry() is not + // called before it is done + wxSemaphore m_semRun; + + // this one is signaled when the thread should resume after having been + // Pause()d + wxSemaphore m_semSuspend; }; -DWORD wxThreadInternal::WinThreadStart(wxThread *thread) +OSStatus wxThreadInternal::MacThreadStart(void *parameter) { - // store the thread object in the TLS - if ( !::TlsSetValue(s_tlsThisThread, thread) ) + wxThread* thread = (wxThread*) parameter ; + wxThreadInternal *pthread = thread->m_internal; + + // add to TLS so that This() will work + verify_noerr( MPSetTaskStorageValue( gs_tlsForWXThread , (long) thread ) ) ; + + // have to declare this before pthread_cleanup_push() which defines a + // block! + bool dontRunAtAll; + + // wait for the semaphore to be posted from Run() + pthread->m_semRun.Wait(); + + // test whether we should run the run at all - may be it was deleted + // before it started to Run()? + { + wxCriticalSectionLocker lock(thread->m_critsect); + + dontRunAtAll = pthread->GetState() == STATE_NEW && + pthread->WasCancelled(); + } + + if ( !dontRunAtAll ) { - wxLogSysError(_("Can not start thread: error writing TLS.")); + pthread->m_exitcode = thread->Entry(); - return (DWORD)-1; + { + wxCriticalSectionLocker lock(thread->m_critsect); + pthread->SetState(STATE_EXITED); + } } + + if ( dontRunAtAll ) + { + if ( pthread->m_isDetached ) + delete thread; - DWORD ret = (DWORD)thread->Entry(); - thread->p_internal->SetState(STATE_EXITED); - thread->OnExit(); + return -1 ; + } + else + { + // on mac for the running code the correct thread termination is to + // return - delete thread; + // terminate the thread + thread->Exit(pthread->m_exitcode); - return ret; + return (OSStatus) NULL ; // pthread->m_exitcode; + } } -bool wxThreadInternal::Create(wxThread *thread) +bool wxThreadInternal::Create(wxThread *thread, unsigned int stackSize) { - m_hThread = ::CreateThread - ( - NULL, // default security - 0, // default stack size - (LPTHREAD_START_ROUTINE) // thread entry point - wxThreadInternal::WinThreadStart, // - (LPVOID)thread, // parameter - CREATE_SUSPENDED, // flags - &m_tid // [out] thread id - ); + wxASSERT_MSG( m_state == STATE_NEW && !m_tid, + _T("Create()ing thread twice?") ); - if ( m_hThread == NULL ) + OSStatus err = noErr ; + m_thread = thread; + + if ( m_notifyQueueId == kInvalidID ) { - wxLogSysError(_("Can't create thread")); - - return FALSE; + OSStatus err = MPCreateQueue( & m_notifyQueueId); + if( err) + { + wxLogSysError(_("Cant create the thread event queue")); + return false; + } } - - // translate wxWindows priority to the Windows one - int win_priority; - if (m_priority <= 20) - win_priority = THREAD_PRIORITY_LOWEST; - else if (m_priority <= 40) - win_priority = THREAD_PRIORITY_BELOW_NORMAL; - else if (m_priority <= 60) - win_priority = THREAD_PRIORITY_NORMAL; - else if (m_priority <= 80) - win_priority = THREAD_PRIORITY_ABOVE_NORMAL; - else if (m_priority <= 100) - win_priority = THREAD_PRIORITY_HIGHEST; - else + + m_state = STATE_NEW; + + err = MPCreateTask( MacThreadStart, + (void*) m_thread, + stackSize, + m_notifyQueueId, + &m_exitcode, + 0, + 0, + &m_tid); + + if ( err) { - wxFAIL_MSG(wxT("invalid value of thread priority parameter")); - win_priority = THREAD_PRIORITY_NORMAL; + wxLogSysError(_("Can't create thread")); + return false; } + + if ( m_prio != WXTHREAD_DEFAULT_PRIORITY ) + { + SetPriority(m_prio); + } + + return true; +} - if ( ::SetThreadPriority(m_hThread, win_priority) == 0 ) +void wxThreadInternal::SetPriority( int priority) +{ + m_prio = priority; + + if ( m_tid) { - wxLogSysError(_("Can't set thread priority")); + // Mac priorities range from 1 to 10,000, with a default of 100. + // wxWidgets priorities range from 0 to 100 with a default of 50. + // We can map wxWidgets to Mac priorities easily by assuming + // the former uses a logarithmic scale. + const unsigned int macPriority = ( int)( exp( priority / 25.0 * log( 10.0)) + 0.5); + + MPSetTaskWeight( m_tid, macPriority); } +} + +wxThreadError wxThreadInternal::Run() +{ + wxCHECK_MSG( GetState() == STATE_NEW, wxTHREAD_RUNNING, + wxT("thread may only be started once after Create()") ); - return TRUE; + SetState(STATE_RUNNING); + + // wake up threads waiting for our start + SignalRun(); + + return wxTHREAD_NO_ERROR; } -bool wxThreadInternal::Suspend() +void wxThreadInternal::Wait() { - DWORD nSuspendCount = ::SuspendThread(m_hThread); - if ( nSuspendCount == (DWORD)-1 ) + 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() ) + { + // give the thread we're waiting for chance to do the GUI call + // it might be in, we don't do this conditionally as the to be waited on + // thread might have to acquire the mutex later but before terminating + if ( wxGuiOwnedByMainThread() ) + { + wxMutexGuiLeave(); + } + } + { - wxLogSysError(_("Can not suspend thread %x"), m_hThread); + wxCriticalSectionLocker lock(m_csJoinFlag); + + if ( m_shouldBeJoined ) + { + void * param1; + void * param2; + void * rc; + + OSStatus err = MPWaitOnQueue ( m_notifyQueueId, + & param1, + & param2, + & rc, + kDurationForever); + if ( err) + { + wxLogSysError( _( "Cannot wait for thread termination.")); + rc = (void*) -1; + } + + // actually param1 would be the address of m_exitcode + // but we don't need this here + m_exitcode = rc; - return FALSE; + m_shouldBeJoined = FALSE; + } } +} - m_state = STATE_PAUSED; +void wxThreadInternal::Pause() +{ + // the state is set from the thread which pauses us first, this function + // is called later so the state should have been already set + wxCHECK_RET( m_state == STATE_PAUSED, + wxT("thread must first be paused with wxThread::Pause().") ); - return TRUE; + // wait until the semaphore is Post()ed from Resume() + m_semSuspend.Wait(); } -bool wxThreadInternal::Resume() +void wxThreadInternal::Resume() { - DWORD nSuspendCount = ::ResumeThread(m_hThread); - if ( nSuspendCount == (DWORD)-1 ) + wxCHECK_RET( m_state == STATE_PAUSED, + wxT("can't resume thread which is not suspended.") ); + + // the thread might be not actually paused yet - if there were no call to + // TestDestroy() since the last call to Pause() for example + if ( IsReallyPaused() ) { - wxLogSysError(_("Can not resume thread %x"), m_hThread); + // wake up Pause() + m_semSuspend.Post(); - return FALSE; + // reset the flag + SetReallyPaused(FALSE); } - m_state = STATE_RUNNING; - - return TRUE; + SetState(STATE_RUNNING); } // static functions @@ -419,270 +1219,394 @@ bool wxThreadInternal::Resume() wxThread *wxThread::This() { - wxThread *thread = (wxThread *)::TlsGetValue(s_tlsThisThread); - - // be careful, 0 may be a valid return value as well - if ( !thread && (::GetLastError() != NO_ERROR) ) - { - wxLogSysError(_("Couldn't get the current thread pointer")); - - // return NULL... - } - - return thread; + wxThread* thr = (wxThread*) MPGetTaskStorageValue( gs_tlsForWXThread ) ; + return thr; } bool wxThread::IsMain() { - return ::GetCurrentThreadId() == s_idMainThread; + return GetCurrentId() == gs_idMainThread || gs_idMainThread == kInvalidID ; } #ifdef Yield - #undef Yield +#undef Yield #endif void wxThread::Yield() { - // 0 argument to Sleep() is special - ::Sleep(0); +#if TARGET_API_MAC_OSX + CFRunLoopRunInMode( kCFRunLoopDefaultMode , 0 , true ) ; +#endif + MPYield(); } + void wxThread::Sleep(unsigned long milliseconds) { - ::Sleep(milliseconds); + AbsoluteTime wakeup = AddDurationToAbsolute( milliseconds, UpTime()); + MPDelayUntil( & wakeup); } -// create/start thread -// ------------------- -wxThreadError wxThread::Create() +int wxThread::GetCPUCount() { - if ( !p_internal->Create(this) ) - return wxTHREAD_NO_RESOURCE; + return MPProcessors(); +} - return wxTHREAD_NO_ERROR; +unsigned long wxThread::GetCurrentId() +{ + return (unsigned long)MPCurrentTaskID(); } -wxThreadError wxThread::Run() + +bool wxThread::SetConcurrency(size_t level) { - wxCriticalSectionLocker lock(m_critsect); + // Cannot be set in MacOS. + return false; +} + + +wxThread::wxThread(wxThreadKind kind) +{ + g_numberOfThreads++; + m_internal = new wxThreadInternal(); + + m_isDetached = (kind == wxTHREAD_DETACHED); +} + +wxThread::~wxThread() +{ + wxASSERT_MSG( g_numberOfThreads>0 , wxT("More threads deleted than created.") ) ; + g_numberOfThreads--; + +#ifdef __WXDEBUG__ + m_critsect.Enter(); - if ( p_internal->GetState() != STATE_NEW ) + // check that the thread either exited or couldn't be created + if ( m_internal->GetState() != STATE_EXITED && + m_internal->GetState() != STATE_NEW ) { - // actually, it may be almost any state at all, not only STATE_RUNNING - return wxTHREAD_RUNNING; + wxLogDebug(_T("The thread %ld is being destroyed although it is still running! The application may crash."), GetId()); } - return Resume(); + m_critsect.Leave(); +#endif // __WXDEBUG__ + + wxDELETE( m_internal ) ; +} + + +wxThreadError wxThread::Create(unsigned int stackSize) +{ + wxCriticalSectionLocker lock(m_critsect); + + if ( m_isDetached ) + { + m_internal->Detach() ; + } + if ( m_internal->Create(this, stackSize) == false ) + { + m_internal->SetState(STATE_EXITED); + return wxTHREAD_NO_RESOURCE; + } + + return wxTHREAD_NO_ERROR; } -// suspend/resume thread -// --------------------- +wxThreadError wxThread::Run() +{ + wxCriticalSectionLocker lock(m_critsect); + + wxCHECK_MSG( m_internal->GetId(), wxTHREAD_MISC_ERROR, + wxT("must call wxThread::Create() first") ); + + return m_internal->Run(); +} + +// ----------------------------------------------------------------------------- +// pause/resume +// ----------------------------------------------------------------------------- wxThreadError wxThread::Pause() { + wxCHECK_MSG( This() != this, wxTHREAD_MISC_ERROR, + _T("a thread can't pause itself") ); + wxCriticalSectionLocker lock(m_critsect); - return p_internal->Suspend() ? wxTHREAD_NO_ERROR : wxTHREAD_MISC_ERROR; + if ( m_internal->GetState() != STATE_RUNNING ) + { + wxLogDebug(wxT("Can't pause thread which is not running.")); + + return wxTHREAD_NOT_RUNNING; + } + + // just set a flag, the thread will be really paused only during the next + // call to TestDestroy() + m_internal->SetState(STATE_PAUSED); + + return wxTHREAD_NO_ERROR; } wxThreadError wxThread::Resume() { + wxCHECK_MSG( This() != this, wxTHREAD_MISC_ERROR, + _T("a thread can't resume itself") ); + wxCriticalSectionLocker lock(m_critsect); - return p_internal->Resume() ? wxTHREAD_NO_ERROR : wxTHREAD_MISC_ERROR; + wxThreadState state = m_internal->GetState(); + + switch ( state ) + { + case STATE_PAUSED: + m_internal->Resume(); + return wxTHREAD_NO_ERROR; + case STATE_EXITED: + return wxTHREAD_NO_ERROR; + + default: + wxLogDebug(_T("Attempt to resume a thread which is not paused.")); + + return wxTHREAD_MISC_ERROR; + } } -// stopping thread -// --------------- +// ----------------------------------------------------------------------------- +// exiting thread +// ----------------------------------------------------------------------------- -wxThread::ExitCode wxThread::Delete() +wxThread::ExitCode wxThread::Wait() { - ExitCode rc = 0; + wxCHECK_MSG( This() != this, (ExitCode)-1, + _T("a thread can't wait for itself") ); - // Delete() is always safe to call, so consider all possible states - if ( IsPaused() ) - Resume(); + wxCHECK_MSG( !m_isDetached, (ExitCode)-1, + _T("can't wait for detached thread") ); - if ( IsRunning() ) - { - if ( IsMain() ) - { - // set flag for wxIsWaitingForThread() - s_waitingForThread = TRUE; + m_internal->Wait(); - wxBeginBusyCursor(); - } + return m_internal->GetExitCode(); +} - HANDLE hThread; - { - wxCriticalSectionLocker lock(m_critsect); +wxThreadError wxThread::Delete(ExitCode *rc) +{ + wxCHECK_MSG( This() != this, wxTHREAD_MISC_ERROR, + _T("a thread can't delete itself") ); - p_internal->Cancel(); - hThread = p_internal->GetHandle(); - } + bool isDetached = m_isDetached; - // we can't just wait for the thread to terminate because it might be - // calling some GUI functions and so it will never terminate before we - // process the Windows messages that result from these functions - DWORD result; - do - { - result = ::MsgWaitForMultipleObjects - ( - 1, // number of objects to wait for - &hThread, // the objects - FALSE, // don't wait for all objects - INFINITE, // no timeout - QS_ALLEVENTS // return as soon as there are any events - ); - - switch ( result ) - { - case 0xFFFFFFFF: - // error - wxLogSysError(_("Can not wait for thread termination")); - Kill(); - return (ExitCode)-1; - - case WAIT_OBJECT_0: - // thread we're waiting for terminated - break; - - case WAIT_OBJECT_0 + 1: - // new message arrived, process it - if ( !wxTheApp->DoMessage() ) - { - // WM_QUIT received: kill the thread - Kill(); - - return (ExitCode)-1; - } - - if ( IsMain() ) - { - // give the thread we're waiting for chance to exit - // from the GUI call it might have been in - if ( (s_nWaitingForGui > 0) && wxGuiOwnedByMainThread() ) - { - wxMutexGuiLeave(); - } - } - - break; - - default: - wxFAIL_MSG(wxT("unexpected result of MsgWaitForMultipleObject")); - } - } while ( result != WAIT_OBJECT_0 ); + m_critsect.Enter(); + wxThreadState state = m_internal->GetState(); - if ( IsMain() ) - { - s_waitingForThread = FALSE; + // ask the thread to stop + m_internal->SetCancelFlag(); - wxEndBusyCursor(); - } + m_critsect.Leave(); - if ( !::GetExitCodeThread(hThread, (LPDWORD)&rc) ) - { - wxLogLastError("GetExitCodeThread"); + switch ( state ) + { + case STATE_NEW: + // we need to wake up the thread so that PthreadStart() will + // terminate - right now it's blocking on run semaphore in + // PthreadStart() + m_internal->SignalRun(); - rc = (ExitCode)-1; - } + // fall through + + case STATE_EXITED: + // nothing to do + break; + + case STATE_PAUSED: + // resume the thread first + m_internal->Resume(); - wxASSERT_MSG( (LPVOID)rc != (LPVOID)STILL_ACTIVE, - wxT("thread must be already terminated.") ); + // fall through - ::CloseHandle(hThread); + default: + if ( !isDetached ) + { + // wait until the thread stops + m_internal->Wait(); + + if ( rc ) + { + // return the exit code of the thread + *rc = m_internal->GetExitCode(); + } + } } - return rc; + return wxTHREAD_NO_ERROR; } wxThreadError wxThread::Kill() { - if ( !IsRunning() ) - return wxTHREAD_NOT_RUNNING; + wxCHECK_MSG( This() != this, wxTHREAD_MISC_ERROR, + _T("a thread can't kill itself") ); - if ( !::TerminateThread(p_internal->GetHandle(), (DWORD)-1) ) + switch ( m_internal->GetState() ) { - wxLogSysError(_("Couldn't terminate thread")); + case STATE_NEW: + case STATE_EXITED: + return wxTHREAD_NOT_RUNNING; - return wxTHREAD_MISC_ERROR; - } + case STATE_PAUSED: + // resume the thread first + Resume(); - delete this; + // fall through - return wxTHREAD_NO_ERROR; + default: + OSStatus err = MPTerminateTask( m_internal->GetId() , -1 ) ; + if ( err ) + { + wxLogError(_("Failed to terminate a thread.")); + + return wxTHREAD_MISC_ERROR; + } + + if ( m_isDetached ) + { + delete this ; + } + else + { + // this should be retrieved by Wait actually + m_internal->SetExitCode((void*)-1); + } + + return wxTHREAD_NO_ERROR; + } +} + +void wxThread::Exit(ExitCode status) +{ + wxASSERT_MSG( This() == this, + _T("wxThread::Exit() can only be called in the context of the same thread") ); + + // don't enter m_critsect before calling OnExit() because the user code + // might deadlock if, for example, it signals a condition in OnExit() (a + // common case) while the main thread calls any of functions entering + // m_critsect on us (almost all of them do) + OnExit(); + + MPTaskID threadid = m_internal->GetId() ; + + if ( IsDetached() ) + { + delete this; + } + else // joinable + { + // update the status of the joinable thread + wxCriticalSectionLocker lock(m_critsect); + m_internal->SetState(STATE_EXITED); + } + MPTerminateTask( threadid , (long) status) ; } -void wxThread::Exit(void *status) +// also test whether we were paused +bool wxThread::TestDestroy() { - delete this; + wxASSERT_MSG( This() == this, + _T("wxThread::TestDestroy() can only be called in the context of the same thread") ); - ::ExitThread((DWORD)status); + m_critsect.Enter(); - wxFAIL_MSG(wxT("Couldn't return from ExitThread()!")); + if ( m_internal->GetState() == STATE_PAUSED ) + { + m_internal->SetReallyPaused(TRUE); + + // leave the crit section or the other threads will stop too if they + // try to call any of (seemingly harmless) IsXXX() functions while we + // sleep + m_critsect.Leave(); + + m_internal->Pause(); + } + else + { + // thread wasn't requested to pause, nothing to do + m_critsect.Leave(); + } + + return m_internal->WasCancelled(); } +// ----------------------------------------------------------------------------- +// priority setting +// ----------------------------------------------------------------------------- + void wxThread::SetPriority(unsigned int prio) { + wxCHECK_RET( ((int)WXTHREAD_MIN_PRIORITY <= (int)prio) && + ((int)prio <= (int)WXTHREAD_MAX_PRIORITY), + wxT("invalid thread priority") ); + wxCriticalSectionLocker lock(m_critsect); - p_internal->SetPriority(prio); + switch ( m_internal->GetState() ) + { + case STATE_RUNNING: + case STATE_PAUSED: + case STATE_NEW: + // thread not yet started, priority will be set when it is + m_internal->SetPriority(prio); + break; + + case STATE_EXITED: + default: + wxFAIL_MSG(wxT("impossible to set thread priority in this state")); + } } unsigned int wxThread::GetPriority() const { - wxCriticalSectionLocker lock((wxCriticalSection &)m_critsect); + wxCriticalSectionLocker lock((wxCriticalSection &)m_critsect); // const_cast - return p_internal->GetPriority(); + return m_internal->GetPriority(); } -unsigned long wxThread::GetID() const +unsigned long wxThread::GetId() const { - wxCriticalSectionLocker lock((wxCriticalSection &)m_critsect); + wxCriticalSectionLocker lock((wxCriticalSection &)m_critsect); // const_cast - return (unsigned long)p_internal->GetId(); + return (unsigned long)m_internal->GetId(); } +// ----------------------------------------------------------------------------- +// state tests +// ----------------------------------------------------------------------------- + bool wxThread::IsRunning() const { wxCriticalSectionLocker lock((wxCriticalSection &)m_critsect); - return p_internal->GetState() == STATE_RUNNING; + return m_internal->GetState() == STATE_RUNNING; } bool wxThread::IsAlive() const { - wxCriticalSectionLocker lock((wxCriticalSection &)m_critsect); - - return (p_internal->GetState() == STATE_RUNNING) || - (p_internal->GetState() == STATE_PAUSED); -} + wxCriticalSectionLocker lock((wxCriticalSection&)m_critsect); -bool wxThread::IsPaused() const -{ - wxCriticalSectionLocker lock((wxCriticalSection &)m_critsect); + switch ( m_internal->GetState() ) + { + case STATE_RUNNING: + case STATE_PAUSED: + return TRUE; - return (p_internal->GetState() == STATE_PAUSED); + default: + return FALSE; + } } -bool wxThread::TestDestroy() +bool wxThread::IsPaused() const { - wxCriticalSectionLocker lock((wxCriticalSection &)m_critsect); + wxCriticalSectionLocker lock((wxCriticalSection&)m_critsect); - return p_internal->GetState() == STATE_CANCELED; -} - -wxThread::wxThread() -{ - p_internal = new wxThreadInternal(); -} - -wxThread::~wxThread() -{ - delete p_internal; + return (m_internal->GetState() == STATE_PAUSED); } // ---------------------------------------------------------------------------- @@ -692,78 +1616,63 @@ wxThread::~wxThread() class wxThreadModule : public wxModule { public: - virtual bool OnInit(); - virtual void OnExit(); - + virtual bool OnInit(); + virtual void OnExit(); + private: - DECLARE_DYNAMIC_CLASS(wxThreadModule) + DECLARE_DYNAMIC_CLASS(wxThreadModule) }; IMPLEMENT_DYNAMIC_CLASS(wxThreadModule, wxModule) bool wxThreadModule::OnInit() { - // allocate TLS index for storing the pointer to the current thread - s_tlsThisThread = ::TlsAlloc(); - if ( s_tlsThisThread == 0xFFFFFFFF ) - { - // in normal circumstances it will only happen if all other - // TLS_MINIMUM_AVAILABLE (>= 64) indices are already taken - in other - // words, this should never happen - wxLogSysError(_("Thread module initialization failed: " - "impossible to allocate index in thread " - "local storage")); - - return FALSE; - } - - // main thread doesn't have associated wxThread object, so store 0 in the - // TLS instead - if ( !::TlsSetValue(s_tlsThisThread, (LPVOID)0) ) + bool hasThreadManager = false ; + hasThreadManager = MPLibraryIsLoaded(); + + if ( !hasThreadManager ) { - ::TlsFree(s_tlsThisThread); - s_tlsThisThread = 0xFFFFFFFF; - - wxLogSysError(_("Thread module initialization failed: " - "can not store value in thread local storage")); - - return FALSE; + wxLogError( _("MP Thread Support is not available on this System" ) ) ; + return FALSE ; } - - s_critsectWaitingForGui = new wxCriticalSection(); - - s_critsectGui = new wxCriticalSection(); - s_critsectGui->Enter(); - - // no error return for GetCurrentThreadId() - s_idMainThread = ::GetCurrentThreadId(); - - return TRUE; + + verify_noerr( MPAllocateTaskStorageIndex( &gs_tlsForWXThread ) ) ; + // main thread's This() is NULL + verify_noerr( MPSetTaskStorageValue( gs_tlsForWXThread , 0 ) ) ; + + gs_idMainThread = wxThread::GetCurrentId() ; + + gs_critsectWaitingForGui = new wxCriticalSection(); + + gs_critsectGui = new wxCriticalSection(); + gs_critsectGui->Enter(); + + return TRUE; } void wxThreadModule::OnExit() { - if ( !::TlsFree(s_tlsThisThread) ) + if ( gs_critsectGui ) { - wxLogLastError("TlsFree failed."); - } - - if ( s_critsectGui ) - { - s_critsectGui->Leave(); - delete s_critsectGui; - s_critsectGui = NULL; + if ( !wxGuiOwnedByMainThread() ) + { + gs_critsectGui->Enter(); + gs_bGuiOwnedByMainThread = true; + } + gs_critsectGui->Leave(); + delete gs_critsectGui; + gs_critsectGui = NULL; } - wxDELETE(s_critsectWaitingForGui); + delete gs_critsectWaitingForGui; + gs_critsectWaitingForGui = NULL; } // ---------------------------------------------------------------------------- -// under Windows, these functions are implemented usign a critical section and -// not a mutex, so the names are a bit confusing +// GUI Serialization copied from MSW implementation // ---------------------------------------------------------------------------- -void WXDLLEXPORT wxMutexGuiEnter() +void WXDLLIMPEXP_BASE wxMutexGuiEnter() { // this would dead lock everything... wxASSERT_MSG( !wxThread::IsMain(), @@ -773,56 +1682,59 @@ void WXDLLEXPORT wxMutexGuiEnter() // set the flag telling to the main thread that we want to do some GUI { - wxCriticalSectionLocker enter(*s_critsectWaitingForGui); + wxCriticalSectionLocker enter(*gs_critsectWaitingForGui); - s_nWaitingForGui++; + gs_nWaitingForGui++; } wxWakeUpMainThread(); // now we may block here because the main thread will soon let us in // (during the next iteration of OnIdle()) - s_critsectGui->Enter(); + gs_critsectGui->Enter(); } -void WXDLLEXPORT wxMutexGuiLeave() +void WXDLLIMPEXP_BASE wxMutexGuiLeave() { - wxCriticalSectionLocker enter(*s_critsectWaitingForGui); + wxCriticalSectionLocker enter(*gs_critsectWaitingForGui); if ( wxThread::IsMain() ) { - s_bGuiOwnedByMainThread = FALSE; + gs_bGuiOwnedByMainThread = false; } else { - // decrement the number of waiters now - wxASSERT_MSG( s_nWaitingForGui > 0, + // decrement the number of threads waiting for GUI access now + wxASSERT_MSG( gs_nWaitingForGui > 0, wxT("calling wxMutexGuiLeave() without entering it first?") ); - s_nWaitingForGui--; + gs_nWaitingForGui--; wxWakeUpMainThread(); } - s_critsectGui->Leave(); + gs_critsectGui->Leave(); } -void WXDLLEXPORT wxMutexGuiLeaveOrEnter() +void WXDLLIMPEXP_BASE wxMutexGuiLeaveOrEnter() { wxASSERT_MSG( wxThread::IsMain(), wxT("only main thread may call wxMutexGuiLeaveOrEnter()!") ); - wxCriticalSectionLocker enter(*s_critsectWaitingForGui); + if( !gs_critsectWaitingForGui ) + return; + + wxCriticalSectionLocker enter(*gs_critsectWaitingForGui); - if ( s_nWaitingForGui == 0 ) + if ( gs_nWaitingForGui == 0 ) { // no threads are waiting for GUI - so we may acquire the lock without // any danger (but only if we don't already have it) if ( !wxGuiOwnedByMainThread() ) { - s_critsectGui->Enter(); + gs_critsectGui->Enter(); - s_bGuiOwnedByMainThread = TRUE; + gs_bGuiOwnedByMainThread = true; } //else: already have it, nothing to do } @@ -837,26 +1749,21 @@ void WXDLLEXPORT wxMutexGuiLeaveOrEnter() } } -bool WXDLLEXPORT wxGuiOwnedByMainThread() +bool WXDLLIMPEXP_BASE wxGuiOwnedByMainThread() { - return s_bGuiOwnedByMainThread; + return gs_bGuiOwnedByMainThread; } -// wake up the main thread if it's in ::GetMessage() +// wake up the main thread void WXDLLEXPORT wxWakeUpMainThread() { - // sending any message would do - hopefully WM_NULL is harmless enough - if ( !::PostThreadMessage(s_idMainThread, WM_NULL, 0, 0) ) - { - // should never happen - wxLogLastError("PostThreadMessage(WM_NULL)"); - } + wxMacWakeUp() ; } -bool WXDLLEXPORT wxIsWaitingForThread() -{ - return s_waitingForThread; -} -*/ +// ---------------------------------------------------------------------------- +// include common implementation code +// ---------------------------------------------------------------------------- + +#include "wx/thrimpl.cpp" #endif // wxUSE_THREADS