]> git.saurik.com Git - wxWidgets.git/blobdiff - src/mac/carbon/thread.cpp
Do not emit a wxEVT_COMMAND_TEXT_UPDATED when the tab key is lifted (otherwise each...
[wxWidgets.git] / src / mac / carbon / thread.cpp
index a2fb9e43eba20798b90abc79c89b8800a158efdb..6809af50f6e8f69d7663bfcdd3d92d556e4413b6 100644 (file)
 #else
 #include <DriverServices.h>
 #include <Multiprocessing.h>
 #else
 #include <DriverServices.h>
 #include <Multiprocessing.h>
-#include <math.h>
+#include "wx/math.h"
 #endif
 #include "wx/mac/uma.h"
 #endif
 
 #endif
 #include "wx/mac/uma.h"
 #endif
 
+#include "wx/mac/macnotfy.h"
+
 // ----------------------------------------------------------------------------
 // constants
 // ----------------------------------------------------------------------------
 // ----------------------------------------------------------------------------
 // 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.
     
     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
     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 
 */
 
 
     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
 // ----------------------------------------------------------------------------
 
 // ----------------------------------------------------------------------------
 // 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
 {
 
 class wxMutexInternal
 {
@@ -170,6 +371,7 @@ wxMutexInternal::~wxMutexInternal()
 {
     if ( m_semaphore != kInvalidID )
            MPDeleteSemaphore( m_semaphore);
 {
     if ( m_semaphore != kInvalidID )
            MPDeleteSemaphore( m_semaphore);
+       MPYield() ;
 }
 
 wxMutexError wxMutexInternal::Lock()
 }
 
 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);
 {
     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;        
     }
        if ( err)
     {
                wxLogSysError(_("Could not unlock mutex"));
                return wxMUTEX_MISC_ERROR;        
     }
-    
        return wxMUTEX_NO_ERROR;
 }
 
        return wxMUTEX_NO_ERROR;
 }
 
-#else
+#endif
+
+#if wxUSE_MAC_CRITICAL_REGION_MUTEX
 
 class wxMutexInternal
 {
 
 class wxMutexInternal
 {
@@ -248,6 +452,7 @@ wxMutexInternal::~wxMutexInternal()
 {
     if ( m_critRegion != kInvalidID )
            MPDeleteCriticalRegion( m_critRegion);
 {
     if ( m_critRegion != kInvalidID )
            MPDeleteCriticalRegion( m_critRegion);
+       MPYield() ;
 }
 
 wxMutexError wxMutexInternal::Lock()
 }
 
 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);
 {
     wxCHECK_MSG( m_isOk , wxMUTEX_MISC_ERROR , wxT("Invalid Mutex") ) ;
        OSStatus err = MPExitCriticalRegion( m_critRegion);
+       MPYield() ;
        if ( err)
     {
                wxLogSysError(_("Could not unlock mutex"));
        if ( err)
     {
                wxLogSysError(_("Could not unlock mutex"));
@@ -345,6 +551,7 @@ wxSemaphoreInternal::~wxSemaphoreInternal()
 {
     if( m_semaphore != kInvalidID )
            MPDeleteSemaphore( m_semaphore);
 {
     if( m_semaphore != kInvalidID )
            MPDeleteSemaphore( m_semaphore);
+       MPYield() ;
 }
 
 wxSemaError wxSemaphoreInternal::WaitTimeout(unsigned long milliseconds)
 }
 
 wxSemaError wxSemaphoreInternal::WaitTimeout(unsigned long milliseconds)
@@ -364,6 +571,7 @@ wxSemaError wxSemaphoreInternal::WaitTimeout(unsigned long milliseconds)
 wxSemaError wxSemaphoreInternal::Post()
 {
        OSStatus err = MPSignalSemaphore( m_semaphore);
 wxSemaError wxSemaphoreInternal::Post()
 {
        OSStatus err = MPSignalSemaphore( m_semaphore);
+       MPYield() ;
        if ( err)
     {
                return wxSEMA_MISC_ERROR;
        if ( err)
     {
                return wxSEMA_MISC_ERROR;
@@ -778,7 +986,7 @@ private:
        MPQueueID           m_notifyQueueId;    // its notification queue
 
     wxThreadState m_state;              // see wxThreadState enum
        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;
 
     // 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.
        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);
                
                // 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() )
     // 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);
 
     {
         wxCriticalSectionLocker lock(m_csJoinFlag);
@@ -966,10 +1182,6 @@ void wxThreadInternal::Wait()
             m_shouldBeJoined = FALSE;
         }
     }
             m_shouldBeJoined = FALSE;
         }
     }
-
-    // reacquire GUI mutex
-    if ( wxThread::IsMain() )
-        wxMutexGuiEnter();
 }
 
 void wxThreadInternal::Pause()
 }
 
 void wxThreadInternal::Pause()
@@ -1013,7 +1225,7 @@ wxThread *wxThread::This()
 
 bool wxThread::IsMain()
 {
 
 bool wxThread::IsMain()
 {
-       return GetCurrentId() == gs_idMainThread;
+       return GetCurrentId() == gs_idMainThread || gs_idMainThread == kInvalidID ;
 }
 
 #ifdef Yield
 }
 
 #ifdef Yield
@@ -1224,7 +1436,6 @@ wxThreadError wxThread::Delete(ExitCode *rc)
                     *rc = m_internal->GetExitCode();
                 }
             }
                     *rc = m_internal->GetExitCode();
                 }
             }
-            //else: can't wait for detached threads
     }
 
     return wxTHREAD_NO_ERROR;
     }
 
     return wxTHREAD_NO_ERROR;
@@ -1281,7 +1492,7 @@ void wxThread::Exit(ExitCode status)
     // m_critsect on us (almost all of them do)
     OnExit();
 
     // m_critsect on us (almost all of them do)
     OnExit();
 
-    MPTerminateTask( m_internal->GetId() , (long) status) ;
+    MPTaskID threadid = m_internal->GetId() ;
     
     if ( IsDetached() )
     {
     
     if ( IsDetached() )
     {
@@ -1293,6 +1504,7 @@ void wxThread::Exit(ExitCode status)
         wxCriticalSectionLocker lock(m_critsect);
         m_internal->SetState(STATE_EXITED);
     }
         wxCriticalSectionLocker lock(m_critsect);
         m_internal->SetState(STATE_EXITED);
     }
+    MPTerminateTask( threadid , (long) status) ;    
 }
 
 // also test whether we were paused
 }
 
 // also test whether we were paused
@@ -1442,6 +1654,11 @@ void wxThreadModule::OnExit()
 {
     if ( gs_critsectGui )
     {
 {
     if ( gs_critsectGui )
     {
+        if ( !wxGuiOwnedByMainThread() )
+        {
+            gs_critsectGui->Enter();
+            gs_bGuiOwnedByMainThread = true;
+        }
         gs_critsectGui->Leave();
         delete gs_critsectGui;
         gs_critsectGui = NULL;
         gs_critsectGui->Leave();
         delete gs_critsectGui;
         gs_critsectGui = NULL;