| 1 | /* | 
|---|
| 2 |  * tclMacOSXNotify.c -- | 
|---|
| 3 |  * | 
|---|
| 4 |  *      This file contains the implementation of a merged CFRunLoop/select() | 
|---|
| 5 |  *      based notifier, which is the lowest-level part of the Tcl event loop. | 
|---|
| 6 |  *      This file works together with generic/tclNotify.c. | 
|---|
| 7 |  * | 
|---|
| 8 |  * Copyright (c) 1995-1997 Sun Microsystems, Inc. | 
|---|
| 9 |  * Copyright 2001, Apple Computer, Inc. | 
|---|
| 10 |  * Copyright (c) 2005-2008 Daniel A. Steffen <das@users.sourceforge.net> | 
|---|
| 11 |  * | 
|---|
| 12 |  * See the file "license.terms" for information on usage and redistribution of | 
|---|
| 13 |  * this file, and for a DISCLAIMER OF ALL WARRANTIES. | 
|---|
| 14 |  * | 
|---|
| 15 |  * RCS: @(#) $Id: tclMacOSXNotify.c,v 1.18 2008/03/11 22:24:17 das Exp $ | 
|---|
| 16 |  */ | 
|---|
| 17 |  | 
|---|
| 18 | #include "tclInt.h" | 
|---|
| 19 | #ifdef HAVE_COREFOUNDATION      /* Traditional unix select-based notifier is | 
|---|
| 20 |                                  * in tclUnixNotfy.c */ | 
|---|
| 21 | #include <CoreFoundation/CoreFoundation.h> | 
|---|
| 22 | #include <pthread.h> | 
|---|
| 23 |  | 
|---|
| 24 | extern TclStubs tclStubs; | 
|---|
| 25 | extern Tcl_NotifierProcs tclOriginalNotifier; | 
|---|
| 26 |  | 
|---|
| 27 | /* | 
|---|
| 28 |  * This structure is used to keep track of the notifier info for a registered | 
|---|
| 29 |  * file. | 
|---|
| 30 |  */ | 
|---|
| 31 |  | 
|---|
| 32 | typedef struct FileHandler { | 
|---|
| 33 |     int fd; | 
|---|
| 34 |     int mask;                   /* Mask of desired events: TCL_READABLE, | 
|---|
| 35 |                                  * etc. */ | 
|---|
| 36 |     int readyMask;              /* Mask of events that have been seen since | 
|---|
| 37 |                                  * the last time file handlers were invoked | 
|---|
| 38 |                                  * for this file. */ | 
|---|
| 39 |     Tcl_FileProc *proc;         /* Function to call, in the style of | 
|---|
| 40 |                                  * Tcl_CreateFileHandler. */ | 
|---|
| 41 |     ClientData clientData;      /* Argument to pass to proc. */ | 
|---|
| 42 |     struct FileHandler *nextPtr;/* Next in list of all files we care about. */ | 
|---|
| 43 | } FileHandler; | 
|---|
| 44 |  | 
|---|
| 45 | /* | 
|---|
| 46 |  * The following structure is what is added to the Tcl event queue when file | 
|---|
| 47 |  * handlers are ready to fire. | 
|---|
| 48 |  */ | 
|---|
| 49 |  | 
|---|
| 50 | typedef struct FileHandlerEvent { | 
|---|
| 51 |     Tcl_Event header;           /* Information that is standard for all | 
|---|
| 52 |                                  * events. */ | 
|---|
| 53 |     int fd;                     /* File descriptor that is ready. Used to find | 
|---|
| 54 |                                  * the FileHandler structure for the file | 
|---|
| 55 |                                  * (can't point directly to the FileHandler | 
|---|
| 56 |                                  * structure because it could go away while | 
|---|
| 57 |                                  * the event is queued). */ | 
|---|
| 58 | } FileHandlerEvent; | 
|---|
| 59 |  | 
|---|
| 60 | /* | 
|---|
| 61 |  * The following structure contains a set of select() masks to track readable, | 
|---|
| 62 |  * writable, and exceptional conditions. | 
|---|
| 63 |  */ | 
|---|
| 64 |  | 
|---|
| 65 | typedef struct SelectMasks { | 
|---|
| 66 |     fd_set readable; | 
|---|
| 67 |     fd_set writable; | 
|---|
| 68 |     fd_set exceptional; | 
|---|
| 69 | } SelectMasks; | 
|---|
| 70 |  | 
|---|
| 71 | /* | 
|---|
| 72 |  * The following static structure contains the state information for the | 
|---|
| 73 |  * select based implementation of the Tcl notifier. One of these structures is | 
|---|
| 74 |  * created for each thread that is using the notifier. | 
|---|
| 75 |  */ | 
|---|
| 76 |  | 
|---|
| 77 | typedef struct ThreadSpecificData { | 
|---|
| 78 |     FileHandler *firstFileHandlerPtr; | 
|---|
| 79 |                                 /* Pointer to head of file handler list. */ | 
|---|
| 80 |     SelectMasks checkMasks;     /* This structure is used to build up the | 
|---|
| 81 |                                  * masks to be used in the next call to | 
|---|
| 82 |                                  * select. Bits are set in response to calls | 
|---|
| 83 |                                  * to Tcl_CreateFileHandler. */ | 
|---|
| 84 |     SelectMasks readyMasks;     /* This array reflects the readable/writable | 
|---|
| 85 |                                  * conditions that were found to exist by the | 
|---|
| 86 |                                  * last call to select. */ | 
|---|
| 87 |     int numFdBits;              /* Number of valid bits in checkMasks (one | 
|---|
| 88 |                                  * more than highest fd for which | 
|---|
| 89 |                                  * Tcl_WatchFile has been called). */ | 
|---|
| 90 |     int onList;                 /* True if it is in this list */ | 
|---|
| 91 |     unsigned int pollState;     /* pollState is used to implement a polling | 
|---|
| 92 |                                  * handshake between each thread and the | 
|---|
| 93 |                                  * notifier thread. Bits defined below. */ | 
|---|
| 94 |     struct ThreadSpecificData *nextPtr, *prevPtr; | 
|---|
| 95 |                                 /* All threads that are currently waiting on | 
|---|
| 96 |                                  * an event have their ThreadSpecificData | 
|---|
| 97 |                                  * structure on a doubly-linked listed formed | 
|---|
| 98 |                                  * from these pointers. You must hold the | 
|---|
| 99 |                                  * notifierLock before accessing these | 
|---|
| 100 |                                  * fields. */ | 
|---|
| 101 |     CFRunLoopSourceRef runLoopSource; | 
|---|
| 102 |                                 /* Any other thread alerts a notifier that an | 
|---|
| 103 |                                  * event is ready to be processed by signaling | 
|---|
| 104 |                                  * this CFRunLoopSource. */ | 
|---|
| 105 |     CFRunLoopRef runLoop;       /* This thread's CFRunLoop, needs to be woken | 
|---|
| 106 |                                  * up whenever the runLoopSource is | 
|---|
| 107 |                                  * signaled. */ | 
|---|
| 108 |     int eventReady;             /* True if an event is ready to be | 
|---|
| 109 |                                  * processed. */ | 
|---|
| 110 | } ThreadSpecificData; | 
|---|
| 111 |  | 
|---|
| 112 | static Tcl_ThreadDataKey dataKey; | 
|---|
| 113 |  | 
|---|
| 114 | /* | 
|---|
| 115 |  * The following static indicates the number of threads that have initialized | 
|---|
| 116 |  * notifiers. | 
|---|
| 117 |  * | 
|---|
| 118 |  * You must hold the notifierInitLock before accessing this variable. | 
|---|
| 119 |  */ | 
|---|
| 120 |  | 
|---|
| 121 | static int notifierCount = 0; | 
|---|
| 122 |  | 
|---|
| 123 | /* | 
|---|
| 124 |  * The following variable points to the head of a doubly-linked list of | 
|---|
| 125 |  * ThreadSpecificData structures for all threads that are currently waiting on | 
|---|
| 126 |  * an event. | 
|---|
| 127 |  * | 
|---|
| 128 |  * You must hold the notifierLock before accessing this list. | 
|---|
| 129 |  */ | 
|---|
| 130 |  | 
|---|
| 131 | static ThreadSpecificData *waitingListPtr = NULL; | 
|---|
| 132 |  | 
|---|
| 133 | /* | 
|---|
| 134 |  * The notifier thread spends all its time in select() waiting for a file | 
|---|
| 135 |  * descriptor associated with one of the threads on the waitingListPtr list to | 
|---|
| 136 |  * do something interesting. But if the contents of the waitingListPtr list | 
|---|
| 137 |  * ever changes, we need to wake up and restart the select() system call. You | 
|---|
| 138 |  * can wake up the notifier thread by writing a single byte to the file | 
|---|
| 139 |  * descriptor defined below. This file descriptor is the input-end of a pipe | 
|---|
| 140 |  * and the notifier thread is listening for data on the output-end of the same | 
|---|
| 141 |  * pipe. Hence writing to this file descriptor will cause the select() system | 
|---|
| 142 |  * call to return and wake up the notifier thread. | 
|---|
| 143 |  * | 
|---|
| 144 |  * You must hold the notifierLock lock before writing to the pipe. | 
|---|
| 145 |  */ | 
|---|
| 146 |  | 
|---|
| 147 | static int triggerPipe = -1; | 
|---|
| 148 | static int receivePipe = -1; /* Output end of triggerPipe */ | 
|---|
| 149 |  | 
|---|
| 150 | /* | 
|---|
| 151 |  * We use the Darwin-native spinlock API rather than pthread mutexes for | 
|---|
| 152 |  * notifier locking: this radically simplifies the implementation and lowers | 
|---|
| 153 |  * overhead. Note that these are not pure spinlocks, they employ various | 
|---|
| 154 |  * strategies to back off and relinquish the processor, making them immune to | 
|---|
| 155 |  * most priority-inversion livelocks (c.f. 'man 3 OSSpinLockLock' and Darwin | 
|---|
| 156 |  * sources: xnu/osfmk/{ppc,i386}/commpage/spinlocks.s). | 
|---|
| 157 |  */ | 
|---|
| 158 |  | 
|---|
| 159 | #if defined(HAVE_LIBKERN_OSATOMIC_H) && defined(HAVE_OSSPINLOCKLOCK) | 
|---|
| 160 | /* | 
|---|
| 161 |  * Use OSSpinLock API where available (Tiger or later). | 
|---|
| 162 |  */ | 
|---|
| 163 |  | 
|---|
| 164 | #include <libkern/OSAtomic.h> | 
|---|
| 165 |  | 
|---|
| 166 | #if defined(HAVE_WEAK_IMPORT) && MAC_OS_X_VERSION_MIN_REQUIRED < 1040 | 
|---|
| 167 | /* | 
|---|
| 168 |  * Support for weakly importing spinlock API. | 
|---|
| 169 |  */ | 
|---|
| 170 | #define WEAK_IMPORT_SPINLOCKLOCK | 
|---|
| 171 | #if MAC_OS_X_VERSION_MAX_ALLOWED >= 1050 | 
|---|
| 172 | #define VOLATILE volatile | 
|---|
| 173 | #else | 
|---|
| 174 | #define VOLATILE | 
|---|
| 175 | #endif | 
|---|
| 176 | #ifndef bool | 
|---|
| 177 | #define bool int | 
|---|
| 178 | #endif | 
|---|
| 179 | extern void OSSpinLockLock(VOLATILE OSSpinLock *lock) WEAK_IMPORT_ATTRIBUTE; | 
|---|
| 180 | extern void OSSpinLockUnlock(VOLATILE OSSpinLock *lock) WEAK_IMPORT_ATTRIBUTE; | 
|---|
| 181 | extern bool OSSpinLockTry(VOLATILE OSSpinLock *lock) WEAK_IMPORT_ATTRIBUTE; | 
|---|
| 182 | extern void _spin_lock(VOLATILE OSSpinLock *lock) WEAK_IMPORT_ATTRIBUTE; | 
|---|
| 183 | extern void _spin_unlock(VOLATILE OSSpinLock *lock) WEAK_IMPORT_ATTRIBUTE; | 
|---|
| 184 | extern bool _spin_lock_try(VOLATILE OSSpinLock *lock) WEAK_IMPORT_ATTRIBUTE; | 
|---|
| 185 | static void (* lockLock)(VOLATILE OSSpinLock *lock) = NULL; | 
|---|
| 186 | static void (* lockUnlock)(VOLATILE OSSpinLock *lock) = NULL; | 
|---|
| 187 | static bool (* lockTry)(VOLATILE OSSpinLock *lock) = NULL; | 
|---|
| 188 | #undef VOLATILE | 
|---|
| 189 | static pthread_once_t spinLockLockInitControl = PTHREAD_ONCE_INIT; | 
|---|
| 190 | static void SpinLockLockInit(void) { | 
|---|
| 191 |     lockLock   = OSSpinLockLock   != NULL ? OSSpinLockLock   : _spin_lock; | 
|---|
| 192 |     lockUnlock = OSSpinLockUnlock != NULL ? OSSpinLockUnlock : _spin_unlock; | 
|---|
| 193 |     lockTry    = OSSpinLockTry    != NULL ? OSSpinLockTry    : _spin_lock_try; | 
|---|
| 194 |     if (lockLock == NULL || lockUnlock == NULL) { | 
|---|
| 195 |         Tcl_Panic("SpinLockLockInit: no spinlock API available"); | 
|---|
| 196 |     } | 
|---|
| 197 | } | 
|---|
| 198 | #define SpinLockLock(p)         lockLock(p) | 
|---|
| 199 | #define SpinLockUnlock(p)       lockUnlock(p) | 
|---|
| 200 | #define SpinLockTry(p)          lockTry(p) | 
|---|
| 201 | #else | 
|---|
| 202 | #define SpinLockLock(p)         OSSpinLockLock(p) | 
|---|
| 203 | #define SpinLockUnlock(p)       OSSpinLockUnlock(p) | 
|---|
| 204 | #define SpinLockTry(p)          OSSpinLockTry(p) | 
|---|
| 205 | #endif /* HAVE_WEAK_IMPORT */ | 
|---|
| 206 | #define SPINLOCK_INIT           OS_SPINLOCK_INIT | 
|---|
| 207 |  | 
|---|
| 208 | #else | 
|---|
| 209 | /* | 
|---|
| 210 |  * Otherwise, use commpage spinlock SPI directly. | 
|---|
| 211 |  */ | 
|---|
| 212 |  | 
|---|
| 213 | typedef uint32_t OSSpinLock; | 
|---|
| 214 | extern void _spin_lock(OSSpinLock *lock); | 
|---|
| 215 | extern void _spin_unlock(OSSpinLock *lock); | 
|---|
| 216 | extern int  _spin_lock_try(OSSpinLock *lock); | 
|---|
| 217 | #define SpinLockLock(p)         _spin_lock(p) | 
|---|
| 218 | #define SpinLockUnlock(p)       _spin_unlock(p) | 
|---|
| 219 | #define SpinLockTry(p)          _spin_lock_try(p) | 
|---|
| 220 | #define SPINLOCK_INIT           0 | 
|---|
| 221 |  | 
|---|
| 222 | #endif /* HAVE_LIBKERN_OSATOMIC_H && HAVE_OSSPINLOCKLOCK */ | 
|---|
| 223 |  | 
|---|
| 224 | /* | 
|---|
| 225 |  * These spinlocks lock access to the global notifier state. | 
|---|
| 226 |  */ | 
|---|
| 227 |  | 
|---|
| 228 | static OSSpinLock notifierInitLock = SPINLOCK_INIT; | 
|---|
| 229 | static OSSpinLock notifierLock     = SPINLOCK_INIT; | 
|---|
| 230 |  | 
|---|
| 231 | /* | 
|---|
| 232 |  * Macros abstracting notifier locking/unlocking | 
|---|
| 233 |  */ | 
|---|
| 234 |  | 
|---|
| 235 | #define LOCK_NOTIFIER_INIT      SpinLockLock(¬ifierInitLock) | 
|---|
| 236 | #define UNLOCK_NOTIFIER_INIT    SpinLockUnlock(¬ifierInitLock) | 
|---|
| 237 | #define LOCK_NOTIFIER           SpinLockLock(¬ifierLock) | 
|---|
| 238 | #define UNLOCK_NOTIFIER         SpinLockUnlock(¬ifierLock) | 
|---|
| 239 |  | 
|---|
| 240 | #ifdef TCL_MAC_DEBUG_NOTIFIER | 
|---|
| 241 | /* | 
|---|
| 242 |  * Debug version of SpinLockLock that logs the time spent waiting for the lock | 
|---|
| 243 |  */ | 
|---|
| 244 |  | 
|---|
| 245 | #define SpinLockLockDbg(p)      if(!SpinLockTry(p)) { \ | 
|---|
| 246 |                                     Tcl_WideInt s = TclpGetWideClicks(), e; \ | 
|---|
| 247 |                                     SpinLockLock(p); e = TclpGetWideClicks(); \ | 
|---|
| 248 |                                     fprintf(notifierLog, "tclMacOSXNotify.c:" \ | 
|---|
| 249 |                                     "%4d: thread %10p waited on %s for " \ | 
|---|
| 250 |                                     "%8llu ns\n", __LINE__, pthread_self(), \ | 
|---|
| 251 |                                     #p, TclpWideClicksToNanoseconds(e-s)); \ | 
|---|
| 252 |                                     fflush(notifierLog); \ | 
|---|
| 253 |                                 } | 
|---|
| 254 | #undef LOCK_NOTIFIER_INIT | 
|---|
| 255 | #define LOCK_NOTIFIER_INIT      SpinLockLockDbg(¬ifierInitLock) | 
|---|
| 256 | #undef LOCK_NOTIFIER | 
|---|
| 257 | #define LOCK_NOTIFIER           SpinLockLockDbg(¬ifierLock) | 
|---|
| 258 | static FILE *notifierLog = stderr; | 
|---|
| 259 | #ifndef NOTIFIER_LOG | 
|---|
| 260 | #define NOTIFIER_LOG "/tmp/tclMacOSXNotify.log" | 
|---|
| 261 | #endif | 
|---|
| 262 | #define OPEN_NOTIFIER_LOG       if (notifierLog == stderr) { \ | 
|---|
| 263 |                                     notifierLog = fopen(NOTIFIER_LOG, "a"); \ | 
|---|
| 264 |                                 } | 
|---|
| 265 | #define CLOSE_NOTIFIER_LOG      if (notifierLog != stderr) { \ | 
|---|
| 266 |                                     fclose(notifierLog); \ | 
|---|
| 267 |                                     notifierLog = stderr; \ | 
|---|
| 268 |                                 } | 
|---|
| 269 | #endif /* TCL_MAC_DEBUG_NOTIFIER */ | 
|---|
| 270 |  | 
|---|
| 271 | /* | 
|---|
| 272 |  * The pollState bits | 
|---|
| 273 |  *      POLL_WANT is set by each thread before it waits on its condition | 
|---|
| 274 |  *              variable. It is checked by the notifier before it does select. | 
|---|
| 275 |  *      POLL_DONE is set by the notifier if it goes into select after seeing | 
|---|
| 276 |  *              POLL_WANT. The idea is to ensure it tries a select with the | 
|---|
| 277 |  *              same bits the initial thread had set. | 
|---|
| 278 |  */ | 
|---|
| 279 |  | 
|---|
| 280 | #define POLL_WANT       0x1 | 
|---|
| 281 | #define POLL_DONE       0x2 | 
|---|
| 282 |  | 
|---|
| 283 | /* | 
|---|
| 284 |  * This is the thread ID of the notifier thread that does select. | 
|---|
| 285 |  */ | 
|---|
| 286 |  | 
|---|
| 287 | static pthread_t notifierThread; | 
|---|
| 288 |  | 
|---|
| 289 | /* | 
|---|
| 290 |  * Custom run loop mode containing only the run loop source for the | 
|---|
| 291 |  * notifier thread. | 
|---|
| 292 |  */ | 
|---|
| 293 |  | 
|---|
| 294 | #ifndef TCL_EVENTS_ONLY_RUN_LOOP_MODE | 
|---|
| 295 | #define TCL_EVENTS_ONLY_RUN_LOOP_MODE "com.tcltk.tclEventsOnlyRunLoopMode" | 
|---|
| 296 | #endif | 
|---|
| 297 | #ifdef __CONSTANT_CFSTRINGS__ | 
|---|
| 298 | #define tclEventsOnlyRunLoopMode CFSTR(TCL_EVENTS_ONLY_RUN_LOOP_MODE) | 
|---|
| 299 | #else | 
|---|
| 300 | static CFStringRef tclEventsOnlyRunLoopMode = NULL; | 
|---|
| 301 | #endif | 
|---|
| 302 |  | 
|---|
| 303 | /* | 
|---|
| 304 |  * Static routines defined in this file. | 
|---|
| 305 |  */ | 
|---|
| 306 |  | 
|---|
| 307 | static void     NotifierThreadProc(ClientData clientData) | 
|---|
| 308 |         __attribute__ ((__noreturn__)); | 
|---|
| 309 | static int      FileHandlerEventProc(Tcl_Event *evPtr, int flags); | 
|---|
| 310 |  | 
|---|
| 311 | #ifdef HAVE_PTHREAD_ATFORK | 
|---|
| 312 | static int      atForkInit = 0; | 
|---|
| 313 | static void     AtForkPrepare(void); | 
|---|
| 314 | static void     AtForkParent(void); | 
|---|
| 315 | static void     AtForkChild(void); | 
|---|
| 316 | #if defined(HAVE_WEAK_IMPORT) && MAC_OS_X_VERSION_MIN_REQUIRED < 1040 | 
|---|
| 317 | /* Support for weakly importing pthread_atfork. */ | 
|---|
| 318 | #define WEAK_IMPORT_PTHREAD_ATFORK | 
|---|
| 319 | extern int pthread_atfork(void (*prepare)(void), void (*parent)(void), | 
|---|
| 320 |                           void (*child)(void)) WEAK_IMPORT_ATTRIBUTE; | 
|---|
| 321 | #endif /* HAVE_WEAK_IMPORT */ | 
|---|
| 322 | /* | 
|---|
| 323 |  * On Darwin 9 and later, it is not possible to call CoreFoundation after | 
|---|
| 324 |  * a fork. | 
|---|
| 325 |  */ | 
|---|
| 326 | #if !defined(MAC_OS_X_VERSION_MIN_REQUIRED) || \ | 
|---|
| 327 |         MAC_OS_X_VERSION_MIN_REQUIRED < 1050 | 
|---|
| 328 | MODULE_SCOPE long tclMacOSXDarwinRelease; | 
|---|
| 329 | #define noCFafterFork (tclMacOSXDarwinRelease >= 9) | 
|---|
| 330 | #else /* MAC_OS_X_VERSION_MIN_REQUIRED */ | 
|---|
| 331 | #define noCFafterFork 1 | 
|---|
| 332 | #endif /* MAC_OS_X_VERSION_MIN_REQUIRED */ | 
|---|
| 333 | #endif /* HAVE_PTHREAD_ATFORK */ | 
|---|
| 334 |  | 
|---|
| 335 | /* | 
|---|
| 336 |  *---------------------------------------------------------------------- | 
|---|
| 337 |  * | 
|---|
| 338 |  * Tcl_InitNotifier -- | 
|---|
| 339 |  * | 
|---|
| 340 |  *      Initializes the platform specific notifier state. | 
|---|
| 341 |  * | 
|---|
| 342 |  * Results: | 
|---|
| 343 |  *      Returns a handle to the notifier state for this thread. | 
|---|
| 344 |  * | 
|---|
| 345 |  * Side effects: | 
|---|
| 346 |  *      None. | 
|---|
| 347 |  * | 
|---|
| 348 |  *---------------------------------------------------------------------- | 
|---|
| 349 |  */ | 
|---|
| 350 |  | 
|---|
| 351 | ClientData | 
|---|
| 352 | Tcl_InitNotifier(void) | 
|---|
| 353 | { | 
|---|
| 354 |     ThreadSpecificData *tsdPtr = TCL_TSD_INIT(&dataKey); | 
|---|
| 355 |  | 
|---|
| 356 |     tsdPtr->eventReady = 0; | 
|---|
| 357 |  | 
|---|
| 358 | #ifdef WEAK_IMPORT_SPINLOCKLOCK | 
|---|
| 359 |     /* | 
|---|
| 360 |      * Initialize support for weakly imported spinlock API. | 
|---|
| 361 |      */ | 
|---|
| 362 |     if (pthread_once(&spinLockLockInitControl, SpinLockLockInit)) { | 
|---|
| 363 |         Tcl_Panic("Tcl_InitNotifier: pthread_once failed"); | 
|---|
| 364 |     } | 
|---|
| 365 | #endif | 
|---|
| 366 |  | 
|---|
| 367 | #ifndef __CONSTANT_CFSTRINGS__ | 
|---|
| 368 |     if (!tclEventsOnlyRunLoopMode) { | 
|---|
| 369 |         tclEventsOnlyRunLoopMode = CFSTR(TCL_EVENTS_ONLY_RUN_LOOP_MODE); | 
|---|
| 370 |     } | 
|---|
| 371 | #endif | 
|---|
| 372 |  | 
|---|
| 373 |     /* | 
|---|
| 374 |      * Initialize CFRunLoopSource and add it to CFRunLoop of this thread. | 
|---|
| 375 |      */ | 
|---|
| 376 |  | 
|---|
| 377 |     if (!tsdPtr->runLoop) { | 
|---|
| 378 |         CFRunLoopRef runLoop = CFRunLoopGetCurrent(); | 
|---|
| 379 |         CFRunLoopSourceRef runLoopSource; | 
|---|
| 380 |         CFRunLoopSourceContext runLoopSourceContext; | 
|---|
| 381 |  | 
|---|
| 382 |         bzero(&runLoopSourceContext, sizeof(CFRunLoopSourceContext)); | 
|---|
| 383 |         runLoopSourceContext.info = tsdPtr; | 
|---|
| 384 |         runLoopSource = CFRunLoopSourceCreate(NULL, 0, &runLoopSourceContext); | 
|---|
| 385 |         if (!runLoopSource) { | 
|---|
| 386 |             Tcl_Panic("Tcl_InitNotifier: could not create CFRunLoopSource"); | 
|---|
| 387 |         } | 
|---|
| 388 |         CFRunLoopAddSource(runLoop, runLoopSource, kCFRunLoopCommonModes); | 
|---|
| 389 |         CFRunLoopAddSource(runLoop, runLoopSource, tclEventsOnlyRunLoopMode); | 
|---|
| 390 |         tsdPtr->runLoopSource = runLoopSource; | 
|---|
| 391 |         tsdPtr->runLoop = runLoop; | 
|---|
| 392 |     } | 
|---|
| 393 |  | 
|---|
| 394 |     LOCK_NOTIFIER_INIT; | 
|---|
| 395 | #ifdef HAVE_PTHREAD_ATFORK | 
|---|
| 396 |     /* | 
|---|
| 397 |      * Install pthread_atfork handlers to reinitialize the notifier in the | 
|---|
| 398 |      * child of a fork. | 
|---|
| 399 |      */ | 
|---|
| 400 |  | 
|---|
| 401 |     if ( | 
|---|
| 402 | #ifdef WEAK_IMPORT_PTHREAD_ATFORK | 
|---|
| 403 |             pthread_atfork != NULL && | 
|---|
| 404 | #endif | 
|---|
| 405 |             !atForkInit) { | 
|---|
| 406 |         int result = pthread_atfork(AtForkPrepare, AtForkParent, AtForkChild); | 
|---|
| 407 |         if (result) { | 
|---|
| 408 |             Tcl_Panic("Tcl_InitNotifier: pthread_atfork failed"); | 
|---|
| 409 |         } | 
|---|
| 410 |         atForkInit = 1; | 
|---|
| 411 |     } | 
|---|
| 412 | #endif | 
|---|
| 413 |     if (notifierCount == 0) { | 
|---|
| 414 |         int fds[2], status; | 
|---|
| 415 |  | 
|---|
| 416 |         /* | 
|---|
| 417 |          * Initialize trigger pipe. | 
|---|
| 418 |          */ | 
|---|
| 419 |  | 
|---|
| 420 |         if (pipe(fds) != 0) { | 
|---|
| 421 |             Tcl_Panic("Tcl_InitNotifier: could not create trigger pipe"); | 
|---|
| 422 |         } | 
|---|
| 423 |  | 
|---|
| 424 |         status = fcntl(fds[0], F_GETFL); | 
|---|
| 425 |         status |= O_NONBLOCK; | 
|---|
| 426 |         if (fcntl(fds[0], F_SETFL, status) < 0) { | 
|---|
| 427 |             Tcl_Panic("Tcl_InitNotifier: could not make receive pipe non blocking"); | 
|---|
| 428 |         } | 
|---|
| 429 |         status = fcntl(fds[1], F_GETFL); | 
|---|
| 430 |         status |= O_NONBLOCK; | 
|---|
| 431 |         if (fcntl(fds[1], F_SETFL, status) < 0) { | 
|---|
| 432 |             Tcl_Panic("Tcl_InitNotifier: could not make trigger pipe non blocking"); | 
|---|
| 433 |         } | 
|---|
| 434 |  | 
|---|
| 435 |         receivePipe = fds[0]; | 
|---|
| 436 |         triggerPipe = fds[1]; | 
|---|
| 437 |  | 
|---|
| 438 |         /* | 
|---|
| 439 |          * Create notifier thread lazily in Tcl_WaitForEvent() to avoid | 
|---|
| 440 |          * interfering with fork() followed immediately by execve() | 
|---|
| 441 |          * (cannot execve() when more than one thread is present). | 
|---|
| 442 |          */ | 
|---|
| 443 |  | 
|---|
| 444 |         notifierThread = 0; | 
|---|
| 445 | #ifdef TCL_MAC_DEBUG_NOTIFIER | 
|---|
| 446 |         OPEN_NOTIFIER_LOG; | 
|---|
| 447 | #endif | 
|---|
| 448 |     } | 
|---|
| 449 |     notifierCount++; | 
|---|
| 450 |     UNLOCK_NOTIFIER_INIT; | 
|---|
| 451 |  | 
|---|
| 452 |     return (ClientData) tsdPtr; | 
|---|
| 453 | } | 
|---|
| 454 |  | 
|---|
| 455 | /* | 
|---|
| 456 |  *---------------------------------------------------------------------- | 
|---|
| 457 |  * | 
|---|
| 458 |  * Tcl_FinalizeNotifier -- | 
|---|
| 459 |  * | 
|---|
| 460 |  *      This function is called to cleanup the notifier state before a thread | 
|---|
| 461 |  *      is terminated. | 
|---|
| 462 |  * | 
|---|
| 463 |  * Results: | 
|---|
| 464 |  *      None. | 
|---|
| 465 |  * | 
|---|
| 466 |  * Side effects: | 
|---|
| 467 |  *      May terminate the background notifier thread if this is the last | 
|---|
| 468 |  *      notifier instance. | 
|---|
| 469 |  * | 
|---|
| 470 |  *---------------------------------------------------------------------- | 
|---|
| 471 |  */ | 
|---|
| 472 |  | 
|---|
| 473 | void | 
|---|
| 474 | Tcl_FinalizeNotifier( | 
|---|
| 475 |     ClientData clientData)              /* Not used. */ | 
|---|
| 476 | { | 
|---|
| 477 |     ThreadSpecificData *tsdPtr = TCL_TSD_INIT(&dataKey); | 
|---|
| 478 |  | 
|---|
| 479 |     LOCK_NOTIFIER_INIT; | 
|---|
| 480 |     notifierCount--; | 
|---|
| 481 |  | 
|---|
| 482 |     /* | 
|---|
| 483 |      * If this is the last thread to use the notifier, close the notifier pipe | 
|---|
| 484 |      * and wait for the background thread to terminate. | 
|---|
| 485 |      */ | 
|---|
| 486 |  | 
|---|
| 487 |     if (notifierCount == 0) { | 
|---|
| 488 |         int result; | 
|---|
| 489 |  | 
|---|
| 490 |         if (triggerPipe < 0) { | 
|---|
| 491 |             Tcl_Panic("Tcl_FinalizeNotifier: notifier pipe not initialized"); | 
|---|
| 492 |         } | 
|---|
| 493 |  | 
|---|
| 494 |         /* | 
|---|
| 495 |          * Send "q" message to the notifier thread so that it will terminate. | 
|---|
| 496 |          * The notifier will return from its call to select() and notice that | 
|---|
| 497 |          * a "q" message has arrived, it will then close its side of the pipe | 
|---|
| 498 |          * and terminate its thread. Note the we can not just close the pipe | 
|---|
| 499 |          * and check for EOF in the notifier thread because if a background | 
|---|
| 500 |          * child process was created with exec, select() would not register | 
|---|
| 501 |          * the EOF on the pipe until the child processes had terminated. [Bug: | 
|---|
| 502 |          * 4139] [Bug: 1222872] | 
|---|
| 503 |          */ | 
|---|
| 504 |  | 
|---|
| 505 |         write(triggerPipe, "q", 1); | 
|---|
| 506 |         close(triggerPipe); | 
|---|
| 507 |  | 
|---|
| 508 |         if (notifierThread) { | 
|---|
| 509 |             result = pthread_join(notifierThread, NULL); | 
|---|
| 510 |             if (result) { | 
|---|
| 511 |                 Tcl_Panic("Tcl_FinalizeNotifier: unable to join notifier thread"); | 
|---|
| 512 |             } | 
|---|
| 513 |             notifierThread = 0; | 
|---|
| 514 |         } | 
|---|
| 515 |  | 
|---|
| 516 |         close(receivePipe); | 
|---|
| 517 |         triggerPipe = -1; | 
|---|
| 518 | #ifdef TCL_MAC_DEBUG_NOTIFIER | 
|---|
| 519 |         CLOSE_NOTIFIER_LOG; | 
|---|
| 520 | #endif | 
|---|
| 521 |     } | 
|---|
| 522 |     UNLOCK_NOTIFIER_INIT; | 
|---|
| 523 |  | 
|---|
| 524 |     LOCK_NOTIFIER;              /* for concurrency with Tcl_AlertNotifier */ | 
|---|
| 525 |     if (tsdPtr->runLoop) { | 
|---|
| 526 |         tsdPtr->runLoop = NULL; | 
|---|
| 527 |  | 
|---|
| 528 |         /* | 
|---|
| 529 |          * Remove runLoopSource from all CFRunLoops and release it. | 
|---|
| 530 |          */ | 
|---|
| 531 |  | 
|---|
| 532 |         CFRunLoopSourceInvalidate(tsdPtr->runLoopSource); | 
|---|
| 533 |         CFRelease(tsdPtr->runLoopSource); | 
|---|
| 534 |         tsdPtr->runLoopSource = NULL; | 
|---|
| 535 |     } | 
|---|
| 536 |     UNLOCK_NOTIFIER; | 
|---|
| 537 | } | 
|---|
| 538 |  | 
|---|
| 539 | /* | 
|---|
| 540 |  *---------------------------------------------------------------------- | 
|---|
| 541 |  * | 
|---|
| 542 |  * Tcl_AlertNotifier -- | 
|---|
| 543 |  * | 
|---|
| 544 |  *      Wake up the specified notifier from any thread. This routine is called | 
|---|
| 545 |  *      by the platform independent notifier code whenever the Tcl_ThreadAlert | 
|---|
| 546 |  *      routine is called. This routine is guaranteed not to be called on a | 
|---|
| 547 |  *      given notifier after Tcl_FinalizeNotifier is called for that notifier. | 
|---|
| 548 |  * | 
|---|
| 549 |  * Results: | 
|---|
| 550 |  *      None. | 
|---|
| 551 |  * | 
|---|
| 552 |  * Side effects: | 
|---|
| 553 |  *      Signals the notifier condition variable for the specified notifier. | 
|---|
| 554 |  * | 
|---|
| 555 |  *---------------------------------------------------------------------- | 
|---|
| 556 |  */ | 
|---|
| 557 |  | 
|---|
| 558 | void | 
|---|
| 559 | Tcl_AlertNotifier( | 
|---|
| 560 |     ClientData clientData) | 
|---|
| 561 | { | 
|---|
| 562 |     ThreadSpecificData *tsdPtr = (ThreadSpecificData *) clientData; | 
|---|
| 563 |  | 
|---|
| 564 |     LOCK_NOTIFIER; | 
|---|
| 565 |     if (tsdPtr->runLoop) { | 
|---|
| 566 |         tsdPtr->eventReady = 1; | 
|---|
| 567 |         CFRunLoopSourceSignal(tsdPtr->runLoopSource); | 
|---|
| 568 |         CFRunLoopWakeUp(tsdPtr->runLoop); | 
|---|
| 569 |     } | 
|---|
| 570 |     UNLOCK_NOTIFIER; | 
|---|
| 571 | } | 
|---|
| 572 |  | 
|---|
| 573 | /* | 
|---|
| 574 |  *---------------------------------------------------------------------- | 
|---|
| 575 |  * | 
|---|
| 576 |  * Tcl_SetTimer -- | 
|---|
| 577 |  * | 
|---|
| 578 |  *      This function sets the current notifier timer value. This interface is | 
|---|
| 579 |  *      not implemented in this notifier because we are always running inside | 
|---|
| 580 |  *      of Tcl_DoOneEvent. | 
|---|
| 581 |  * | 
|---|
| 582 |  * Results: | 
|---|
| 583 |  *      None. | 
|---|
| 584 |  * | 
|---|
| 585 |  * Side effects: | 
|---|
| 586 |  *      None. | 
|---|
| 587 |  * | 
|---|
| 588 |  *---------------------------------------------------------------------- | 
|---|
| 589 |  */ | 
|---|
| 590 |  | 
|---|
| 591 | void | 
|---|
| 592 | Tcl_SetTimer( | 
|---|
| 593 |     Tcl_Time *timePtr)          /* Timeout value, may be NULL. */ | 
|---|
| 594 | { | 
|---|
| 595 |     /* | 
|---|
| 596 |      * The interval timer doesn't do anything in this implementation, because | 
|---|
| 597 |      * the only event loop is via Tcl_DoOneEvent, which passes timeout values | 
|---|
| 598 |      * to Tcl_WaitForEvent. | 
|---|
| 599 |      */ | 
|---|
| 600 |  | 
|---|
| 601 |     if (tclStubs.tcl_SetTimer != tclOriginalNotifier.setTimerProc) { | 
|---|
| 602 |         tclStubs.tcl_SetTimer(timePtr); | 
|---|
| 603 |     } | 
|---|
| 604 | } | 
|---|
| 605 |  | 
|---|
| 606 | /* | 
|---|
| 607 |  *---------------------------------------------------------------------- | 
|---|
| 608 |  * | 
|---|
| 609 |  * Tcl_ServiceModeHook -- | 
|---|
| 610 |  * | 
|---|
| 611 |  *      This function is invoked whenever the service mode changes. | 
|---|
| 612 |  * | 
|---|
| 613 |  * Results: | 
|---|
| 614 |  *      None. | 
|---|
| 615 |  * | 
|---|
| 616 |  * Side effects: | 
|---|
| 617 |  *      None. | 
|---|
| 618 |  * | 
|---|
| 619 |  *---------------------------------------------------------------------- | 
|---|
| 620 |  */ | 
|---|
| 621 |  | 
|---|
| 622 | void | 
|---|
| 623 | Tcl_ServiceModeHook( | 
|---|
| 624 |     int mode)                   /* Either TCL_SERVICE_ALL, or | 
|---|
| 625 |                                  * TCL_SERVICE_NONE. */ | 
|---|
| 626 | { | 
|---|
| 627 | } | 
|---|
| 628 |  | 
|---|
| 629 | /* | 
|---|
| 630 |  *---------------------------------------------------------------------- | 
|---|
| 631 |  * | 
|---|
| 632 |  * Tcl_CreateFileHandler -- | 
|---|
| 633 |  * | 
|---|
| 634 |  *      This function registers a file handler with the select notifier. | 
|---|
| 635 |  * | 
|---|
| 636 |  * Results: | 
|---|
| 637 |  *      None. | 
|---|
| 638 |  * | 
|---|
| 639 |  * Side effects: | 
|---|
| 640 |  *      Creates a new file handler structure. | 
|---|
| 641 |  * | 
|---|
| 642 |  *---------------------------------------------------------------------- | 
|---|
| 643 |  */ | 
|---|
| 644 |  | 
|---|
| 645 | void | 
|---|
| 646 | Tcl_CreateFileHandler( | 
|---|
| 647 |     int fd,                     /* Handle of stream to watch. */ | 
|---|
| 648 |     int mask,                   /* OR'ed combination of TCL_READABLE, | 
|---|
| 649 |                                  * TCL_WRITABLE, and TCL_EXCEPTION: indicates | 
|---|
| 650 |                                  * conditions under which proc should be | 
|---|
| 651 |                                  * called. */ | 
|---|
| 652 |     Tcl_FileProc *proc,         /* Function to call for each selected | 
|---|
| 653 |                                  * event. */ | 
|---|
| 654 |     ClientData clientData)      /* Arbitrary data to pass to proc. */ | 
|---|
| 655 | { | 
|---|
| 656 |     ThreadSpecificData *tsdPtr = TCL_TSD_INIT(&dataKey); | 
|---|
| 657 |     FileHandler *filePtr; | 
|---|
| 658 |  | 
|---|
| 659 |     if (tclStubs.tcl_CreateFileHandler != | 
|---|
| 660 |             tclOriginalNotifier.createFileHandlerProc) { | 
|---|
| 661 |         tclStubs.tcl_CreateFileHandler(fd, mask, proc, clientData); | 
|---|
| 662 |         return; | 
|---|
| 663 |     } | 
|---|
| 664 |  | 
|---|
| 665 |     for (filePtr = tsdPtr->firstFileHandlerPtr; filePtr != NULL; | 
|---|
| 666 |             filePtr = filePtr->nextPtr) { | 
|---|
| 667 |         if (filePtr->fd == fd) { | 
|---|
| 668 |             break; | 
|---|
| 669 |         } | 
|---|
| 670 |     } | 
|---|
| 671 |     if (filePtr == NULL) { | 
|---|
| 672 |         filePtr = (FileHandler*) ckalloc(sizeof(FileHandler)); | 
|---|
| 673 |         filePtr->fd = fd; | 
|---|
| 674 |         filePtr->readyMask = 0; | 
|---|
| 675 |         filePtr->nextPtr = tsdPtr->firstFileHandlerPtr; | 
|---|
| 676 |         tsdPtr->firstFileHandlerPtr = filePtr; | 
|---|
| 677 |     } | 
|---|
| 678 |     filePtr->proc = proc; | 
|---|
| 679 |     filePtr->clientData = clientData; | 
|---|
| 680 |     filePtr->mask = mask; | 
|---|
| 681 |  | 
|---|
| 682 |     /* | 
|---|
| 683 |      * Update the check masks for this file. | 
|---|
| 684 |      */ | 
|---|
| 685 |  | 
|---|
| 686 |     if (mask & TCL_READABLE) { | 
|---|
| 687 |         FD_SET(fd, &(tsdPtr->checkMasks.readable)); | 
|---|
| 688 |     } else { | 
|---|
| 689 |         FD_CLR(fd, &(tsdPtr->checkMasks.readable)); | 
|---|
| 690 |     } | 
|---|
| 691 |     if (mask & TCL_WRITABLE) { | 
|---|
| 692 |         FD_SET(fd, &(tsdPtr->checkMasks.writable)); | 
|---|
| 693 |     } else { | 
|---|
| 694 |         FD_CLR(fd, &(tsdPtr->checkMasks.writable)); | 
|---|
| 695 |     } | 
|---|
| 696 |     if (mask & TCL_EXCEPTION) { | 
|---|
| 697 |         FD_SET(fd, &(tsdPtr->checkMasks.exceptional)); | 
|---|
| 698 |     } else { | 
|---|
| 699 |         FD_CLR(fd, &(tsdPtr->checkMasks.exceptional)); | 
|---|
| 700 |     } | 
|---|
| 701 |     if (tsdPtr->numFdBits <= fd) { | 
|---|
| 702 |         tsdPtr->numFdBits = fd+1; | 
|---|
| 703 |     } | 
|---|
| 704 | } | 
|---|
| 705 |  | 
|---|
| 706 | /* | 
|---|
| 707 |  *---------------------------------------------------------------------- | 
|---|
| 708 |  * | 
|---|
| 709 |  * Tcl_DeleteFileHandler -- | 
|---|
| 710 |  * | 
|---|
| 711 |  *      Cancel a previously-arranged callback arrangement for a file. | 
|---|
| 712 |  * | 
|---|
| 713 |  * Results: | 
|---|
| 714 |  *      None. | 
|---|
| 715 |  * | 
|---|
| 716 |  * Side effects: | 
|---|
| 717 |  *      If a callback was previously registered on file, remove it. | 
|---|
| 718 |  * | 
|---|
| 719 |  *---------------------------------------------------------------------- | 
|---|
| 720 |  */ | 
|---|
| 721 |  | 
|---|
| 722 | void | 
|---|
| 723 | Tcl_DeleteFileHandler( | 
|---|
| 724 |     int fd)                     /* Stream id for which to remove callback | 
|---|
| 725 |                                  * function. */ | 
|---|
| 726 | { | 
|---|
| 727 |     FileHandler *filePtr, *prevPtr; | 
|---|
| 728 |     int i; | 
|---|
| 729 |     ThreadSpecificData *tsdPtr = TCL_TSD_INIT(&dataKey); | 
|---|
| 730 |  | 
|---|
| 731 |     if (tclStubs.tcl_DeleteFileHandler != | 
|---|
| 732 |             tclOriginalNotifier.deleteFileHandlerProc) { | 
|---|
| 733 |         tclStubs.tcl_DeleteFileHandler(fd); | 
|---|
| 734 |         return; | 
|---|
| 735 |     } | 
|---|
| 736 |  | 
|---|
| 737 |     /* | 
|---|
| 738 |      * Find the entry for the given file (and return if there isn't one). | 
|---|
| 739 |      */ | 
|---|
| 740 |  | 
|---|
| 741 |     for (prevPtr = NULL, filePtr = tsdPtr->firstFileHandlerPtr; ; | 
|---|
| 742 |          prevPtr = filePtr, filePtr = filePtr->nextPtr) { | 
|---|
| 743 |         if (filePtr == NULL) { | 
|---|
| 744 |             return; | 
|---|
| 745 |         } | 
|---|
| 746 |         if (filePtr->fd == fd) { | 
|---|
| 747 |             break; | 
|---|
| 748 |         } | 
|---|
| 749 |     } | 
|---|
| 750 |  | 
|---|
| 751 |     /* | 
|---|
| 752 |      * Update the check masks for this file. | 
|---|
| 753 |      */ | 
|---|
| 754 |  | 
|---|
| 755 |     if (filePtr->mask & TCL_READABLE) { | 
|---|
| 756 |         FD_CLR(fd, &(tsdPtr->checkMasks.readable)); | 
|---|
| 757 |     } | 
|---|
| 758 |     if (filePtr->mask & TCL_WRITABLE) { | 
|---|
| 759 |         FD_CLR(fd, &(tsdPtr->checkMasks.writable)); | 
|---|
| 760 |     } | 
|---|
| 761 |     if (filePtr->mask & TCL_EXCEPTION) { | 
|---|
| 762 |         FD_CLR(fd, &(tsdPtr->checkMasks.exceptional)); | 
|---|
| 763 |     } | 
|---|
| 764 |  | 
|---|
| 765 |     /* | 
|---|
| 766 |      * Find current max fd. | 
|---|
| 767 |      */ | 
|---|
| 768 |  | 
|---|
| 769 |     if (fd+1 == tsdPtr->numFdBits) { | 
|---|
| 770 |         tsdPtr->numFdBits = 0; | 
|---|
| 771 |         for (i = fd-1; i >= 0; i--) { | 
|---|
| 772 |             if (FD_ISSET(i, &(tsdPtr->checkMasks.readable)) | 
|---|
| 773 |                     || FD_ISSET(i, &(tsdPtr->checkMasks.writable)) | 
|---|
| 774 |                     || FD_ISSET(i, &(tsdPtr->checkMasks.exceptional))) { | 
|---|
| 775 |                 tsdPtr->numFdBits = i+1; | 
|---|
| 776 |                 break; | 
|---|
| 777 |             } | 
|---|
| 778 |         } | 
|---|
| 779 |     } | 
|---|
| 780 |  | 
|---|
| 781 |     /* | 
|---|
| 782 |      * Clean up information in the callback record. | 
|---|
| 783 |      */ | 
|---|
| 784 |  | 
|---|
| 785 |     if (prevPtr == NULL) { | 
|---|
| 786 |         tsdPtr->firstFileHandlerPtr = filePtr->nextPtr; | 
|---|
| 787 |     } else { | 
|---|
| 788 |         prevPtr->nextPtr = filePtr->nextPtr; | 
|---|
| 789 |     } | 
|---|
| 790 |     ckfree((char *) filePtr); | 
|---|
| 791 | } | 
|---|
| 792 |  | 
|---|
| 793 | /* | 
|---|
| 794 |  *---------------------------------------------------------------------- | 
|---|
| 795 |  * | 
|---|
| 796 |  * FileHandlerEventProc -- | 
|---|
| 797 |  * | 
|---|
| 798 |  *      This function is called by Tcl_ServiceEvent when a file event reaches | 
|---|
| 799 |  *      the front of the event queue. This function is responsible for | 
|---|
| 800 |  *      actually handling the event by invoking the callback for the file | 
|---|
| 801 |  *      handler. | 
|---|
| 802 |  * | 
|---|
| 803 |  * Results: | 
|---|
| 804 |  *      Returns 1 if the event was handled, meaning it should be removed from | 
|---|
| 805 |  *      the queue. Returns 0 if the event was not handled, meaning it should | 
|---|
| 806 |  *      stay on the queue. The only time the event isn't handled is if the | 
|---|
| 807 |  *      TCL_FILE_EVENTS flag bit isn't set. | 
|---|
| 808 |  * | 
|---|
| 809 |  * Side effects: | 
|---|
| 810 |  *      Whatever the file handler's callback function does. | 
|---|
| 811 |  * | 
|---|
| 812 |  *---------------------------------------------------------------------- | 
|---|
| 813 |  */ | 
|---|
| 814 |  | 
|---|
| 815 | static int | 
|---|
| 816 | FileHandlerEventProc( | 
|---|
| 817 |     Tcl_Event *evPtr,           /* Event to service. */ | 
|---|
| 818 |     int flags)                  /* Flags that indicate what events to handle, | 
|---|
| 819 |                                  * such as TCL_FILE_EVENTS. */ | 
|---|
| 820 | { | 
|---|
| 821 |     int mask; | 
|---|
| 822 |     FileHandler *filePtr; | 
|---|
| 823 |     FileHandlerEvent *fileEvPtr = (FileHandlerEvent *) evPtr; | 
|---|
| 824 |     ThreadSpecificData *tsdPtr; | 
|---|
| 825 |  | 
|---|
| 826 |     if (!(flags & TCL_FILE_EVENTS)) { | 
|---|
| 827 |         return 0; | 
|---|
| 828 |     } | 
|---|
| 829 |  | 
|---|
| 830 |     /* | 
|---|
| 831 |      * Search through the file handlers to find the one whose handle matches | 
|---|
| 832 |      * the event. We do this rather than keeping a pointer to the file handler | 
|---|
| 833 |      * directly in the event, so that the handler can be deleted while the | 
|---|
| 834 |      * event is queued without leaving a dangling pointer. | 
|---|
| 835 |      */ | 
|---|
| 836 |  | 
|---|
| 837 |     tsdPtr = TCL_TSD_INIT(&dataKey); | 
|---|
| 838 |     for (filePtr = tsdPtr->firstFileHandlerPtr; filePtr != NULL; | 
|---|
| 839 |             filePtr = filePtr->nextPtr) { | 
|---|
| 840 |         if (filePtr->fd != fileEvPtr->fd) { | 
|---|
| 841 |             continue; | 
|---|
| 842 |         } | 
|---|
| 843 |  | 
|---|
| 844 |         /* | 
|---|
| 845 |          * The code is tricky for two reasons: | 
|---|
| 846 |          * 1. The file handler's desired events could have changed since the | 
|---|
| 847 |          *    time when the event was queued, so AND the ready mask with the | 
|---|
| 848 |          *    desired mask. | 
|---|
| 849 |          * 2. The file could have been closed and re-opened since the time | 
|---|
| 850 |          *    when the event was queued. This is why the ready mask is stored | 
|---|
| 851 |          *    in the file handler rather than the queued event: it will be | 
|---|
| 852 |          *    zeroed when a new file handler is created for the newly opened | 
|---|
| 853 |          *    file. | 
|---|
| 854 |          */ | 
|---|
| 855 |  | 
|---|
| 856 |         mask = filePtr->readyMask & filePtr->mask; | 
|---|
| 857 |         filePtr->readyMask = 0; | 
|---|
| 858 |         if (mask != 0) { | 
|---|
| 859 |             (*filePtr->proc)(filePtr->clientData, mask); | 
|---|
| 860 |         } | 
|---|
| 861 |         break; | 
|---|
| 862 |     } | 
|---|
| 863 |     return 1; | 
|---|
| 864 | } | 
|---|
| 865 |  | 
|---|
| 866 | /* | 
|---|
| 867 |  *---------------------------------------------------------------------- | 
|---|
| 868 |  * | 
|---|
| 869 |  * Tcl_WaitForEvent -- | 
|---|
| 870 |  * | 
|---|
| 871 |  *      This function is called by Tcl_DoOneEvent to wait for new events on | 
|---|
| 872 |  *      the message queue. If the block time is 0, then Tcl_WaitForEvent just | 
|---|
| 873 |  *      polls without blocking. | 
|---|
| 874 |  * | 
|---|
| 875 |  * Results: | 
|---|
| 876 |  *      Returns -1 if the select would block forever, otherwise returns 0. | 
|---|
| 877 |  * | 
|---|
| 878 |  * Side effects: | 
|---|
| 879 |  *      Queues file events that are detected by the select. | 
|---|
| 880 |  * | 
|---|
| 881 |  *---------------------------------------------------------------------- | 
|---|
| 882 |  */ | 
|---|
| 883 |  | 
|---|
| 884 | int | 
|---|
| 885 | Tcl_WaitForEvent( | 
|---|
| 886 |     Tcl_Time *timePtr)          /* Maximum block time, or NULL. */ | 
|---|
| 887 | { | 
|---|
| 888 |     FileHandler *filePtr; | 
|---|
| 889 |     FileHandlerEvent *fileEvPtr; | 
|---|
| 890 |     int mask; | 
|---|
| 891 |     Tcl_Time myTime; | 
|---|
| 892 |     int waitForFiles; | 
|---|
| 893 |     Tcl_Time *myTimePtr; | 
|---|
| 894 |     ThreadSpecificData *tsdPtr = TCL_TSD_INIT(&dataKey); | 
|---|
| 895 |  | 
|---|
| 896 |     if (tclStubs.tcl_WaitForEvent != tclOriginalNotifier.waitForEventProc) { | 
|---|
| 897 |         return tclStubs.tcl_WaitForEvent(timePtr); | 
|---|
| 898 |     } | 
|---|
| 899 |  | 
|---|
| 900 |     if (timePtr != NULL) { | 
|---|
| 901 |         /* | 
|---|
| 902 |          * TIP #233 (Virtualized Time). Is virtual time in effect? And do we | 
|---|
| 903 |          * actually have something to scale? If yes to both then we call the | 
|---|
| 904 |          * handler to do this scaling. | 
|---|
| 905 |          */ | 
|---|
| 906 |  | 
|---|
| 907 |         myTime.sec  = timePtr->sec; | 
|---|
| 908 |         myTime.usec = timePtr->usec; | 
|---|
| 909 |  | 
|---|
| 910 |         if (myTime.sec != 0 || myTime.usec != 0) { | 
|---|
| 911 |             (*tclScaleTimeProcPtr) (&myTime, tclTimeClientData); | 
|---|
| 912 |         } | 
|---|
| 913 |  | 
|---|
| 914 |         myTimePtr = &myTime; | 
|---|
| 915 |     } else { | 
|---|
| 916 |         myTimePtr = NULL; | 
|---|
| 917 |     } | 
|---|
| 918 |  | 
|---|
| 919 |     /* | 
|---|
| 920 |      * Start notifier thread if necessary. | 
|---|
| 921 |      */ | 
|---|
| 922 |  | 
|---|
| 923 |     LOCK_NOTIFIER_INIT; | 
|---|
| 924 |     if (!notifierCount) { | 
|---|
| 925 |         Tcl_Panic("Tcl_WaitForEvent: notifier not initialized"); | 
|---|
| 926 |     } | 
|---|
| 927 |     if (!notifierThread) { | 
|---|
| 928 |         int result; | 
|---|
| 929 |         pthread_attr_t attr; | 
|---|
| 930 |  | 
|---|
| 931 |         pthread_attr_init(&attr); | 
|---|
| 932 |         pthread_attr_setscope(&attr, PTHREAD_SCOPE_SYSTEM); | 
|---|
| 933 |         pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_JOINABLE); | 
|---|
| 934 |         pthread_attr_setstacksize(&attr, 60 * 1024); | 
|---|
| 935 |         result = pthread_create(¬ifierThread, &attr, | 
|---|
| 936 |                 (void * (*)(void *))NotifierThreadProc, NULL); | 
|---|
| 937 |         pthread_attr_destroy(&attr); | 
|---|
| 938 |         if (result || !notifierThread) { | 
|---|
| 939 |             Tcl_Panic("Tcl_WaitForEvent: unable to start notifier thread"); | 
|---|
| 940 |         } | 
|---|
| 941 |     } | 
|---|
| 942 |     UNLOCK_NOTIFIER_INIT; | 
|---|
| 943 |  | 
|---|
| 944 |     /* | 
|---|
| 945 |      * Place this thread on the list of interested threads, signal the | 
|---|
| 946 |      * notifier thread, and wait for a response or a timeout. | 
|---|
| 947 |      */ | 
|---|
| 948 |  | 
|---|
| 949 |     LOCK_NOTIFIER; | 
|---|
| 950 |     if (!tsdPtr->runLoop) { | 
|---|
| 951 |         Tcl_Panic("Tcl_WaitForEvent: CFRunLoop not initialized"); | 
|---|
| 952 |     } | 
|---|
| 953 |     waitForFiles = (tsdPtr->numFdBits > 0); | 
|---|
| 954 |     if (myTimePtr != NULL && myTimePtr->sec == 0 && myTimePtr->usec == 0) { | 
|---|
| 955 |         /* | 
|---|
| 956 |          * Cannot emulate a polling select with a polling condition variable. | 
|---|
| 957 |          * Instead, pretend to wait for files and tell the notifier thread | 
|---|
| 958 |          * what we are doing. The notifier thread makes sure it goes through | 
|---|
| 959 |          * select with its select mask in the same state as ours currently is. | 
|---|
| 960 |          * We block until that happens. | 
|---|
| 961 |          */ | 
|---|
| 962 |  | 
|---|
| 963 |         waitForFiles = 1; | 
|---|
| 964 |         tsdPtr->pollState = POLL_WANT; | 
|---|
| 965 |         myTimePtr = NULL; | 
|---|
| 966 |     } else { | 
|---|
| 967 |         tsdPtr->pollState = 0; | 
|---|
| 968 |     } | 
|---|
| 969 |  | 
|---|
| 970 |     if (waitForFiles) { | 
|---|
| 971 |         /* | 
|---|
| 972 |          * Add the ThreadSpecificData structure of this thread to the list of | 
|---|
| 973 |          * ThreadSpecificData structures of all threads that are waiting on | 
|---|
| 974 |          * file events. | 
|---|
| 975 |          */ | 
|---|
| 976 |  | 
|---|
| 977 |         tsdPtr->nextPtr = waitingListPtr; | 
|---|
| 978 |         if (waitingListPtr) { | 
|---|
| 979 |             waitingListPtr->prevPtr = tsdPtr; | 
|---|
| 980 |         } | 
|---|
| 981 |         tsdPtr->prevPtr = 0; | 
|---|
| 982 |         waitingListPtr = tsdPtr; | 
|---|
| 983 |         tsdPtr->onList = 1; | 
|---|
| 984 |  | 
|---|
| 985 |         write(triggerPipe, "", 1); | 
|---|
| 986 |     } | 
|---|
| 987 |  | 
|---|
| 988 |     FD_ZERO(&(tsdPtr->readyMasks.readable)); | 
|---|
| 989 |     FD_ZERO(&(tsdPtr->readyMasks.writable)); | 
|---|
| 990 |     FD_ZERO(&(tsdPtr->readyMasks.exceptional)); | 
|---|
| 991 |  | 
|---|
| 992 |     if (!tsdPtr->eventReady) { | 
|---|
| 993 |         CFTimeInterval waitTime; | 
|---|
| 994 |         CFStringRef runLoopMode; | 
|---|
| 995 |  | 
|---|
| 996 |         if (myTimePtr == NULL) { | 
|---|
| 997 |             waitTime = 1.0e10; /* Wait forever, as per CFRunLoop.c */ | 
|---|
| 998 |         } else { | 
|---|
| 999 |             waitTime = myTimePtr->sec + 1.0e-6 * myTimePtr->usec; | 
|---|
| 1000 |         } | 
|---|
| 1001 |         /* | 
|---|
| 1002 |          * If the run loop is already running (e.g. if Tcl_WaitForEvent was | 
|---|
| 1003 |          * called recursively), re-run it in a custom run loop mode containing | 
|---|
| 1004 |          * only the source for the notifier thread, otherwise wakeups from other | 
|---|
| 1005 |          * sources added to the common run loop modes might get lost. | 
|---|
| 1006 |          */ | 
|---|
| 1007 |         if ((runLoopMode = CFRunLoopCopyCurrentMode(tsdPtr->runLoop))) { | 
|---|
| 1008 |             CFRelease(runLoopMode); | 
|---|
| 1009 |             runLoopMode = tclEventsOnlyRunLoopMode; | 
|---|
| 1010 |         } else { | 
|---|
| 1011 |             runLoopMode = kCFRunLoopDefaultMode; | 
|---|
| 1012 |         } | 
|---|
| 1013 |         UNLOCK_NOTIFIER; | 
|---|
| 1014 |         CFRunLoopRunInMode(runLoopMode, waitTime, TRUE); | 
|---|
| 1015 |         LOCK_NOTIFIER; | 
|---|
| 1016 |     } | 
|---|
| 1017 |     tsdPtr->eventReady = 0; | 
|---|
| 1018 |  | 
|---|
| 1019 |     if (waitForFiles && tsdPtr->onList) { | 
|---|
| 1020 |         /* | 
|---|
| 1021 |          * Remove the ThreadSpecificData structure of this thread from the | 
|---|
| 1022 |          * waiting list. Alert the notifier thread to recompute its select | 
|---|
| 1023 |          * masks - skipping this caused a hang when trying to close a pipe | 
|---|
| 1024 |          * which the notifier thread was still doing a select on. | 
|---|
| 1025 |          */ | 
|---|
| 1026 |  | 
|---|
| 1027 |         if (tsdPtr->prevPtr) { | 
|---|
| 1028 |             tsdPtr->prevPtr->nextPtr = tsdPtr->nextPtr; | 
|---|
| 1029 |         } else { | 
|---|
| 1030 |             waitingListPtr = tsdPtr->nextPtr; | 
|---|
| 1031 |         } | 
|---|
| 1032 |         if (tsdPtr->nextPtr) { | 
|---|
| 1033 |             tsdPtr->nextPtr->prevPtr = tsdPtr->prevPtr; | 
|---|
| 1034 |         } | 
|---|
| 1035 |         tsdPtr->nextPtr = tsdPtr->prevPtr = NULL; | 
|---|
| 1036 |         tsdPtr->onList = 0; | 
|---|
| 1037 |         write(triggerPipe, "", 1); | 
|---|
| 1038 |     } | 
|---|
| 1039 |  | 
|---|
| 1040 |     /* | 
|---|
| 1041 |      * Queue all detected file events before returning. | 
|---|
| 1042 |      */ | 
|---|
| 1043 |  | 
|---|
| 1044 |     for (filePtr = tsdPtr->firstFileHandlerPtr; (filePtr != NULL); | 
|---|
| 1045 |             filePtr = filePtr->nextPtr) { | 
|---|
| 1046 |  | 
|---|
| 1047 |         mask = 0; | 
|---|
| 1048 |         if (FD_ISSET(filePtr->fd, &(tsdPtr->readyMasks.readable))) { | 
|---|
| 1049 |             mask |= TCL_READABLE; | 
|---|
| 1050 |         } | 
|---|
| 1051 |         if (FD_ISSET(filePtr->fd, &(tsdPtr->readyMasks.writable))) { | 
|---|
| 1052 |             mask |= TCL_WRITABLE; | 
|---|
| 1053 |         } | 
|---|
| 1054 |         if (FD_ISSET(filePtr->fd, &(tsdPtr->readyMasks.exceptional))) { | 
|---|
| 1055 |             mask |= TCL_EXCEPTION; | 
|---|
| 1056 |         } | 
|---|
| 1057 |  | 
|---|
| 1058 |         if (!mask) { | 
|---|
| 1059 |             continue; | 
|---|
| 1060 |         } | 
|---|
| 1061 |  | 
|---|
| 1062 |         /* | 
|---|
| 1063 |          * Don't bother to queue an event if the mask was previously non-zero | 
|---|
| 1064 |          * since an event must still be on the queue. | 
|---|
| 1065 |          */ | 
|---|
| 1066 |  | 
|---|
| 1067 |         if (filePtr->readyMask == 0) { | 
|---|
| 1068 |             fileEvPtr = (FileHandlerEvent *) ckalloc(sizeof(FileHandlerEvent)); | 
|---|
| 1069 |             fileEvPtr->header.proc = FileHandlerEventProc; | 
|---|
| 1070 |             fileEvPtr->fd = filePtr->fd; | 
|---|
| 1071 |             Tcl_QueueEvent((Tcl_Event *) fileEvPtr, TCL_QUEUE_TAIL); | 
|---|
| 1072 |         } | 
|---|
| 1073 |         filePtr->readyMask = mask; | 
|---|
| 1074 |     } | 
|---|
| 1075 |     UNLOCK_NOTIFIER; | 
|---|
| 1076 |     return 0; | 
|---|
| 1077 | } | 
|---|
| 1078 |  | 
|---|
| 1079 | /* | 
|---|
| 1080 |  *---------------------------------------------------------------------- | 
|---|
| 1081 |  * | 
|---|
| 1082 |  * NotifierThreadProc -- | 
|---|
| 1083 |  * | 
|---|
| 1084 |  *      This routine is the initial (and only) function executed by the | 
|---|
| 1085 |  *      special notifier thread. Its job is to wait for file descriptors to | 
|---|
| 1086 |  *      become readable or writable or to have an exception condition and then | 
|---|
| 1087 |  *      to notify other threads who are interested in this information by | 
|---|
| 1088 |  *      signalling a condition variable. Other threads can signal this | 
|---|
| 1089 |  *      notifier thread of a change in their interests by writing a single | 
|---|
| 1090 |  *      byte to a special pipe that the notifier thread is monitoring. | 
|---|
| 1091 |  * | 
|---|
| 1092 |  * Result: | 
|---|
| 1093 |  *      None. Once started, this routine never exits. It dies with the overall | 
|---|
| 1094 |  *      process. | 
|---|
| 1095 |  * | 
|---|
| 1096 |  * Side effects: | 
|---|
| 1097 |  *      The trigger pipe used to signal the notifier thread is created when | 
|---|
| 1098 |  *      the notifier thread first starts. | 
|---|
| 1099 |  * | 
|---|
| 1100 |  *---------------------------------------------------------------------- | 
|---|
| 1101 |  */ | 
|---|
| 1102 |  | 
|---|
| 1103 | static void | 
|---|
| 1104 | NotifierThreadProc( | 
|---|
| 1105 |     ClientData clientData)      /* Not used. */ | 
|---|
| 1106 | { | 
|---|
| 1107 |     ThreadSpecificData *tsdPtr; | 
|---|
| 1108 |     fd_set readableMask; | 
|---|
| 1109 |     fd_set writableMask; | 
|---|
| 1110 |     fd_set exceptionalMask; | 
|---|
| 1111 |     int i, numFdBits = 0; | 
|---|
| 1112 |     long found; | 
|---|
| 1113 |     struct timeval poll = {0., 0.}, *timePtr; | 
|---|
| 1114 |     char buf[2]; | 
|---|
| 1115 |  | 
|---|
| 1116 |     /* | 
|---|
| 1117 |      * Look for file events and report them to interested threads. | 
|---|
| 1118 |      */ | 
|---|
| 1119 |  | 
|---|
| 1120 |     while (1) { | 
|---|
| 1121 |         FD_ZERO(&readableMask); | 
|---|
| 1122 |         FD_ZERO(&writableMask); | 
|---|
| 1123 |         FD_ZERO(&exceptionalMask); | 
|---|
| 1124 |  | 
|---|
| 1125 |         /* | 
|---|
| 1126 |          * Compute the logical OR of the select masks from all the waiting | 
|---|
| 1127 |          * notifiers. | 
|---|
| 1128 |          */ | 
|---|
| 1129 |  | 
|---|
| 1130 |         LOCK_NOTIFIER; | 
|---|
| 1131 |         timePtr = NULL; | 
|---|
| 1132 |         for (tsdPtr = waitingListPtr; tsdPtr; tsdPtr = tsdPtr->nextPtr) { | 
|---|
| 1133 |             for (i = tsdPtr->numFdBits-1; i >= 0; --i) { | 
|---|
| 1134 |                 if (FD_ISSET(i, &(tsdPtr->checkMasks.readable))) { | 
|---|
| 1135 |                     FD_SET(i, &readableMask); | 
|---|
| 1136 |                 } | 
|---|
| 1137 |                 if (FD_ISSET(i, &(tsdPtr->checkMasks.writable))) { | 
|---|
| 1138 |                     FD_SET(i, &writableMask); | 
|---|
| 1139 |                 } | 
|---|
| 1140 |                 if (FD_ISSET(i, &(tsdPtr->checkMasks.exceptional))) { | 
|---|
| 1141 |                     FD_SET(i, &exceptionalMask); | 
|---|
| 1142 |                 } | 
|---|
| 1143 |             } | 
|---|
| 1144 |             if (tsdPtr->numFdBits > numFdBits) { | 
|---|
| 1145 |                 numFdBits = tsdPtr->numFdBits; | 
|---|
| 1146 |             } | 
|---|
| 1147 |             if (tsdPtr->pollState & POLL_WANT) { | 
|---|
| 1148 |                 /* | 
|---|
| 1149 |                  * Here we make sure we go through select() with the same mask | 
|---|
| 1150 |                  * bits that were present when the thread tried to poll. | 
|---|
| 1151 |                  */ | 
|---|
| 1152 |  | 
|---|
| 1153 |                 tsdPtr->pollState |= POLL_DONE; | 
|---|
| 1154 |                 timePtr = &poll; | 
|---|
| 1155 |             } | 
|---|
| 1156 |         } | 
|---|
| 1157 |         UNLOCK_NOTIFIER; | 
|---|
| 1158 |  | 
|---|
| 1159 |         /* | 
|---|
| 1160 |          * Set up the select mask to include the receive pipe. | 
|---|
| 1161 |          */ | 
|---|
| 1162 |  | 
|---|
| 1163 |         if (receivePipe >= numFdBits) { | 
|---|
| 1164 |             numFdBits = receivePipe + 1; | 
|---|
| 1165 |         } | 
|---|
| 1166 |         FD_SET(receivePipe, &readableMask); | 
|---|
| 1167 |  | 
|---|
| 1168 |         if (select(numFdBits, &readableMask, &writableMask, &exceptionalMask, | 
|---|
| 1169 |                 timePtr) == -1) { | 
|---|
| 1170 |             /* | 
|---|
| 1171 |              * Try again immediately on an error. | 
|---|
| 1172 |              */ | 
|---|
| 1173 |  | 
|---|
| 1174 |             continue; | 
|---|
| 1175 |         } | 
|---|
| 1176 |  | 
|---|
| 1177 |         /* | 
|---|
| 1178 |          * Alert any threads that are waiting on a ready file descriptor. | 
|---|
| 1179 |          */ | 
|---|
| 1180 |  | 
|---|
| 1181 |         LOCK_NOTIFIER; | 
|---|
| 1182 |         for (tsdPtr = waitingListPtr; tsdPtr; tsdPtr = tsdPtr->nextPtr) { | 
|---|
| 1183 |             found = 0; | 
|---|
| 1184 |  | 
|---|
| 1185 |             for (i = tsdPtr->numFdBits-1; i >= 0; --i) { | 
|---|
| 1186 |                 if (FD_ISSET(i, &(tsdPtr->checkMasks.readable)) | 
|---|
| 1187 |                         && FD_ISSET(i, &readableMask)) { | 
|---|
| 1188 |                     FD_SET(i, &(tsdPtr->readyMasks.readable)); | 
|---|
| 1189 |                     found = 1; | 
|---|
| 1190 |                 } | 
|---|
| 1191 |                 if (FD_ISSET(i, &(tsdPtr->checkMasks.writable)) | 
|---|
| 1192 |                         && FD_ISSET(i, &writableMask)) { | 
|---|
| 1193 |                     FD_SET(i, &(tsdPtr->readyMasks.writable)); | 
|---|
| 1194 |                     found = 1; | 
|---|
| 1195 |                 } | 
|---|
| 1196 |                 if (FD_ISSET(i, &(tsdPtr->checkMasks.exceptional)) | 
|---|
| 1197 |                         && FD_ISSET(i, &exceptionalMask)) { | 
|---|
| 1198 |                     FD_SET(i, &(tsdPtr->readyMasks.exceptional)); | 
|---|
| 1199 |                     found = 1; | 
|---|
| 1200 |                 } | 
|---|
| 1201 |             } | 
|---|
| 1202 |  | 
|---|
| 1203 |             if (found || (tsdPtr->pollState & POLL_DONE)) { | 
|---|
| 1204 |                 tsdPtr->eventReady = 1; | 
|---|
| 1205 |                 if (tsdPtr->onList) { | 
|---|
| 1206 |                     /* | 
|---|
| 1207 |                      * Remove the ThreadSpecificData structure of this thread | 
|---|
| 1208 |                      * from the waiting list. This prevents us from | 
|---|
| 1209 |                      * continuously spining on select until the other threads | 
|---|
| 1210 |                      * runs and services the file event. | 
|---|
| 1211 |                      */ | 
|---|
| 1212 |  | 
|---|
| 1213 |                     if (tsdPtr->prevPtr) { | 
|---|
| 1214 |                         tsdPtr->prevPtr->nextPtr = tsdPtr->nextPtr; | 
|---|
| 1215 |                     } else { | 
|---|
| 1216 |                         waitingListPtr = tsdPtr->nextPtr; | 
|---|
| 1217 |                     } | 
|---|
| 1218 |                     if (tsdPtr->nextPtr) { | 
|---|
| 1219 |                         tsdPtr->nextPtr->prevPtr = tsdPtr->prevPtr; | 
|---|
| 1220 |                     } | 
|---|
| 1221 |                     tsdPtr->nextPtr = tsdPtr->prevPtr = NULL; | 
|---|
| 1222 |                     tsdPtr->onList = 0; | 
|---|
| 1223 |                     tsdPtr->pollState = 0; | 
|---|
| 1224 |                 } | 
|---|
| 1225 |                 if (tsdPtr->runLoop) { | 
|---|
| 1226 |                     CFRunLoopSourceSignal(tsdPtr->runLoopSource); | 
|---|
| 1227 |                     CFRunLoopWakeUp(tsdPtr->runLoop); | 
|---|
| 1228 |                 } | 
|---|
| 1229 |             } | 
|---|
| 1230 |         } | 
|---|
| 1231 |         UNLOCK_NOTIFIER; | 
|---|
| 1232 |  | 
|---|
| 1233 |         /* | 
|---|
| 1234 |          * Consume the next byte from the notifier pipe if the pipe was | 
|---|
| 1235 |          * readable. Note that there may be multiple bytes pending, but to | 
|---|
| 1236 |          * avoid a race condition we only read one at a time. | 
|---|
| 1237 |          */ | 
|---|
| 1238 |  | 
|---|
| 1239 |         if (FD_ISSET(receivePipe, &readableMask)) { | 
|---|
| 1240 |             i = read(receivePipe, buf, 1); | 
|---|
| 1241 |  | 
|---|
| 1242 |             if ((i == 0) || ((i == 1) && (buf[0] == 'q'))) { | 
|---|
| 1243 |                 /* | 
|---|
| 1244 |                  * Someone closed the write end of the pipe or sent us a Quit | 
|---|
| 1245 |                  * message [Bug: 4139] and then closed the write end of the | 
|---|
| 1246 |                  * pipe so we need to shut down the notifier thread. | 
|---|
| 1247 |                  */ | 
|---|
| 1248 |  | 
|---|
| 1249 |                 break; | 
|---|
| 1250 |             } | 
|---|
| 1251 |         } | 
|---|
| 1252 |     } | 
|---|
| 1253 |     pthread_exit(0); | 
|---|
| 1254 | } | 
|---|
| 1255 |  | 
|---|
| 1256 | #ifdef HAVE_PTHREAD_ATFORK | 
|---|
| 1257 | /* | 
|---|
| 1258 |  *---------------------------------------------------------------------- | 
|---|
| 1259 |  * | 
|---|
| 1260 |  * AtForkPrepare -- | 
|---|
| 1261 |  * | 
|---|
| 1262 |  *      Lock the notifier in preparation for a fork. | 
|---|
| 1263 |  * | 
|---|
| 1264 |  * Results: | 
|---|
| 1265 |  *      None. | 
|---|
| 1266 |  * | 
|---|
| 1267 |  * Side effects: | 
|---|
| 1268 |  *      None. | 
|---|
| 1269 |  * | 
|---|
| 1270 |  *---------------------------------------------------------------------- | 
|---|
| 1271 |  */ | 
|---|
| 1272 |  | 
|---|
| 1273 | static void | 
|---|
| 1274 | AtForkPrepare(void) | 
|---|
| 1275 | { | 
|---|
| 1276 |     LOCK_NOTIFIER_INIT; | 
|---|
| 1277 |     LOCK_NOTIFIER; | 
|---|
| 1278 | } | 
|---|
| 1279 |  | 
|---|
| 1280 | /* | 
|---|
| 1281 |  *---------------------------------------------------------------------- | 
|---|
| 1282 |  * | 
|---|
| 1283 |  * AtForkParent -- | 
|---|
| 1284 |  * | 
|---|
| 1285 |  *      Unlock the notifier in the parent after a fork. | 
|---|
| 1286 |  * | 
|---|
| 1287 |  * Results: | 
|---|
| 1288 |  *      None. | 
|---|
| 1289 |  * | 
|---|
| 1290 |  * Side effects: | 
|---|
| 1291 |  *      None. | 
|---|
| 1292 |  * | 
|---|
| 1293 |  *---------------------------------------------------------------------- | 
|---|
| 1294 |  */ | 
|---|
| 1295 |  | 
|---|
| 1296 | static void | 
|---|
| 1297 | AtForkParent(void) | 
|---|
| 1298 | { | 
|---|
| 1299 |     UNLOCK_NOTIFIER; | 
|---|
| 1300 |     UNLOCK_NOTIFIER_INIT; | 
|---|
| 1301 | } | 
|---|
| 1302 |  | 
|---|
| 1303 | /* | 
|---|
| 1304 |  *---------------------------------------------------------------------- | 
|---|
| 1305 |  * | 
|---|
| 1306 |  * AtForkChild -- | 
|---|
| 1307 |  * | 
|---|
| 1308 |  *      Unlock and reinstall the notifier in the child after a fork. | 
|---|
| 1309 |  * | 
|---|
| 1310 |  * Results: | 
|---|
| 1311 |  *      None. | 
|---|
| 1312 |  * | 
|---|
| 1313 |  * Side effects: | 
|---|
| 1314 |  *      None. | 
|---|
| 1315 |  * | 
|---|
| 1316 |  *---------------------------------------------------------------------- | 
|---|
| 1317 |  */ | 
|---|
| 1318 |  | 
|---|
| 1319 | static void | 
|---|
| 1320 | AtForkChild(void) | 
|---|
| 1321 | { | 
|---|
| 1322 |     ThreadSpecificData *tsdPtr = TCL_TSD_INIT(&dataKey); | 
|---|
| 1323 |  | 
|---|
| 1324 |     UNLOCK_NOTIFIER; | 
|---|
| 1325 |     UNLOCK_NOTIFIER_INIT; | 
|---|
| 1326 |     if (tsdPtr->runLoop) { | 
|---|
| 1327 |         tsdPtr->runLoop = NULL; | 
|---|
| 1328 |         if (!noCFafterFork) { | 
|---|
| 1329 |             CFRunLoopSourceInvalidate(tsdPtr->runLoopSource); | 
|---|
| 1330 |         } | 
|---|
| 1331 |         CFRelease(tsdPtr->runLoopSource); | 
|---|
| 1332 |         tsdPtr->runLoopSource = NULL; | 
|---|
| 1333 |     } | 
|---|
| 1334 |     if (notifierCount > 0) { | 
|---|
| 1335 |         notifierCount = 0; | 
|---|
| 1336 |  | 
|---|
| 1337 |         /* | 
|---|
| 1338 |          * Assume that the return value of Tcl_InitNotifier in the child will | 
|---|
| 1339 |          * be identical to the one stored as clientData in tclNotify.c's | 
|---|
| 1340 |          * ThreadSpecificData by the parent's TclInitNotifier, so discard the | 
|---|
| 1341 |          * return value here. This assumption may require the fork() to be | 
|---|
| 1342 |          * executed in the main thread of the parent, otherwise | 
|---|
| 1343 |          * Tcl_AlertNotifier may break in the child. | 
|---|
| 1344 |          */ | 
|---|
| 1345 |  | 
|---|
| 1346 |         if (!noCFafterFork) { | 
|---|
| 1347 |             Tcl_InitNotifier(); | 
|---|
| 1348 |         } | 
|---|
| 1349 |     } | 
|---|
| 1350 | } | 
|---|
| 1351 | #endif /* HAVE_PTHREAD_ATFORK */ | 
|---|
| 1352 |  | 
|---|
| 1353 | #endif /* HAVE_COREFOUNDATION */ | 
|---|
| 1354 |  | 
|---|
| 1355 | /* | 
|---|
| 1356 |  * Local Variables: | 
|---|
| 1357 |  * mode: c | 
|---|
| 1358 |  * c-basic-offset: 4 | 
|---|
| 1359 |  * fill-column: 78 | 
|---|
| 1360 |  * End: | 
|---|
| 1361 |  */ | 
|---|