diff options
Diffstat (limited to 'macosx/tclMacOSXNotify.c')
-rw-r--r-- | macosx/tclMacOSXNotify.c | 78 |
1 files changed, 67 insertions, 11 deletions
diff --git a/macosx/tclMacOSXNotify.c b/macosx/tclMacOSXNotify.c index af2fa65..ecd78ae 100644 --- a/macosx/tclMacOSXNotify.c +++ b/macosx/tclMacOSXNotify.c @@ -13,7 +13,7 @@ * See the file "license.terms" for information on usage and redistribution of * this file, and for a DISCLAIMER OF ALL WARRANTIES. * - * RCS: @(#) $Id: tclMacOSXNotify.c,v 1.12 2007/01/19 01:03:59 das Exp $ + * RCS: @(#) $Id: tclMacOSXNotify.c,v 1.13 2007/03/07 23:43:13 das Exp $ */ #include "tclInt.h" @@ -149,11 +149,12 @@ static int triggerPipe = -1; static int receivePipe = -1; /* Output end of triggerPipe */ /* - * We use Darwin-native spinlocks instead of pthread mutexes for notifier - * locking: this radically simplifies the implementation and lowers overhead. - * Note that these are not pure spinlocks, they employ various strategies to - * back off, making them immune to most priority-inversion livelocks (c.f. man - * 3 OSSpinLockLock). + * We use the Darwin-native spinlock API rather than pthread mutexes for + * notifier locking: this radically simplifies the implementation and lowers + * overhead. Note that these are not pure spinlocks, they employ various + * strategies to back off and relinquish the processor, making them immune to + * most priority-inversion livelocks (c.f. 'man 3 OSSpinLockLock' and Darwin + * sources: xnu/osfmk/{ppc,i386}/commpage/spinlocks.s). */ #if defined(HAVE_LIBKERN_OSATOMIC_H) && defined(HAVE_OSSPINLOCKLOCK) @@ -173,27 +174,37 @@ static int receivePipe = -1; /* Output end of triggerPipe */ #else #define VOLATILE #endif +#ifndef bool +#define bool int +#endif extern void OSSpinLockLock(VOLATILE OSSpinLock *lock) WEAK_IMPORT_ATTRIBUTE; extern void OSSpinLockUnlock(VOLATILE OSSpinLock *lock) WEAK_IMPORT_ATTRIBUTE; +extern bool OSSpinLockTry(VOLATILE OSSpinLock *lock) WEAK_IMPORT_ATTRIBUTE; extern void _spin_lock(VOLATILE OSSpinLock *lock) WEAK_IMPORT_ATTRIBUTE; extern void _spin_unlock(VOLATILE OSSpinLock *lock) WEAK_IMPORT_ATTRIBUTE; +extern bool _spin_lock_try(VOLATILE OSSpinLock *lock) WEAK_IMPORT_ATTRIBUTE; static void (* lockLock)(VOLATILE OSSpinLock *lock) = NULL; static void (* lockUnlock)(VOLATILE OSSpinLock *lock) = NULL; +static bool (* lockTry)(VOLATILE OSSpinLock *lock) = NULL; #undef VOLATILE static pthread_once_t spinLockLockInitControl = PTHREAD_ONCE_INIT; static void SpinLockLockInit(void) { lockLock = OSSpinLockLock != NULL ? OSSpinLockLock : _spin_lock; lockUnlock = OSSpinLockUnlock != NULL ? OSSpinLockUnlock : _spin_unlock; + lockTry = OSSpinLockTry != NULL ? OSSpinLockTry : _spin_lock_try; if (lockLock == NULL || lockUnlock == NULL) { Tcl_Panic("SpinLockLockInit: no spinlock API available"); } } #define SpinLockLock(p) lockLock(p) #define SpinLockUnlock(p) lockUnlock(p) +#define SpinLockTry(p) lockTry(p) #else #define SpinLockLock(p) OSSpinLockLock(p) #define SpinLockUnlock(p) OSSpinLockUnlock(p) +#define SpinLockTry(p) OSSpinLockTry(p) #endif /* HAVE_WEAK_IMPORT */ +#define SPINLOCK_INIT OS_SPINLOCK_INIT #else /* @@ -201,10 +212,13 @@ static void SpinLockLockInit(void) { */ typedef uint32_t OSSpinLock; -extern void _spin_lock(OSSpinLock *lock); -extern void _spin_unlock(OSSpinLock *lock); +extern void _spin_lock(OSSpinLock *lock); +extern void _spin_unlock(OSSpinLock *lock); +extern int _spin_lock_try(OSSpinLock *lock); #define SpinLockLock(p) _spin_lock(p) #define SpinLockUnlock(p) _spin_unlock(p) +#define SpinLockTry(p) _spin_lock_try(p) +#define SPINLOCK_INIT 0 #endif /* HAVE_LIBKERN_OSATOMIC_H && HAVE_OSSPINLOCKLOCK */ @@ -212,8 +226,8 @@ extern void _spin_unlock(OSSpinLock *lock); * These spinlocks lock access to the global notifier state. */ -static OSSpinLock notifierInitLock = 0; -static OSSpinLock notifierLock = 0; +static OSSpinLock notifierInitLock = SPINLOCK_INIT; +static OSSpinLock notifierLock = SPINLOCK_INIT; /* * Macros abstracting notifier locking/unlocking @@ -224,6 +238,37 @@ static OSSpinLock notifierLock = 0; #define LOCK_NOTIFIER SpinLockLock(¬ifierLock) #define UNLOCK_NOTIFIER SpinLockUnlock(¬ifierLock) +#ifdef TCL_MAC_DEBUG_NOTIFIER +/* + * Debug version of SpinLockLock that logs the time spent waiting for the lock + */ + +#define SpinLockLockDbg(p) if(!SpinLockTry(p)) { \ + Tcl_WideInt s = TclpGetWideClicks(), e; \ + SpinLockLock(p); e = TclpGetWideClicks(); \ + fprintf(notifierLog, "tclMacOSXNotify.c:" \ + "%4d: thread %10p waited on %s for " \ + "%8llu ns\n", __LINE__, pthread_self(), \ + #p, TclpWideClicksToNanoseconds(e-s)); \ + fflush(notifierLog); \ + } +#undef LOCK_NOTIFIER_INIT +#define LOCK_NOTIFIER_INIT SpinLockLockDbg(¬ifierInitLock) +#undef LOCK_NOTIFIER +#define LOCK_NOTIFIER SpinLockLockDbg(¬ifierLock) +static FILE *notifierLog = stderr; +#ifndef NOTIFIER_LOG +#define NOTIFIER_LOG "/tmp/tclMacOSXNotify.log" +#endif +#define OPEN_NOTIFIER_LOG if (notifierLog == stderr) { \ + notifierLog = fopen(NOTIFIER_LOG, "a"); \ + } +#define CLOSE_NOTIFIER_LOG if (notifierLog != stderr) { \ + fclose(notifierLog); \ + notifierLog = stderr; \ + } +#endif /* TCL_MAC_DEBUG_NOTIFIER */ + /* * The pollState bits * POLL_WANT is set by each thread before it waits on its condition @@ -386,6 +431,9 @@ Tcl_InitNotifier(void) */ notifierThread = 0; +#ifdef TCL_MAC_DEBUG_NOTIFIER + OPEN_NOTIFIER_LOG; +#endif } notifierCount++; UNLOCK_NOTIFIER_INIT; @@ -456,6 +504,9 @@ Tcl_FinalizeNotifier( close(receivePipe); triggerPipe = -1; +#ifdef TCL_MAC_DEBUG_NOTIFIER + CLOSE_NOTIFIER_LOG; +#endif } UNLOCK_NOTIFIER_INIT; @@ -859,6 +910,9 @@ Tcl_WaitForEvent( */ LOCK_NOTIFIER_INIT; + if (!notifierCount) { + Tcl_Panic("Tcl_WaitForEvent: notifier not initialized"); + } if (!notifierThread) { int result; pthread_attr_t attr; @@ -882,7 +936,9 @@ Tcl_WaitForEvent( */ LOCK_NOTIFIER; - + if (!tsdPtr->runLoop) { + Tcl_Panic("Tcl_WaitForEvent: CFRunLoop not initialized"); + } waitForFiles = (tsdPtr->numFdBits > 0); if (myTimePtr != NULL && myTimePtr->sec == 0 && myTimePtr->usec == 0) { /* |