summaryrefslogtreecommitdiffstats
path: root/macosx/tclMacOSXNotify.c
diff options
context:
space:
mode:
Diffstat (limited to 'macosx/tclMacOSXNotify.c')
-rw-r--r--macosx/tclMacOSXNotify.c513
1 files changed, 286 insertions, 227 deletions
diff --git a/macosx/tclMacOSXNotify.c b/macosx/tclMacOSXNotify.c
index a45741d..ef80192 100644
--- a/macosx/tclMacOSXNotify.c
+++ b/macosx/tclMacOSXNotify.c
@@ -6,13 +6,11 @@
* This file works together with generic/tclNotify.c.
*
* Copyright (c) 1995-1997 Sun Microsystems, Inc.
- * Copyright 2001, Apple Computer, Inc.
+ * Copyright 2001-2009, Apple Inc.
* Copyright (c) 2005-2009 Daniel A. Steffen <das@users.sourceforge.net>
*
* 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.18.2.1 2009/04/10 18:02:42 das Exp $
*/
#include "tclInt.h"
@@ -23,9 +21,6 @@
/* #define TCL_MAC_DEBUG_NOTIFIER 1 */
-extern TclStubs tclStubs;
-extern Tcl_NotifierProcs tclOriginalNotifier;
-
/*
* We use the Darwin-native spinlock API rather than pthread mutexes for
* notifier locking: this radically simplifies the implementation and lowers
@@ -51,22 +46,30 @@ extern Tcl_NotifierProcs tclOriginalNotifier;
#define VOLATILE volatile
#else
#define VOLATILE
-#endif
+#endif /* MAC_OS_X_VERSION_MAX_ALLOWED >= 1050 */
#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;
+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) {
+static void
+SpinLockLockInit(void)
+{
lockLock = OSSpinLockLock != NULL ? OSSpinLockLock : _spin_lock;
lockUnlock = OSSpinLockUnlock != NULL ? OSSpinLockUnlock : _spin_unlock;
lockTry = OSSpinLockTry != NULL ? OSSpinLockTry : _spin_lock_try;
@@ -76,13 +79,13 @@ static void SpinLockLockInit(void) {
}
#define SpinLockLock(p) lockLock(p)
#define SpinLockUnlock(p) lockUnlock(p)
-#define SpinLockTry(p) lockTry(p)
+#define SpinLockTry(p) lockTry(p)
#else
#define SpinLockLock(p) OSSpinLockLock(p)
#define SpinLockUnlock(p) OSSpinLockUnlock(p)
-#define SpinLockTry(p) OSSpinLockTry(p)
+#define SpinLockTry(p) OSSpinLockTry(p)
#endif /* HAVE_WEAK_IMPORT */
-#define SPINLOCK_INIT OS_SPINLOCK_INIT
+#define SPINLOCK_INIT OS_SPINLOCK_INIT
#else
/*
@@ -90,13 +93,13 @@ static void SpinLockLockInit(void) {
*/
typedef uint32_t OSSpinLock;
-extern void _spin_lock(OSSpinLock *lock);
-extern void _spin_unlock(OSSpinLock *lock);
-extern int _spin_lock_try(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
+#define SpinLockTry(p) _spin_lock_try(p)
+#define SPINLOCK_INIT 0
#endif /* HAVE_LIBKERN_OSATOMIC_H && HAVE_OSSPINLOCKLOCK */
@@ -119,23 +122,27 @@ static OSSpinLock notifierLock = SPINLOCK_INIT;
#define UNLOCK_NOTIFIER_TSD SpinLockUnlock(&tsdPtr->tsdLock)
#ifdef TCL_MAC_DEBUG_NOTIFIER
-#define TclMacOSXNotifierDbgMsg(m, ...) do { \
- fprintf(notifierLog?notifierLog:stderr, "tclMacOSXNotify.c:%d: " \
- "%s() pid %5d thread %10p: " m "\n", __LINE__, __func__, \
- getpid(), pthread_self(), ##__VA_ARGS__); \
- fflush(notifierLog?notifierLog:stderr); \
- } while (0)
+#define TclMacOSXNotifierDbgMsg(m, ...) \
+ do { \
+ fprintf(notifierLog?notifierLog:stderr, "tclMacOSXNotify.c:%d: " \
+ "%s() pid %5d thread %10p: " m "\n", __LINE__, __func__, \
+ getpid(), pthread_self(), ##__VA_ARGS__); \
+ fflush(notifierLog?notifierLog:stderr); \
+ } while (0)
/*
* 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(); \
- TclMacOSXNotifierDbgMsg("waited on %s for %8.0f ns", \
- #p, TclpWideClicksToNanoseconds(e-s)); \
- }
+#define SpinLockLockDbg(p) \
+ if (!SpinLockTry(p)) { \
+ Tcl_WideInt s = TclpGetWideClicks(), e; \
+ \
+ SpinLockLock(p); \
+ e = TclpGetWideClicks(); \
+ TclMacOSXNotifierDbgMsg("waited on %s for %8.0f ns", \
+ #p, TclpWideClicksToNanoseconds(e-s)); \
+ }
#undef LOCK_NOTIFIER_INIT
#define LOCK_NOTIFIER_INIT SpinLockLockDbg(&notifierInitLock)
#undef LOCK_NOTIFIER
@@ -147,42 +154,44 @@ static FILE *notifierLog = NULL;
#ifndef NOTIFIER_LOG
#define NOTIFIER_LOG "/tmp/tclMacOSXNotify.log"
#endif
-#define OPEN_NOTIFIER_LOG if (!notifierLog) { \
- notifierLog = fopen(NOTIFIER_LOG, "a"); \
- /*TclMacOSXNotifierDbgMsg("open log"); \
- asl_set_filter(NULL, \
- ASL_FILTER_MASK_UPTO(ASL_LEVEL_DEBUG)); \
- asl_add_log_file(NULL, \
- fileno(notifierLog));*/ \
- }
-#define CLOSE_NOTIFIER_LOG if (notifierLog) { \
- /*asl_remove_log_file(NULL, \
- fileno(notifierLog)); \
- TclMacOSXNotifierDbgMsg("close log");*/ \
- fclose(notifierLog); \
- notifierLog = NULL; \
- }
-#define ENABLE_ASL if (notifierLog) { \
- /*tsdPtr->asl = asl_open(NULL, "com.apple.console", ASL_OPT_NO_REMOTE); \
- asl_set_filter(tsdPtr->asl, \
- ASL_FILTER_MASK_UPTO(ASL_LEVEL_DEBUG)); \
- asl_add_log_file(tsdPtr->asl, \
- fileno(notifierLog));*/ \
- }
-#define DISABLE_ASL /*if (tsdPtr->asl) { \
- if (notifierLog) { \
- asl_remove_log_file(tsdPtr->asl, \
- fileno(notifierLog)); \
- } \
- asl_close(tsdPtr->asl); \
- }*/
-#define ASLCLIENT /*aslclient asl*/
+#define OPEN_NOTIFIER_LOG \
+ if (!notifierLog) { \
+ notifierLog = fopen(NOTIFIER_LOG, "a"); \
+ /*TclMacOSXNotifierDbgMsg("open log"); \
+ *asl_set_filter(NULL, \
+ * ASL_FILTER_MASK_UPTO(ASL_LEVEL_DEBUG)); \
+ *asl_add_log_file(NULL, fileno(notifierLog));*/ \
+ }
+#define CLOSE_NOTIFIER_LOG \
+ if (notifierLog) { \
+ /*asl_remove_log_file(NULL, fileno(notifierLog)); \
+ *TclMacOSXNotifierDbgMsg("close log");*/ \
+ fclose(notifierLog); \
+ notifierLog = NULL; \
+ }
+#define ENABLE_ASL \
+ if (notifierLog) { \
+ /*tsdPtr->asl = asl_open(NULL, "com.apple.console", \
+ * ASL_OPT_NO_REMOTE); \
+ *asl_set_filter(tsdPtr->asl, \
+ * ASL_FILTER_MASK_UPTO(ASL_LEVEL_DEBUG)); \
+ *asl_add_log_file(tsdPtr->asl, fileno(notifierLog));*/ \
+ }
+#define DISABLE_ASL \
+ /*if (tsdPtr->asl) { \
+ * if (notifierLog) { \
+ * asl_remove_log_file(tsdPtr->asl, fileno(notifierLog)); \
+ * } \
+ * asl_close(tsdPtr->asl); \
+ *}*/
+#define ASLCLIENT_DECL /*aslclient asl*/
#else
#define TclMacOSXNotifierDbgMsg(m, ...)
#define OPEN_NOTIFIER_LOG
#define CLOSE_NOTIFIER_LOG
#define ENABLE_ASL
#define DISABLE_ASL
+#define ASLCLIENT_DECL
#endif /* TCL_MAC_DEBUG_NOTIFIER */
/*
@@ -239,26 +248,30 @@ typedef struct ThreadSpecificData {
FileHandler *firstFileHandlerPtr;
/* Pointer to head of file handler list. */
int polled; /* True if the notifier thread has polled for
- * this thread.
- */
+ * this thread. */
int sleeping; /* True if runloop is inside Tcl_Sleep. */
int runLoopSourcePerformed; /* True after the runLoopSource callack was
* performed. */
- int runLoopRunning; /* True if this thread's Tcl runLoop is running */
- int runLoopNestingLevel; /* Level of nested runLoop invocations */
+ int runLoopRunning; /* True if this thread's Tcl runLoop is
+ * running. */
+ int runLoopNestingLevel; /* Level of nested runLoop invocations. */
+ int runLoopServicingEvents; /* True if this thread's runLoop is servicing
+ * Tcl events. */
+
/* Must hold the notifierLock before accessing the following fields: */
/* Start notifierLock section */
- int onList; /* True if this thread is on the waitingList */
+ int onList; /* True if this thread is on the
+ * waitingList */
struct ThreadSpecificData *nextPtr, *prevPtr;
/* All threads that are currently waiting on
* an event have their ThreadSpecificData
* structure on a doubly-linked listed formed
- * from these pointers.
- */
+ * from these pointers. */
/* End notifierLock section */
+
OSSpinLock tsdLock; /* Must hold this lock before acessing the
- * following fields from more than one thread.
- */
+ * following fields from more than one
+ * thread. */
/* Start tsdLock section */
SelectMasks checkMasks; /* This structure is used to build up the
* masks to be used in the next call to
@@ -270,25 +283,26 @@ typedef struct ThreadSpecificData {
int numFdBits; /* Number of valid bits in checkMasks (one
* more than highest fd for which
* Tcl_WatchFile has been called). */
- int polling; /* True if this thread is polling for events */
+ int polling; /* True if this thread is polling for
+ * events. */
CFRunLoopRef runLoop; /* This thread's CFRunLoop, needs to be woken
- * up whenever the runLoopSource is signaled */
+ * up whenever the runLoopSource is
+ * signaled. */
CFRunLoopSourceRef runLoopSource;
/* Any other thread alerts a notifier that an
* event is ready to be processed by signaling
* this CFRunLoopSource. */
- CFRunLoopObserverRef runLoopObserver;
+ CFRunLoopObserverRef runLoopObserver, runLoopObserverTcl;
/* Adds/removes this thread from waitingList
* when the CFRunLoop starts/stops. */
CFRunLoopTimerRef runLoopTimer;
/* Wakes up CFRunLoop after given timeout when
* running embedded. */
/* End tsdLock section */
+
CFTimeInterval waitTime; /* runLoopTimer wait time when running
* embedded. */
-#ifdef TCL_MAC_DEBUG_NOTIFIER
- ASLCLIENT;
-#endif
+ ASLCLIENT_DECL;
} ThreadSpecificData;
static Tcl_ThreadDataKey dataKey;
@@ -338,8 +352,8 @@ static int receivePipe = -1; /* Output end of triggerPipe */
static int notifierThreadRunning;
/*
- * This is the thread ID of the notifier thread that does select.
- * Only valid when notifierThreadRunning is non-zero.
+ * This is the thread ID of the notifier thread that does select. Only valid
+ * when notifierThreadRunning is non-zero.
*
* You must hold the notifierInitLock before accessing this variable.
*/
@@ -348,7 +362,7 @@ static pthread_t notifierThread;
/*
* Custom runloop mode for running with only the runloop source for the
- * notifier thread
+ * notifier thread.
*/
#ifndef TCL_EVENTS_ONLY_RUN_LOOP_MODE
@@ -370,38 +384,45 @@ static CFStringRef tclEventsOnlyRunLoopMode = NULL;
* Static routines defined in this file.
*/
-static void StartNotifierThread(void);
-static void NotifierThreadProc(ClientData clientData)
- __attribute__ ((__noreturn__));
-static int FileHandlerEventProc(Tcl_Event *evPtr, int flags);
-static void TimerWakeUp(CFRunLoopTimerRef timer, void *info);
-static void QueueFileEvents(void *info);
-static void UpdateWaitingListAndServiceEvents(CFRunLoopObserverRef observer,
- CFRunLoopActivity activity, void *info);
-static int OnOffWaitingList(ThreadSpecificData *tsdPtr, int onList,
- int signalNotifier);
+static void StartNotifierThread(void);
+static void NotifierThreadProc(ClientData clientData)
+ __attribute__ ((__noreturn__));
+static int FileHandlerEventProc(Tcl_Event *evPtr, int flags);
+static void TimerWakeUp(CFRunLoopTimerRef timer, void *info);
+static void QueueFileEvents(void *info);
+static void UpdateWaitingListAndServiceEvents(
+ CFRunLoopObserverRef observer,
+ CFRunLoopActivity activity, void *info);
+static int OnOffWaitingList(ThreadSpecificData *tsdPtr,
+ int onList, int signalNotifier);
#ifdef HAVE_PTHREAD_ATFORK
-static int atForkInit = 0;
-static void AtForkPrepare(void);
-static void AtForkParent(void);
-static void AtForkChild(void);
+static int atForkInit = 0;
+static void AtForkPrepare(void);
+static void AtForkParent(void);
+static void AtForkChild(void);
#if defined(HAVE_WEAK_IMPORT) && MAC_OS_X_VERSION_MIN_REQUIRED < 1040
/* Support for weakly importing pthread_atfork. */
#define WEAK_IMPORT_PTHREAD_ATFORK
-extern int pthread_atfork(void (*prepare)(void), void (*parent)(void),
- void (*child)(void)) WEAK_IMPORT_ATTRIBUTE;
+extern int pthread_atfork(void (*prepare)(void),
+ void (*parent)(void), void (*child)(void))
+ WEAK_IMPORT_ATTRIBUTE;
+#define MayUsePthreadAtfork() (pthread_atfork != NULL)
+#else
+#define MayUsePthreadAtfork() (1)
#endif /* HAVE_WEAK_IMPORT */
+
/*
* On Darwin 9 and later, it is not possible to call CoreFoundation after
* a fork.
*/
+
#if !defined(MAC_OS_X_VERSION_MIN_REQUIRED) || \
MAC_OS_X_VERSION_MIN_REQUIRED < 1050
MODULE_SCOPE long tclMacOSXDarwinRelease;
-#define noCFafterFork (tclMacOSXDarwinRelease >= 9)
+#define noCFafterFork (tclMacOSXDarwinRelease >= 9)
#else /* MAC_OS_X_VERSION_MIN_REQUIRED */
-#define noCFafterFork 1
+#define noCFafterFork 1
#endif /* MAC_OS_X_VERSION_MIN_REQUIRED */
#endif /* HAVE_PTHREAD_ATFORK */
@@ -426,6 +447,10 @@ Tcl_InitNotifier(void)
{
ThreadSpecificData *tsdPtr;
+ if (tclNotifierHooks.initNotifierProc) {
+ return tclNotifierHooks.initNotifierProc();
+ }
+
tsdPtr = TCL_TSD_INIT(&dataKey);
#ifdef WEAK_IMPORT_SPINLOCKLOCK
@@ -453,7 +478,7 @@ Tcl_InitNotifier(void)
CFRunLoopSourceRef runLoopSource;
CFRunLoopSourceContext runLoopSourceContext;
CFRunLoopObserverContext runLoopObserverContext;
- CFRunLoopObserverRef runLoopObserver;
+ CFRunLoopObserverRef runLoopObserver, runLoopObserverTcl;
bzero(&runLoopSourceContext, sizeof(CFRunLoopSourceContext));
runLoopSourceContext.info = tsdPtr;
@@ -477,12 +502,30 @@ Tcl_InitNotifier(void)
"CFRunLoopObserver");
}
CFRunLoopAddObserver(runLoop, runLoopObserver, kCFRunLoopCommonModes);
- CFRunLoopAddObserver(runLoop, runLoopObserver,
+
+ /*
+ * Create a second CFRunLoopObserver with the same callback as above
+ * for the tclEventsOnlyRunLoopMode to ensure that the callback can be
+ * re-entered via Tcl_ServiceAll() in the kCFRunLoopBeforeWaiting case
+ * (CFRunLoop prevents observer callback re-entry of a given observer
+ * instance).
+ */
+
+ runLoopObserverTcl = CFRunLoopObserverCreate(NULL,
+ kCFRunLoopEntry|kCFRunLoopExit|kCFRunLoopBeforeWaiting, TRUE,
+ LONG_MIN, UpdateWaitingListAndServiceEvents,
+ &runLoopObserverContext);
+ if (!runLoopObserverTcl) {
+ Tcl_Panic("Tcl_InitNotifier: could not create "
+ "CFRunLoopObserver");
+ }
+ CFRunLoopAddObserver(runLoop, runLoopObserverTcl,
tclEventsOnlyRunLoopMode);
tsdPtr->runLoop = runLoop;
tsdPtr->runLoopSource = runLoopSource;
tsdPtr->runLoopObserver = runLoopObserver;
+ tsdPtr->runLoopObserverTcl = runLoopObserverTcl;
tsdPtr->runLoopTimer = NULL;
tsdPtr->waitTime = CF_TIMEINTERVAL_FOREVER;
tsdPtr->tsdLock = SPINLOCK_INIT;
@@ -495,11 +538,7 @@ Tcl_InitNotifier(void)
* child of a fork.
*/
- if (
-#ifdef WEAK_IMPORT_PTHREAD_ATFORK
- pthread_atfork != NULL &&
-#endif
- !atForkInit) {
+ if (MayUsePthreadAtfork() && !atForkInit) {
int result = pthread_atfork(AtForkPrepare, AtForkParent, AtForkChild);
if (result) {
@@ -507,7 +546,7 @@ Tcl_InitNotifier(void)
}
atForkInit = 1;
}
-#endif
+#endif /* HAVE_PTHREAD_ATFORK */
if (notifierCount == 0) {
int fds[2], status;
@@ -548,7 +587,7 @@ Tcl_InitNotifier(void)
notifierCount++;
UNLOCK_NOTIFIER_INIT;
- return (ClientData) tsdPtr;
+ return tsdPtr;
}
/*
@@ -570,7 +609,7 @@ Tcl_InitNotifier(void)
void
TclMacOSXNotifierAddRunLoopMode(
- CONST void *runLoopMode)
+ const void *runLoopMode)
{
ThreadSpecificData *tsdPtr = TCL_TSD_INIT(&dataKey);
CFStringRef mode = (CFStringRef) runLoopMode;
@@ -651,6 +690,11 @@ Tcl_FinalizeNotifier(
{
ThreadSpecificData *tsdPtr;
+ if (tclNotifierHooks.finalizeNotifierProc) {
+ tclNotifierHooks.finalizeNotifierProc(clientData);
+ return;
+ }
+
tsdPtr = TCL_TSD_INIT(&dataKey);
LOCK_NOTIFIER_INIT;
@@ -669,10 +713,10 @@ Tcl_FinalizeNotifier(
* terminate. The notifier will return from its call to select()
* and notice that a "q" message has arrived, it will then close
* its side of the pipe and terminate its thread. Note the we can
- * not just close the pipe and check for EOF in the notifier thread
- * because if a background child process was created with exec,
- * select() would not register the EOF on the pipe until the child
- * processes had terminated. [Bug: 4139] [Bug: 1222872]
+ * not just close the pipe and check for EOF in the notifier
+ * thread because if a background child process was created with
+ * exec, select() would not register the EOF on the pipe until the
+ * child processes had terminated. [Bug: 4139] [Bug 1222872]
*/
write(triggerPipe, "q", 1);
@@ -710,6 +754,9 @@ Tcl_FinalizeNotifier(
CFRunLoopObserverInvalidate(tsdPtr->runLoopObserver);
CFRelease(tsdPtr->runLoopObserver);
tsdPtr->runLoopObserver = NULL;
+ CFRunLoopObserverInvalidate(tsdPtr->runLoopObserverTcl);
+ CFRelease(tsdPtr->runLoopObserverTcl);
+ tsdPtr->runLoopObserverTcl = NULL;
if (tsdPtr->runLoopTimer) {
CFRunLoopTimerInvalidate(tsdPtr->runLoopTimer);
CFRelease(tsdPtr->runLoopTimer);
@@ -744,6 +791,11 @@ Tcl_AlertNotifier(
{
ThreadSpecificData *tsdPtr = clientData;
+ if (tclNotifierHooks.alertNotifierProc) {
+ tclNotifierHooks.alertNotifierProc(clientData);
+ return;
+ }
+
LOCK_NOTIFIER_TSD;
if (tsdPtr->runLoop) {
CFRunLoopSourceSignal(tsdPtr->runLoopSource);
@@ -770,14 +822,14 @@ Tcl_AlertNotifier(
void
Tcl_SetTimer(
- Tcl_Time *timePtr) /* Timeout value, may be NULL. */
+ const Tcl_Time *timePtr) /* Timeout value, may be NULL. */
{
ThreadSpecificData *tsdPtr;
CFRunLoopTimerRef runLoopTimer;
CFTimeInterval waitTime;
- if (tclStubs.tcl_SetTimer != tclOriginalNotifier.setTimerProc) {
- tclStubs.tcl_SetTimer(timePtr);
+ if (tclNotifierHooks.setTimerProc) {
+ tclNotifierHooks.setTimerProc(timePtr);
return;
}
@@ -787,7 +839,7 @@ Tcl_SetTimer(
return;
}
if (timePtr) {
- Tcl_Time vTime = *timePtr;
+ Tcl_Time vTime = *timePtr;
if (vTime.sec != 0 || vTime.usec != 0) {
tclScaleTimeProcPtr(&vTime, tclTimeClientData);
@@ -849,6 +901,11 @@ Tcl_ServiceModeHook(
{
ThreadSpecificData *tsdPtr;
+ if (tclNotifierHooks.serviceModeHookProc) {
+ tclNotifierHooks.serviceModeHookProc(mode);
+ return;
+ }
+
tsdPtr = TCL_TSD_INIT(&dataKey);
if (mode == TCL_SERVICE_ALL && !tsdPtr->runLoopTimer) {
@@ -896,9 +953,8 @@ Tcl_CreateFileHandler(
ThreadSpecificData *tsdPtr;
FileHandler *filePtr;
- if (tclStubs.tcl_CreateFileHandler !=
- tclOriginalNotifier.createFileHandlerProc) {
- tclStubs.tcl_CreateFileHandler(fd, mask, proc, clientData);
+ if (tclNotifierHooks.createFileHandlerProc) {
+ tclNotifierHooks.createFileHandlerProc(fd, mask, proc, clientData);
return;
}
@@ -911,7 +967,7 @@ Tcl_CreateFileHandler(
}
}
if (filePtr == NULL) {
- filePtr = (FileHandler *) ckalloc(sizeof(FileHandler));
+ filePtr = ckalloc(sizeof(FileHandler));
filePtr->fd = fd;
filePtr->readyMask = 0;
filePtr->nextPtr = tsdPtr->firstFileHandlerPtr;
@@ -927,19 +983,19 @@ Tcl_CreateFileHandler(
LOCK_NOTIFIER_TSD;
if (mask & TCL_READABLE) {
- FD_SET(fd, &(tsdPtr->checkMasks.readable));
+ FD_SET(fd, &tsdPtr->checkMasks.readable);
} else {
- FD_CLR(fd, &(tsdPtr->checkMasks.readable));
+ FD_CLR(fd, &tsdPtr->checkMasks.readable);
}
if (mask & TCL_WRITABLE) {
- FD_SET(fd, &(tsdPtr->checkMasks.writable));
+ FD_SET(fd, &tsdPtr->checkMasks.writable);
} else {
- FD_CLR(fd, &(tsdPtr->checkMasks.writable));
+ FD_CLR(fd, &tsdPtr->checkMasks.writable);
}
if (mask & TCL_EXCEPTION) {
- FD_SET(fd, &(tsdPtr->checkMasks.exceptional));
+ FD_SET(fd, &tsdPtr->checkMasks.exceptional);
} else {
- FD_CLR(fd, &(tsdPtr->checkMasks.exceptional));
+ FD_CLR(fd, &tsdPtr->checkMasks.exceptional);
}
if (tsdPtr->numFdBits <= fd) {
tsdPtr->numFdBits = fd+1;
@@ -972,9 +1028,8 @@ Tcl_DeleteFileHandler(
int i, numFdBits;
ThreadSpecificData *tsdPtr;
- if (tclStubs.tcl_DeleteFileHandler !=
- tclOriginalNotifier.deleteFileHandlerProc) {
- tclStubs.tcl_DeleteFileHandler(fd);
+ if (tclNotifierHooks.deleteFileHandlerProc) {
+ tclNotifierHooks.deleteFileHandlerProc(fd);
return;
}
@@ -1002,9 +1057,9 @@ Tcl_DeleteFileHandler(
if (fd+1 == tsdPtr->numFdBits) {
numFdBits = 0;
for (i = fd-1; i >= 0; i--) {
- if (FD_ISSET(i, &(tsdPtr->checkMasks.readable))
- || FD_ISSET(i, &(tsdPtr->checkMasks.writable))
- || FD_ISSET(i, &(tsdPtr->checkMasks.exceptional))) {
+ if (FD_ISSET(i, &tsdPtr->checkMasks.readable)
+ || FD_ISSET(i, &tsdPtr->checkMasks.writable)
+ || FD_ISSET(i, &tsdPtr->checkMasks.exceptional)) {
numFdBits = i+1;
break;
}
@@ -1021,13 +1076,13 @@ Tcl_DeleteFileHandler(
*/
if (filePtr->mask & TCL_READABLE) {
- FD_CLR(fd, &(tsdPtr->checkMasks.readable));
+ FD_CLR(fd, &tsdPtr->checkMasks.readable);
}
if (filePtr->mask & TCL_WRITABLE) {
- FD_CLR(fd, &(tsdPtr->checkMasks.writable));
+ FD_CLR(fd, &tsdPtr->checkMasks.writable);
}
if (filePtr->mask & TCL_EXCEPTION) {
- FD_CLR(fd, &(tsdPtr->checkMasks.exceptional));
+ FD_CLR(fd, &tsdPtr->checkMasks.exceptional);
}
UNLOCK_NOTIFIER_TSD;
@@ -1040,7 +1095,7 @@ Tcl_DeleteFileHandler(
} else {
prevPtr->nextPtr = filePtr->nextPtr;
}
- ckfree((char *) filePtr);
+ ckfree(filePtr);
}
/*
@@ -1111,13 +1166,13 @@ FileHandlerEventProc(
if (mask != 0) {
LOCK_NOTIFIER_TSD;
if (mask & TCL_READABLE) {
- FD_CLR(filePtr->fd, &(tsdPtr->readyMasks.readable));
+ FD_CLR(filePtr->fd, &tsdPtr->readyMasks.readable);
}
if (mask & TCL_WRITABLE) {
- FD_CLR(filePtr->fd, &(tsdPtr->readyMasks.writable));
+ FD_CLR(filePtr->fd, &tsdPtr->readyMasks.writable);
}
if (mask & TCL_EXCEPTION) {
- FD_CLR(filePtr->fd, &(tsdPtr->readyMasks.exceptional));
+ FD_CLR(filePtr->fd, &tsdPtr->readyMasks.exceptional);
}
UNLOCK_NOTIFIER_TSD;
filePtr->proc(filePtr->clientData, mask);
@@ -1148,16 +1203,15 @@ FileHandlerEventProc(
int
Tcl_WaitForEvent(
- Tcl_Time *timePtr) /* Maximum block time, or NULL. */
+ const Tcl_Time *timePtr) /* Maximum block time, or NULL. */
{
- int result, polling;
+ int result, polling, runLoopRunning;
CFTimeInterval waitTime;
- CFStringRef runLoopMode;
SInt32 runLoopStatus;
ThreadSpecificData *tsdPtr;
- if (tclStubs.tcl_WaitForEvent != tclOriginalNotifier.waitForEventProc) {
- return tclStubs.tcl_WaitForEvent(timePtr);
+ if (tclNotifierHooks.waitForEventProc) {
+ return tclNotifierHooks.waitForEventProc(timePtr);
}
result = -1;
polling = 0;
@@ -1169,7 +1223,7 @@ Tcl_WaitForEvent(
}
if (timePtr) {
- Tcl_Time vTime = *timePtr;
+ Tcl_Time vTime = *timePtr;
/*
* TIP #233 (Virtualized Time). Is virtual time in effect? And do we
@@ -1200,29 +1254,27 @@ Tcl_WaitForEvent(
tsdPtr->runLoopSourcePerformed = 0;
/*
- * If the Tcl run loop is already running (e.g. if Tcl_WaitForEvent was
- * called recursively), re-run it in a custom run loop mode containing only
- * the source for the notifier thread, otherwise wakeups from other sources
- * added to the common run loop modes might get lost.
+ * If the Tcl runloop is already running (e.g. if Tcl_WaitForEvent was
+ * called recursively) or is servicing events via the runloop observer,
+ * re-run it in a custom runloop mode containing only the source for the
+ * notifier thread, otherwise wakeups from other sources added to the
+ * common runloop modes might get lost or 3rd party event handlers might
+ * get called when they do not expect to be.
*/
- if (tsdPtr->runLoopRunning) {
- runLoopMode = tclEventsOnlyRunLoopMode;
- } else {
- runLoopMode = kCFRunLoopDefaultMode;
- tsdPtr->runLoopRunning = 1;
- }
- runLoopStatus = CFRunLoopRunInMode(runLoopMode, waitTime, TRUE);
- if (runLoopMode == kCFRunLoopDefaultMode) {
- tsdPtr->runLoopRunning = 0;
- }
+ runLoopRunning = tsdPtr->runLoopRunning;
+ tsdPtr->runLoopRunning = 1;
+ runLoopStatus = CFRunLoopRunInMode(tsdPtr->runLoopServicingEvents ||
+ runLoopRunning ? tclEventsOnlyRunLoopMode : kCFRunLoopDefaultMode,
+ waitTime, TRUE);
+ tsdPtr->runLoopRunning = runLoopRunning;
LOCK_NOTIFIER_TSD;
tsdPtr->polling = 0;
UNLOCK_NOTIFIER_TSD;
switch (runLoopStatus) {
case kCFRunLoopRunFinished:
- Tcl_Panic("Tcl_WaitForEvent: CFRunLoop finished");
+ Tcl_Panic("Tcl_WaitForEvent: CFRunLoop finished");
break;
case kCFRunLoopRunTimedOut:
QueueFileEvents(tsdPtr);
@@ -1259,19 +1311,19 @@ QueueFileEvents(
{
SelectMasks readyMasks;
FileHandler *filePtr;
- ThreadSpecificData *tsdPtr = (ThreadSpecificData *) info;
+ ThreadSpecificData *tsdPtr = info;
/*
* Queue all detected file events.
*/
LOCK_NOTIFIER_TSD;
- FD_COPY(&(tsdPtr->readyMasks.readable), &readyMasks.readable);
- FD_COPY(&(tsdPtr->readyMasks.writable), &readyMasks.writable);
- FD_COPY(&(tsdPtr->readyMasks.exceptional), &readyMasks.exceptional);
- FD_ZERO(&(tsdPtr->readyMasks.readable));
- FD_ZERO(&(tsdPtr->readyMasks.writable));
- FD_ZERO(&(tsdPtr->readyMasks.exceptional));
+ FD_COPY(&tsdPtr->readyMasks.readable, &readyMasks.readable);
+ FD_COPY(&tsdPtr->readyMasks.writable, &readyMasks.writable);
+ FD_COPY(&tsdPtr->readyMasks.exceptional, &readyMasks.exceptional);
+ FD_ZERO(&tsdPtr->readyMasks.readable);
+ FD_ZERO(&tsdPtr->readyMasks.writable);
+ FD_ZERO(&tsdPtr->readyMasks.exceptional);
UNLOCK_NOTIFIER_TSD;
tsdPtr->runLoopSourcePerformed = 1;
@@ -1298,8 +1350,8 @@ QueueFileEvents(
*/
if (filePtr->readyMask == 0) {
- FileHandlerEvent *fileEvPtr = (FileHandlerEvent *)
- ckalloc(sizeof(FileHandlerEvent));
+ FileHandlerEvent *fileEvPtr = ckalloc(sizeof(FileHandlerEvent));
+
fileEvPtr->header.proc = FileHandlerEventProc;
fileEvPtr->fd = filePtr->fd;
Tcl_QueueEvent((Tcl_Event *) fileEvPtr, TCL_QUEUE_TAIL);
@@ -1331,21 +1383,24 @@ UpdateWaitingListAndServiceEvents(
CFRunLoopActivity activity,
void *info)
{
- ThreadSpecificData *tsdPtr = (ThreadSpecificData*) info;
+ ThreadSpecificData *tsdPtr = info;
+ if (tsdPtr->sleeping) {
+ return;
+ }
switch (activity) {
case kCFRunLoopEntry:
tsdPtr->runLoopNestingLevel++;
- if (tsdPtr->runLoopNestingLevel == 1 && !tsdPtr->sleeping &&
- (tsdPtr->numFdBits > 0 || tsdPtr->polling)) {
+ if (tsdPtr->numFdBits > 0 || tsdPtr->polling) {
LOCK_NOTIFIER;
- OnOffWaitingList(tsdPtr, 1, 1);
+ if (!OnOffWaitingList(tsdPtr, 1, 1) && tsdPtr->polling) {
+ write(triggerPipe, "", 1);
+ }
UNLOCK_NOTIFIER;
}
break;
case kCFRunLoopExit:
- if (tsdPtr->runLoopNestingLevel == 1 && !tsdPtr->sleeping &&
- (tsdPtr->numFdBits > 0 || tsdPtr->polling)) {
+ if (tsdPtr->runLoopNestingLevel == 1) {
LOCK_NOTIFIER;
OnOffWaitingList(tsdPtr, 0, 1);
UNLOCK_NOTIFIER;
@@ -1353,11 +1408,16 @@ UpdateWaitingListAndServiceEvents(
tsdPtr->runLoopNestingLevel--;
break;
case kCFRunLoopBeforeWaiting:
- if (!tsdPtr->sleeping && tsdPtr->runLoopTimer &&
- (tsdPtr->runLoopNestingLevel > 1 || !tsdPtr->runLoopRunning)) {
+ if (tsdPtr->runLoopTimer && !tsdPtr->runLoopServicingEvents &&
+ (tsdPtr->runLoopNestingLevel > 1
+ || !tsdPtr->runLoopRunning)) {
+ tsdPtr->runLoopServicingEvents = 1;
while (Tcl_ServiceAll() && tsdPtr->waitTime == 0) {}
+ tsdPtr->runLoopServicingEvents = 0;
}
break;
+ default:
+ break;
}
}
@@ -1366,8 +1426,8 @@ UpdateWaitingListAndServiceEvents(
*
* OnOffWaitingList --
*
- * Add/remove the specified thread to/from the global waitingList
- * and optionally signal the notifier.
+ * Add/remove the specified thread to/from the global waitingList and
+ * optionally signal the notifier.
*
* !!! Requires notifierLock to be held !!!
*
@@ -1387,8 +1447,9 @@ OnOffWaitingList(
int signalNotifier)
{
int changeWaitingList;
+
#ifdef TCL_MAC_DEBUG_NOTIFIER
- if(SpinLockTry(&notifierLock)) {
+ if (SpinLockTry(&notifierLock)) {
Tcl_Panic("OnOffWaitingList: notifierLock unlocked");
}
#endif
@@ -1415,7 +1476,7 @@ OnOffWaitingList(
tsdPtr->onList = 0;
}
if (signalNotifier) {
- write(triggerPipe, "", 1);
+ write(triggerPipe, "", 1);
}
}
@@ -1453,7 +1514,7 @@ Tcl_Sleep(
* TIP #233: Scale from virtual time to real-time.
*/
- vdelay.sec = ms / 1000;
+ vdelay.sec = ms / 1000;
vdelay.usec = (ms % 1000) * 1000;
tclScaleTimeProcPtr(&vdelay, tclTimeClientData);
@@ -1479,8 +1540,8 @@ Tcl_Sleep(
}
tsdPtr->sleeping = 1;
do {
- runLoopStatus = CFRunLoopRunInMode(kCFRunLoopDefaultMode, waitTime,
- FALSE);
+ runLoopStatus = CFRunLoopRunInMode(kCFRunLoopDefaultMode,
+ waitTime, FALSE);
switch (runLoopStatus) {
case kCFRunLoopRunFinished:
Tcl_Panic("Tcl_Sleep: CFRunLoop finished");
@@ -1544,16 +1605,25 @@ TclUnixWaitForFile(
{
Tcl_Time abortTime = {0, 0}, now; /* silence gcc 4 warning */
struct timeval blockTime, *timeoutPtr;
- int index, numFound, result = 0;
- fd_mask bit;
- fd_mask readyMasks[3*MASK_SIZE];
- fd_mask *maskp[3]; /* This array reflects the readable/writable
- * conditions that were found to exist by the
- * last call to select. */
+ int numFound, result = 0;
+ fd_set readableMask;
+ fd_set writableMask;
+ fd_set exceptionalMask;
#define SET_BITS(var, bits) ((var) |= (bits))
#define CLEAR_BITS(var, bits) ((var) &= ~(bits))
+#ifndef _DARWIN_C_SOURCE
+ /*
+ * Sanity check fd.
+ */
+
+ if (fd >= FD_SETSIZE) {
+ Tcl_Panic("TclUnixWaitForFile can't handle file id %d", fd);
+ /* must never get here, or select masks overrun will occur below */
+ }
+#endif
+
/*
* If there is a non-zero finite timeout, compute the time when we give
* up.
@@ -1577,16 +1647,12 @@ TclUnixWaitForFile(
}
/*
- * Initialize the ready masks and compute the mask offsets.
+ * Initialize the select masks.
*/
- if (fd >= FD_SETSIZE) {
- Tcl_Panic("TclWaitForFile can't handle file id %d", fd);
- /* must never get here, or readyMasks overrun will occur below */
- }
- memset(readyMasks, 0, 3*MASK_SIZE*sizeof(fd_mask));
- index = fd / (NBBY*sizeof(fd_mask));
- bit = ((fd_mask)1) << (fd % (NBBY*sizeof(fd_mask)));
+ FD_ZERO(&readableMask);
+ FD_ZERO(&writableMask);
+ FD_ZERO(&exceptionalMask);
/*
* Loop in a mini-event loop of our own, waiting for either the file to
@@ -1608,41 +1674,33 @@ TclUnixWaitForFile(
}
/*
- * Set the appropriate bit in the ready masks for the fd.
+ * Setup the select masks for the fd.
*/
if (mask & TCL_READABLE) {
- readyMasks[index] |= bit;
+ FD_SET(fd, &readableMask);
}
if (mask & TCL_WRITABLE) {
- (readyMasks+MASK_SIZE)[index] |= bit;
+ FD_SET(fd, &writableMask);
}
if (mask & TCL_EXCEPTION) {
- (readyMasks+2*(MASK_SIZE))[index] |= bit;
+ FD_SET(fd, &exceptionalMask);
}
/*
* Wait for the event or a timeout.
*/
- /*
- * This is needed to satisfy GCC 3.3's strict aliasing rules.
- */
-
- maskp[0] = &readyMasks[0];
- maskp[1] = &readyMasks[MASK_SIZE];
- maskp[2] = &readyMasks[2*MASK_SIZE];
- numFound = select(fd+1, (SELECT_MASK *) maskp[0],
- (SELECT_MASK *) maskp[1],
- (SELECT_MASK *) maskp[2], timeoutPtr);
+ numFound = select(fd + 1, &readableMask, &writableMask,
+ &exceptionalMask, timeoutPtr);
if (numFound == 1) {
- if (readyMasks[index] & bit) {
+ if (FD_ISSET(fd, &readableMask)) {
SET_BITS(result, TCL_READABLE);
}
- if ((readyMasks+MASK_SIZE)[index] & bit) {
+ if (FD_ISSET(fd, &writableMask)) {
SET_BITS(result, TCL_WRITABLE);
}
- if ((readyMasks+2*(MASK_SIZE))[index] & bit) {
+ if (FD_ISSET(fd, &exceptionalMask)) {
SET_BITS(result, TCL_EXCEPTION);
}
result &= mask;
@@ -1723,13 +1781,13 @@ NotifierThreadProc(
for (tsdPtr = waitingListPtr; tsdPtr; tsdPtr = tsdPtr->nextPtr) {
LOCK_NOTIFIER_TSD;
for (i = tsdPtr->numFdBits-1; i >= 0; --i) {
- if (FD_ISSET(i, &(tsdPtr->checkMasks.readable))) {
+ if (FD_ISSET(i, &tsdPtr->checkMasks.readable)) {
FD_SET(i, &readableMask);
}
- if (FD_ISSET(i, &(tsdPtr->checkMasks.writable))) {
+ if (FD_ISSET(i, &tsdPtr->checkMasks.writable)) {
FD_SET(i, &writableMask);
}
- if (FD_ISSET(i, &(tsdPtr->checkMasks.exceptional))) {
+ if (FD_ISSET(i, &tsdPtr->checkMasks.exceptional)) {
FD_SET(i, &exceptionalMask);
}
}
@@ -1772,9 +1830,9 @@ NotifierThreadProc(
SelectMasks readyMasks, checkMasks;
LOCK_NOTIFIER_TSD;
- FD_COPY(&(tsdPtr->checkMasks.readable), &checkMasks.readable);
- FD_COPY(&(tsdPtr->checkMasks.writable), &checkMasks.writable);
- FD_COPY(&(tsdPtr->checkMasks.exceptional), &checkMasks.exceptional);
+ FD_COPY(&tsdPtr->checkMasks.readable, &checkMasks.readable);
+ FD_COPY(&tsdPtr->checkMasks.writable, &checkMasks.writable);
+ FD_COPY(&tsdPtr->checkMasks.exceptional, &checkMasks.exceptional);
UNLOCK_NOTIFIER_TSD;
found = tsdPtr->polled;
FD_ZERO(&readyMasks.readable);
@@ -1810,9 +1868,10 @@ NotifierThreadProc(
OnOffWaitingList(tsdPtr, 0, 0);
LOCK_NOTIFIER_TSD;
- FD_COPY(&readyMasks.readable, &(tsdPtr->readyMasks.readable));
- FD_COPY(&readyMasks.writable, &(tsdPtr->readyMasks.writable));
- FD_COPY(&readyMasks.exceptional, &(tsdPtr->readyMasks.exceptional));
+ FD_COPY(&readyMasks.readable, &tsdPtr->readyMasks.readable);
+ FD_COPY(&readyMasks.writable, &tsdPtr->readyMasks.writable);
+ FD_COPY(&readyMasks.exceptional,
+ &tsdPtr->readyMasks.exceptional);
UNLOCK_NOTIFIER_TSD;
tsdPtr->polled = 0;
if (tsdPtr->runLoop) {
@@ -1960,7 +2019,7 @@ AtForkChild(void)
void
TclMacOSXNotifierAddRunLoopMode(
- CONST void *runLoopMode)
+ const void *runLoopMode)
{
Tcl_Panic("TclMacOSXNotifierAddRunLoopMode: "
"Tcl not built with CoreFoundation support");