X-Git-Url: https://git.saurik.com/wxWidgets.git/blobdiff_plain/68ba3b21da83bad8fa95b3d7f9efc041df9a64b2..db36543353d37c2357e8e30aab53ff37337b1127:/src/mac/carbon/thread.cpp diff --git a/src/mac/carbon/thread.cpp b/src/mac/carbon/thread.cpp index b0d67a020c..6c477b3685 100644 --- a/src/mac/carbon/thread.cpp +++ b/src/mac/carbon/thread.cpp @@ -45,6 +45,8 @@ #include "wx/mac/uma.h" #endif +#include "wx/mac/macnotfy.h" + // ---------------------------------------------------------------------------- // constants // ---------------------------------------------------------------------------- @@ -112,21 +114,220 @@ MPCriticalRegionID gs_guiCritical = kInvalidID; to use two indices one for each 32 bit part as the MP implementation is limited to longs. - I have two implementations for mutexes : + 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 + 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 0 +#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: + 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; +}; + +#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) +{ + 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); + + 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_isOk = err == 0; + if ( !m_isOk ) + { + wxLogApiError( wxT("pthread_mutex_init()"), err); + } +} + +wxMutexInternal::~wxMutexInternal() +{ + if ( m_isOk ) + { + int err = pthread_mutex_destroy(&m_mutex); + if ( err != 0 ) + { + wxLogApiError( wxT("pthread_mutex_destroy()"), err); + } + } +} + +wxMutexError wxMutexInternal::Lock() +{ + 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; + + 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 EBUSY: + // not an error: mutex is already locked, but we're prepared for + // this + return wxMUTEX_BUSY; + + case EINVAL: + 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() +{ + int err = pthread_mutex_unlock(&m_mutex); + switch ( err ) + { + case EPERM: + // we don't own the mutex + return wxMUTEX_UNLOCKED; + + case EINVAL: + wxLogDebug(_T("pthread_mutex_unlock(): mutex not initialized.")); + break; + + case 0: + return wxMUTEX_NO_ERROR; + + default: + wxLogApiError(_T("pthread_mutex_unlock()"), err); + } + + return wxMUTEX_MISC_ERROR; +} + + +#endif + +#if wxUSE_MAC_SEMAPHORE_MUTEX class wxMutexInternal { @@ -170,6 +371,7 @@ wxMutexInternal::~wxMutexInternal() { if ( m_semaphore != kInvalidID ) MPDeleteSemaphore( m_semaphore); + MPYield() ; } wxMutexError wxMutexInternal::Lock() @@ -206,16 +408,18 @@ 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; } -#else +#endif + +#if wxUSE_MAC_CRITICAL_REGION_MUTEX class wxMutexInternal { @@ -248,6 +452,7 @@ wxMutexInternal::~wxMutexInternal() { if ( m_critRegion != kInvalidID ) MPDeleteCriticalRegion( m_critRegion); + MPYield() ; } wxMutexError wxMutexInternal::Lock() @@ -284,6 +489,7 @@ wxMutexError wxMutexInternal::Unlock() { wxCHECK_MSG( m_isOk , wxMUTEX_MISC_ERROR , wxT("Invalid Mutex") ) ; OSStatus err = MPExitCriticalRegion( m_critRegion); + MPYield() ; if ( err) { wxLogSysError(_("Could not unlock mutex")); @@ -345,6 +551,7 @@ wxSemaphoreInternal::~wxSemaphoreInternal() { if( m_semaphore != kInvalidID ) MPDeleteSemaphore( m_semaphore); + MPYield() ; } wxSemaError wxSemaphoreInternal::WaitTimeout(unsigned long milliseconds) @@ -364,6 +571,7 @@ wxSemaError wxSemaphoreInternal::WaitTimeout(unsigned long milliseconds) wxSemaError wxSemaphoreInternal::Post() { OSStatus err = MPSignalSemaphore( m_semaphore); + MPYield() ; if ( err) { return wxSEMA_MISC_ERROR; @@ -778,7 +986,7 @@ private: MPQueueID m_notifyQueueId; // its notification queue wxThreadState m_state; // see wxThreadState enum - int m_prio; // in wxWindows units: from 0 to 100 + int m_prio; // in wxWidgets units: from 0 to 100 // this flag is set when the thread should terminate bool m_cancelled; @@ -908,8 +1116,8 @@ void wxThreadInternal::SetPriority( int priority) if ( m_tid) { // Mac priorities range from 1 to 10,000, with a default of 100. - // wxWindows priorities range from 0 to 100 with a default of 50. - // We can map wxWindows to Mac priorities easily by assuming + // 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); @@ -937,7 +1145,15 @@ void wxThreadInternal::Wait() // 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(); + { + // give the thread we're waiting for chance to do the GUI call + // it might be in, we don't do this conditionally as the to be waited on + // thread might have to acquire the mutex later but before terminating + if ( wxGuiOwnedByMainThread() ) + { + wxMutexGuiLeave(); + } + } { wxCriticalSectionLocker lock(m_csJoinFlag); @@ -966,10 +1182,6 @@ void wxThreadInternal::Wait() m_shouldBeJoined = FALSE; } } - - // reacquire GUI mutex - if ( wxThread::IsMain() ) - wxMutexGuiEnter(); } void wxThreadInternal::Pause() @@ -1013,7 +1225,7 @@ wxThread *wxThread::This() bool wxThread::IsMain() { - return GetCurrentId() == gs_idMainThread; + return GetCurrentId() == gs_idMainThread || gs_idMainThread == kInvalidID ; } #ifdef Yield @@ -1224,7 +1436,6 @@ wxThreadError wxThread::Delete(ExitCode *rc) *rc = m_internal->GetExitCode(); } } - //else: can't wait for detached threads } return wxTHREAD_NO_ERROR; @@ -1281,7 +1492,7 @@ void wxThread::Exit(ExitCode status) // m_critsect on us (almost all of them do) OnExit(); - MPTerminateTask( m_internal->GetId() , (long) status) ; + MPTaskID threadid = m_internal->GetId() ; if ( IsDetached() ) { @@ -1293,6 +1504,7 @@ void wxThread::Exit(ExitCode status) wxCriticalSectionLocker lock(m_critsect); m_internal->SetState(STATE_EXITED); } + MPTerminateTask( threadid , (long) status) ; } // also test whether we were paused @@ -1420,7 +1632,7 @@ bool wxThreadModule::OnInit() if ( !hasThreadManager ) { - wxMessageBox( wxT("Error") , wxT("MP Thread Support is not available on this System" ), wxOK ) ; + wxLogError( _("MP Thread Support is not available on this System" ) ) ; return FALSE ; } @@ -1442,6 +1654,11 @@ void wxThreadModule::OnExit() { if ( gs_critsectGui ) { + if ( !wxGuiOwnedByMainThread() ) + { + gs_critsectGui->Enter(); + gs_bGuiOwnedByMainThread = true; + } gs_critsectGui->Leave(); delete gs_critsectGui; gs_critsectGui = NULL;