]> git.saurik.com Git - wxWidgets.git/blobdiff - src/mac/carbon/thread.cpp
must be static of course, otherwise items just get appended for every menu-build
[wxWidgets.git] / src / mac / carbon / thread.cpp
index a2fb9e43eba20798b90abc79c89b8800a158efdb..cb8ecb4c2d523b4ef4540c070d309843ff65b583 100644 (file)
@@ -10,7 +10,7 @@
 // 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
 // ----------------------------------------------------------------------------
@@ -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 <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
 {
@@ -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);
@@ -955,7 +1171,7 @@ void wxThreadInternal::Wait()
                                 kDurationForever);
             if ( err)
             {
-                wxLogSysError( _( "Cannot wait on thread to exit."));
+                wxLogSysError( _( "Cannot wait for thread termination."));
                 rc = (void*) -1;
             }
 
@@ -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
@@ -1426,7 +1638,7 @@ bool wxThreadModule::OnInit()
        
        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() ;
        
@@ -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;
@@ -1504,6 +1721,9 @@ void WXDLLIMPEXP_BASE wxMutexGuiLeaveOrEnter()
     wxASSERT_MSG( wxThread::IsMain(),
                   wxT("only main thread may call wxMutexGuiLeaveOrEnter()!") );
 
+    if( !gs_critsectWaitingForGui )
+        return;
+        
     wxCriticalSectionLocker enter(*gs_critsectWaitingForGui);
 
     if ( gs_nWaitingForGui == 0 )