// Licence: wxWindows licence
/////////////////////////////////////////////////////////////////////////////
-#ifdef __GNUG__
+#if defined(__GNUG__) && !defined(NO_GCC_PRAGMA)
#pragma implementation "thread.h"
#endif
#else
#include <DriverServices.h>
#include <Multiprocessing.h>
-#include <math.h>
+#include "wx/math.h"
#endif
#include "wx/mac/uma.h"
#endif
+#include "wx/mac/macnotfy.h"
+
// ----------------------------------------------------------------------------
// constants
// ----------------------------------------------------------------------------
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 <pthread.h>
+
+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
{
{
if ( m_semaphore != kInvalidID )
MPDeleteSemaphore( m_semaphore);
+ MPYield() ;
}
wxMutexError wxMutexInternal::Lock()
{
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
{
{
if ( m_critRegion != kInvalidID )
MPDeleteCriticalRegion( m_critRegion);
+ MPYield() ;
}
wxMutexError wxMutexInternal::Lock()
{
wxCHECK_MSG( m_isOk , wxMUTEX_MISC_ERROR , wxT("Invalid Mutex") ) ;
OSStatus err = MPExitCriticalRegion( m_critRegion);
+ MPYield() ;
if ( err)
{
wxLogSysError(_("Could not unlock mutex"));
{
if( m_semaphore != kInvalidID )
MPDeleteSemaphore( m_semaphore);
+ MPYield() ;
}
wxSemaError wxSemaphoreInternal::WaitTimeout(unsigned long milliseconds)
wxSemaError wxSemaphoreInternal::Post()
{
OSStatus err = MPSignalSemaphore( m_semaphore);
+ MPYield() ;
if ( err)
{
return wxSEMA_MISC_ERROR;
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;
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);
// 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);
kDurationForever);
if ( err)
{
- wxLogSysError( _( "Cannot wait on thread to exit."));
+ wxLogSysError( _( "Cannot wait for thread termination."));
rc = (void*) -1;
}
m_shouldBeJoined = FALSE;
}
}
-
- // reacquire GUI mutex
- if ( wxThread::IsMain() )
- wxMutexGuiEnter();
}
void wxThreadInternal::Pause()
bool wxThread::IsMain()
{
- return GetCurrentId() == gs_idMainThread;
+ return GetCurrentId() == gs_idMainThread || gs_idMainThread == kInvalidID ;
}
#ifdef Yield
*rc = m_internal->GetExitCode();
}
}
- //else: can't wait for detached threads
}
return wxTHREAD_NO_ERROR;
// m_critsect on us (almost all of them do)
OnExit();
- MPTerminateTask( m_internal->GetId() , (long) status) ;
+ MPTaskID threadid = m_internal->GetId() ;
if ( IsDetached() )
{
wxCriticalSectionLocker lock(m_critsect);
m_internal->SetState(STATE_EXITED);
}
+ MPTerminateTask( threadid , (long) status) ;
}
// also test whether we were paused
verify_noerr( MPAllocateTaskStorageIndex( &gs_tlsForWXThread ) ) ;
// main thread's This() is NULL
- verify_noerr( MPSetTaskStorageValue( gs_tlsForWXThread , NULL ) ) ;
+ verify_noerr( MPSetTaskStorageValue( gs_tlsForWXThread , 0 ) ) ;
gs_idMainThread = wxThread::GetCurrentId() ;
{
if ( gs_critsectGui )
{
+ if ( !wxGuiOwnedByMainThread() )
+ {
+ gs_critsectGui->Enter();
+ gs_bGuiOwnedByMainThread = true;
+ }
gs_critsectGui->Leave();
delete gs_critsectGui;
gs_critsectGui = NULL;
wxASSERT_MSG( wxThread::IsMain(),
wxT("only main thread may call wxMutexGuiLeaveOrEnter()!") );
+ if( !gs_critsectWaitingForGui )
+ return;
+
wxCriticalSectionLocker enter(*gs_critsectWaitingForGui);
if ( gs_nWaitingForGui == 0 )