os/persistentdata/persistentstorage/sqlite3api/TEST/TCL/tcldistribution/macosx/tclMacOSXNotify.c
1.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000
1.2 +++ b/os/persistentdata/persistentstorage/sqlite3api/TEST/TCL/tcldistribution/macosx/tclMacOSXNotify.c Fri Jun 15 03:10:57 2012 +0200
1.3 @@ -0,0 +1,1281 @@
1.4 +/*
1.5 + * tclMacOSXNotify.c --
1.6 + *
1.7 + * This file contains the implementation of a merged CFRunLoop/select()
1.8 + * based notifier, which is the lowest-level part of the Tcl event loop.
1.9 + * This file works together with generic/tclNotify.c.
1.10 + *
1.11 + * Copyright (c) 1995-1997 Sun Microsystems, Inc.
1.12 + * Copyright 2001, Apple Computer, Inc.
1.13 + * Copyright (c) 2005-2007 Daniel A. Steffen <das@users.sourceforge.net>
1.14 + *
1.15 + * See the file "license.terms" for information on usage and redistribution of
1.16 + * this file, and for a DISCLAIMER OF ALL WARRANTIES.
1.17 + *
1.18 + * RCS: @(#) $Id: tclMacOSXNotify.c,v 1.1.2.12 2007/04/29 02:21:33 das Exp $
1.19 + */
1.20 +
1.21 +#include "tclInt.h"
1.22 +#include "tclPort.h"
1.23 +#ifdef HAVE_COREFOUNDATION /* Traditional unix select-based notifier is
1.24 + * in tclUnixNotfy.c */
1.25 +#include <CoreFoundation/CoreFoundation.h>
1.26 +#include <pthread.h>
1.27 +
1.28 +extern TclStubs tclStubs;
1.29 +extern Tcl_NotifierProcs tclOriginalNotifier;
1.30 +
1.31 +/*
1.32 + * This structure is used to keep track of the notifier info for a registered
1.33 + * file.
1.34 + */
1.35 +
1.36 +typedef struct FileHandler {
1.37 + int fd;
1.38 + int mask; /* Mask of desired events: TCL_READABLE,
1.39 + * etc. */
1.40 + int readyMask; /* Mask of events that have been seen since
1.41 + * the last time file handlers were invoked
1.42 + * for this file. */
1.43 + Tcl_FileProc *proc; /* Function to call, in the style of
1.44 + * Tcl_CreateFileHandler. */
1.45 + ClientData clientData; /* Argument to pass to proc. */
1.46 + struct FileHandler *nextPtr;/* Next in list of all files we care about. */
1.47 +} FileHandler;
1.48 +
1.49 +/*
1.50 + * The following structure is what is added to the Tcl event queue when file
1.51 + * handlers are ready to fire.
1.52 + */
1.53 +
1.54 +typedef struct FileHandlerEvent {
1.55 + Tcl_Event header; /* Information that is standard for all
1.56 + * events. */
1.57 + int fd; /* File descriptor that is ready. Used to find
1.58 + * the FileHandler structure for the file
1.59 + * (can't point directly to the FileHandler
1.60 + * structure because it could go away while
1.61 + * the event is queued). */
1.62 +} FileHandlerEvent;
1.63 +
1.64 +/*
1.65 + * The following structure contains a set of select() masks to track readable,
1.66 + * writable, and exceptional conditions.
1.67 + */
1.68 +
1.69 +typedef struct SelectMasks {
1.70 + fd_set readable;
1.71 + fd_set writable;
1.72 + fd_set exceptional;
1.73 +} SelectMasks;
1.74 +
1.75 +/*
1.76 + * The following static structure contains the state information for the
1.77 + * select based implementation of the Tcl notifier. One of these structures is
1.78 + * created for each thread that is using the notifier.
1.79 + */
1.80 +
1.81 +typedef struct ThreadSpecificData {
1.82 + FileHandler *firstFileHandlerPtr;
1.83 + /* Pointer to head of file handler list. */
1.84 + SelectMasks checkMasks; /* This structure is used to build up the
1.85 + * masks to be used in the next call to
1.86 + * select. Bits are set in response to calls
1.87 + * to Tcl_CreateFileHandler. */
1.88 + SelectMasks readyMasks; /* This array reflects the readable/writable
1.89 + * conditions that were found to exist by the
1.90 + * last call to select. */
1.91 + int numFdBits; /* Number of valid bits in checkMasks (one
1.92 + * more than highest fd for which
1.93 + * Tcl_WatchFile has been called). */
1.94 + int onList; /* True if it is in this list */
1.95 + unsigned int pollState; /* pollState is used to implement a polling
1.96 + * handshake between each thread and the
1.97 + * notifier thread. Bits defined below. */
1.98 + struct ThreadSpecificData *nextPtr, *prevPtr;
1.99 + /* All threads that are currently waiting on
1.100 + * an event have their ThreadSpecificData
1.101 + * structure on a doubly-linked listed formed
1.102 + * from these pointers. You must hold the
1.103 + * notifierLock before accessing these
1.104 + * fields. */
1.105 + CFRunLoopSourceRef runLoopSource;
1.106 + /* Any other thread alerts a notifier that an
1.107 + * event is ready to be processed by signaling
1.108 + * this CFRunLoopSource. */
1.109 + CFRunLoopRef runLoop; /* This thread's CFRunLoop, needs to be woken
1.110 + * up whenever the runLoopSource is
1.111 + * signaled. */
1.112 + int eventReady; /* True if an event is ready to be
1.113 + * processed. */
1.114 +} ThreadSpecificData;
1.115 +
1.116 +static Tcl_ThreadDataKey dataKey;
1.117 +
1.118 +/*
1.119 + * The following static indicates the number of threads that have initialized
1.120 + * notifiers.
1.121 + *
1.122 + * You must hold the notifierInitLock before accessing this variable.
1.123 + */
1.124 +
1.125 +static int notifierCount = 0;
1.126 +
1.127 +/*
1.128 + * The following variable points to the head of a doubly-linked list of
1.129 + * ThreadSpecificData structures for all threads that are currently waiting on
1.130 + * an event.
1.131 + *
1.132 + * You must hold the notifierLock before accessing this list.
1.133 + */
1.134 +
1.135 +static ThreadSpecificData *waitingListPtr = NULL;
1.136 +
1.137 +/*
1.138 + * The notifier thread spends all its time in select() waiting for a file
1.139 + * descriptor associated with one of the threads on the waitingListPtr list to
1.140 + * do something interesting. But if the contents of the waitingListPtr list
1.141 + * ever changes, we need to wake up and restart the select() system call. You
1.142 + * can wake up the notifier thread by writing a single byte to the file
1.143 + * descriptor defined below. This file descriptor is the input-end of a pipe
1.144 + * and the notifier thread is listening for data on the output-end of the same
1.145 + * pipe. Hence writing to this file descriptor will cause the select() system
1.146 + * call to return and wake up the notifier thread.
1.147 + *
1.148 + * You must hold the notifierLock lock before writing to the pipe.
1.149 + */
1.150 +
1.151 +static int triggerPipe = -1;
1.152 +static int receivePipe = -1; /* Output end of triggerPipe */
1.153 +
1.154 +/*
1.155 + * We use the Darwin-native spinlock API rather than pthread mutexes for
1.156 + * notifier locking: this radically simplifies the implementation and lowers
1.157 + * overhead. Note that these are not pure spinlocks, they employ various
1.158 + * strategies to back off and relinquish the processor, making them immune to
1.159 + * most priority-inversion livelocks (c.f. 'man 3 OSSpinLockLock' and Darwin
1.160 + * sources: xnu/osfmk/{ppc,i386}/commpage/spinlocks.s).
1.161 + */
1.162 +
1.163 +#if defined(HAVE_LIBKERN_OSATOMIC_H) && defined(HAVE_OSSPINLOCKLOCK)
1.164 +/*
1.165 + * Use OSSpinLock API where available (Tiger or later).
1.166 + */
1.167 +
1.168 +#include <libkern/OSAtomic.h>
1.169 +
1.170 +#if defined(HAVE_WEAK_IMPORT) && MAC_OS_X_VERSION_MIN_REQUIRED < 1040
1.171 +/*
1.172 + * Support for weakly importing spinlock API.
1.173 + */
1.174 +#define WEAK_IMPORT_SPINLOCKLOCK
1.175 +#if MAC_OS_X_VERSION_MAX_ALLOWED >= 1050
1.176 +#define VOLATILE volatile
1.177 +#else
1.178 +#define VOLATILE
1.179 +#endif
1.180 +#ifndef bool
1.181 +#define bool int
1.182 +#endif
1.183 +extern void OSSpinLockLock(VOLATILE OSSpinLock *lock) WEAK_IMPORT_ATTRIBUTE;
1.184 +extern void OSSpinLockUnlock(VOLATILE OSSpinLock *lock) WEAK_IMPORT_ATTRIBUTE;
1.185 +extern bool OSSpinLockTry(VOLATILE OSSpinLock *lock) WEAK_IMPORT_ATTRIBUTE;
1.186 +extern void _spin_lock(VOLATILE OSSpinLock *lock) WEAK_IMPORT_ATTRIBUTE;
1.187 +extern void _spin_unlock(VOLATILE OSSpinLock *lock) WEAK_IMPORT_ATTRIBUTE;
1.188 +extern bool _spin_lock_try(VOLATILE OSSpinLock *lock) WEAK_IMPORT_ATTRIBUTE;
1.189 +static void (* lockLock)(VOLATILE OSSpinLock *lock) = NULL;
1.190 +static void (* lockUnlock)(VOLATILE OSSpinLock *lock) = NULL;
1.191 +static bool (* lockTry)(VOLATILE OSSpinLock *lock) = NULL;
1.192 +#undef VOLATILE
1.193 +static pthread_once_t spinLockLockInitControl = PTHREAD_ONCE_INIT;
1.194 +static void SpinLockLockInit(void) {
1.195 + lockLock = OSSpinLockLock != NULL ? OSSpinLockLock : _spin_lock;
1.196 + lockUnlock = OSSpinLockUnlock != NULL ? OSSpinLockUnlock : _spin_unlock;
1.197 + lockTry = OSSpinLockTry != NULL ? OSSpinLockTry : _spin_lock_try;
1.198 + if (lockLock == NULL || lockUnlock == NULL) {
1.199 + Tcl_Panic("SpinLockLockInit: no spinlock API available");
1.200 + }
1.201 +}
1.202 +#define SpinLockLock(p) lockLock(p)
1.203 +#define SpinLockUnlock(p) lockUnlock(p)
1.204 +#define SpinLockTry(p) lockTry(p)
1.205 +#else
1.206 +#define SpinLockLock(p) OSSpinLockLock(p)
1.207 +#define SpinLockUnlock(p) OSSpinLockUnlock(p)
1.208 +#define SpinLockTry(p) OSSpinLockTry(p)
1.209 +#endif /* HAVE_WEAK_IMPORT */
1.210 +#define SPINLOCK_INIT OS_SPINLOCK_INIT
1.211 +
1.212 +#else
1.213 +/*
1.214 + * Otherwise, use commpage spinlock SPI directly.
1.215 + */
1.216 +
1.217 +typedef uint32_t OSSpinLock;
1.218 +extern void _spin_lock(OSSpinLock *lock);
1.219 +extern void _spin_unlock(OSSpinLock *lock);
1.220 +extern int _spin_lock_try(OSSpinLock *lock);
1.221 +#define SpinLockLock(p) _spin_lock(p)
1.222 +#define SpinLockUnlock(p) _spin_unlock(p)
1.223 +#define SpinLockTry(p) _spin_lock_try(p)
1.224 +#define SPINLOCK_INIT 0
1.225 +
1.226 +#endif /* HAVE_LIBKERN_OSATOMIC_H && HAVE_OSSPINLOCKLOCK */
1.227 +
1.228 +/*
1.229 + * These spinlocks lock access to the global notifier state.
1.230 + */
1.231 +
1.232 +static OSSpinLock notifierInitLock = SPINLOCK_INIT;
1.233 +static OSSpinLock notifierLock = SPINLOCK_INIT;
1.234 +
1.235 +/*
1.236 + * Macros abstracting notifier locking/unlocking
1.237 + */
1.238 +
1.239 +#define LOCK_NOTIFIER_INIT SpinLockLock(¬ifierInitLock)
1.240 +#define UNLOCK_NOTIFIER_INIT SpinLockUnlock(¬ifierInitLock)
1.241 +#define LOCK_NOTIFIER SpinLockLock(¬ifierLock)
1.242 +#define UNLOCK_NOTIFIER SpinLockUnlock(¬ifierLock)
1.243 +
1.244 +/*
1.245 + * The pollState bits
1.246 + * POLL_WANT is set by each thread before it waits on its condition
1.247 + * variable. It is checked by the notifier before it does select.
1.248 + * POLL_DONE is set by the notifier if it goes into select after seeing
1.249 + * POLL_WANT. The idea is to ensure it tries a select with the
1.250 + * same bits the initial thread had set.
1.251 + */
1.252 +
1.253 +#define POLL_WANT 0x1
1.254 +#define POLL_DONE 0x2
1.255 +
1.256 +/*
1.257 + * This is the thread ID of the notifier thread that does select.
1.258 + */
1.259 +
1.260 +static pthread_t notifierThread;
1.261 +
1.262 +/*
1.263 + * Custom run loop mode containing only the run loop source for the
1.264 + * notifier thread.
1.265 + */
1.266 +
1.267 +#ifndef TCL_EVENTS_ONLY_RUN_LOOP_MODE
1.268 +#define TCL_EVENTS_ONLY_RUN_LOOP_MODE "com.tcltk.tclEventsOnlyRunLoopMode"
1.269 +#endif
1.270 +#ifdef __CONSTANT_CFSTRINGS__
1.271 +#define tclEventsOnlyRunLoopMode CFSTR(TCL_EVENTS_ONLY_RUN_LOOP_MODE)
1.272 +#else
1.273 +static CFStringRef tclEventsOnlyRunLoopMode = NULL;
1.274 +#endif
1.275 +
1.276 +/*
1.277 + * Static routines defined in this file.
1.278 + */
1.279 +
1.280 +static void NotifierThreadProc(ClientData clientData)
1.281 + __attribute__ ((__noreturn__));
1.282 +static int FileHandlerEventProc(Tcl_Event *evPtr, int flags);
1.283 +
1.284 +#ifdef HAVE_PTHREAD_ATFORK
1.285 +static int atForkInit = 0;
1.286 +static void AtForkPrepare(void);
1.287 +static void AtForkParent(void);
1.288 +static void AtForkChild(void);
1.289 +#if defined(HAVE_WEAK_IMPORT) && MAC_OS_X_VERSION_MIN_REQUIRED < 1040
1.290 +/* Support for weakly importing pthread_atfork. */
1.291 +#define WEAK_IMPORT_PTHREAD_ATFORK
1.292 +extern int pthread_atfork(void (*prepare)(void), void (*parent)(void),
1.293 + void (*child)(void)) WEAK_IMPORT_ATTRIBUTE;
1.294 +#endif /* HAVE_WEAK_IMPORT */
1.295 +#endif /* HAVE_PTHREAD_ATFORK */
1.296 +
1.297 +/*
1.298 + *----------------------------------------------------------------------
1.299 + *
1.300 + * Tcl_InitNotifier --
1.301 + *
1.302 + * Initializes the platform specific notifier state.
1.303 + *
1.304 + * Results:
1.305 + * Returns a handle to the notifier state for this thread.
1.306 + *
1.307 + * Side effects:
1.308 + * None.
1.309 + *
1.310 + *----------------------------------------------------------------------
1.311 + */
1.312 +
1.313 +ClientData
1.314 +Tcl_InitNotifier(void)
1.315 +{
1.316 + ThreadSpecificData *tsdPtr = TCL_TSD_INIT(&dataKey);
1.317 +
1.318 + tsdPtr->eventReady = 0;
1.319 +
1.320 +#ifdef WEAK_IMPORT_SPINLOCKLOCK
1.321 + /*
1.322 + * Initialize support for weakly imported spinlock API.
1.323 + */
1.324 + if (pthread_once(&spinLockLockInitControl, SpinLockLockInit)) {
1.325 + Tcl_Panic("Tcl_InitNotifier: pthread_once failed");
1.326 + }
1.327 +#endif
1.328 +
1.329 +#ifndef __CONSTANT_CFSTRINGS__
1.330 + if (!tclEventsOnlyRunLoopMode) {
1.331 + tclEventsOnlyRunLoopMode = CFSTR(TCL_EVENTS_ONLY_RUN_LOOP_MODE);
1.332 + }
1.333 +#endif
1.334 +
1.335 + /*
1.336 + * Initialize CFRunLoopSource and add it to CFRunLoop of this thread.
1.337 + */
1.338 +
1.339 + if (!tsdPtr->runLoop) {
1.340 + CFRunLoopRef runLoop = CFRunLoopGetCurrent();
1.341 + CFRunLoopSourceRef runLoopSource;
1.342 + CFRunLoopSourceContext runLoopSourceContext;
1.343 +
1.344 + bzero(&runLoopSourceContext, sizeof(CFRunLoopSourceContext));
1.345 + runLoopSourceContext.info = tsdPtr;
1.346 + runLoopSource = CFRunLoopSourceCreate(NULL, 0, &runLoopSourceContext);
1.347 + if (!runLoopSource) {
1.348 + Tcl_Panic("Tcl_InitNotifier: could not create CFRunLoopSource");
1.349 + }
1.350 + CFRunLoopAddSource(runLoop, runLoopSource, kCFRunLoopCommonModes);
1.351 + CFRunLoopAddSource(runLoop, runLoopSource, tclEventsOnlyRunLoopMode);
1.352 + tsdPtr->runLoopSource = runLoopSource;
1.353 + tsdPtr->runLoop = runLoop;
1.354 + }
1.355 +
1.356 + LOCK_NOTIFIER_INIT;
1.357 +#ifdef HAVE_PTHREAD_ATFORK
1.358 + /*
1.359 + * Install pthread_atfork handlers to reinitialize the notifier in the
1.360 + * child of a fork.
1.361 + */
1.362 +
1.363 + if (
1.364 +#ifdef WEAK_IMPORT_PTHREAD_ATFORK
1.365 + pthread_atfork != NULL &&
1.366 +#endif
1.367 + !atForkInit) {
1.368 + int result = pthread_atfork(AtForkPrepare, AtForkParent, AtForkChild);
1.369 + if (result) {
1.370 + Tcl_Panic("Tcl_InitNotifier: pthread_atfork failed");
1.371 + }
1.372 + atForkInit = 1;
1.373 + }
1.374 +#endif
1.375 + if (notifierCount == 0) {
1.376 + int fds[2], status;
1.377 +
1.378 + /*
1.379 + * Initialize trigger pipe.
1.380 + */
1.381 +
1.382 + if (pipe(fds) != 0) {
1.383 + Tcl_Panic("Tcl_InitNotifier: could not create trigger pipe");
1.384 + }
1.385 +
1.386 + status = fcntl(fds[0], F_GETFL);
1.387 + status |= O_NONBLOCK;
1.388 + if (fcntl(fds[0], F_SETFL, status) < 0) {
1.389 + Tcl_Panic("Tcl_InitNotifier: could not make receive pipe non blocking");
1.390 + }
1.391 + status = fcntl(fds[1], F_GETFL);
1.392 + status |= O_NONBLOCK;
1.393 + if (fcntl(fds[1], F_SETFL, status) < 0) {
1.394 + Tcl_Panic("Tcl_InitNotifier: could not make trigger pipe non blocking");
1.395 + }
1.396 +
1.397 + receivePipe = fds[0];
1.398 + triggerPipe = fds[1];
1.399 +
1.400 + /*
1.401 + * Create notifier thread lazily in Tcl_WaitForEvent() to avoid
1.402 + * interfering with fork() followed immediately by execve()
1.403 + * (cannot execve() when more than one thread is present).
1.404 + */
1.405 +
1.406 + notifierThread = 0;
1.407 + }
1.408 + notifierCount++;
1.409 + UNLOCK_NOTIFIER_INIT;
1.410 +
1.411 + return (ClientData) tsdPtr;
1.412 +}
1.413 +
1.414 +/*
1.415 + *----------------------------------------------------------------------
1.416 + *
1.417 + * Tcl_FinalizeNotifier --
1.418 + *
1.419 + * This function is called to cleanup the notifier state before a thread
1.420 + * is terminated.
1.421 + *
1.422 + * Results:
1.423 + * None.
1.424 + *
1.425 + * Side effects:
1.426 + * May terminate the background notifier thread if this is the last
1.427 + * notifier instance.
1.428 + *
1.429 + *----------------------------------------------------------------------
1.430 + */
1.431 +
1.432 +void
1.433 +Tcl_FinalizeNotifier(
1.434 + ClientData clientData) /* Not used. */
1.435 +{
1.436 + ThreadSpecificData *tsdPtr = TCL_TSD_INIT(&dataKey);
1.437 +
1.438 + LOCK_NOTIFIER_INIT;
1.439 + notifierCount--;
1.440 +
1.441 + /*
1.442 + * If this is the last thread to use the notifier, close the notifier pipe
1.443 + * and wait for the background thread to terminate.
1.444 + */
1.445 +
1.446 + if (notifierCount == 0) {
1.447 + int result;
1.448 +
1.449 + if (triggerPipe < 0) {
1.450 + Tcl_Panic("Tcl_FinalizeNotifier: notifier pipe not initialized");
1.451 + }
1.452 +
1.453 + /*
1.454 + * Send "q" message to the notifier thread so that it will terminate.
1.455 + * The notifier will return from its call to select() and notice that
1.456 + * a "q" message has arrived, it will then close its side of the pipe
1.457 + * and terminate its thread. Note the we can not just close the pipe
1.458 + * and check for EOF in the notifier thread because if a background
1.459 + * child process was created with exec, select() would not register
1.460 + * the EOF on the pipe until the child processes had terminated. [Bug:
1.461 + * 4139] [Bug: 1222872]
1.462 + */
1.463 +
1.464 + write(triggerPipe, "q", 1);
1.465 + close(triggerPipe);
1.466 +
1.467 + if (notifierThread) {
1.468 + result = pthread_join(notifierThread, NULL);
1.469 + if (result) {
1.470 + Tcl_Panic("Tcl_FinalizeNotifier: unable to join notifier thread");
1.471 + }
1.472 + notifierThread = 0;
1.473 + }
1.474 +
1.475 + close(receivePipe);
1.476 + triggerPipe = -1;
1.477 + }
1.478 + UNLOCK_NOTIFIER_INIT;
1.479 +
1.480 + LOCK_NOTIFIER; /* for concurrency with Tcl_AlertNotifier */
1.481 + if (tsdPtr->runLoop) {
1.482 + tsdPtr->runLoop = NULL;
1.483 +
1.484 + /*
1.485 + * Remove runLoopSource from all CFRunLoops and release it.
1.486 + */
1.487 +
1.488 + CFRunLoopSourceInvalidate(tsdPtr->runLoopSource);
1.489 + CFRelease(tsdPtr->runLoopSource);
1.490 + tsdPtr->runLoopSource = NULL;
1.491 + }
1.492 + UNLOCK_NOTIFIER;
1.493 +}
1.494 +
1.495 +/*
1.496 + *----------------------------------------------------------------------
1.497 + *
1.498 + * Tcl_AlertNotifier --
1.499 + *
1.500 + * Wake up the specified notifier from any thread. This routine is called
1.501 + * by the platform independent notifier code whenever the Tcl_ThreadAlert
1.502 + * routine is called. This routine is guaranteed not to be called on a
1.503 + * given notifier after Tcl_FinalizeNotifier is called for that notifier.
1.504 + *
1.505 + * Results:
1.506 + * None.
1.507 + *
1.508 + * Side effects:
1.509 + * Signals the notifier condition variable for the specified notifier.
1.510 + *
1.511 + *----------------------------------------------------------------------
1.512 + */
1.513 +
1.514 +void
1.515 +Tcl_AlertNotifier(
1.516 + ClientData clientData)
1.517 +{
1.518 + ThreadSpecificData *tsdPtr = (ThreadSpecificData *) clientData;
1.519 +
1.520 + LOCK_NOTIFIER;
1.521 + if (tsdPtr->runLoop) {
1.522 + tsdPtr->eventReady = 1;
1.523 + CFRunLoopSourceSignal(tsdPtr->runLoopSource);
1.524 + CFRunLoopWakeUp(tsdPtr->runLoop);
1.525 + }
1.526 + UNLOCK_NOTIFIER;
1.527 +}
1.528 +
1.529 +/*
1.530 + *----------------------------------------------------------------------
1.531 + *
1.532 + * Tcl_SetTimer --
1.533 + *
1.534 + * This function sets the current notifier timer value. This interface is
1.535 + * not implemented in this notifier because we are always running inside
1.536 + * of Tcl_DoOneEvent.
1.537 + *
1.538 + * Results:
1.539 + * None.
1.540 + *
1.541 + * Side effects:
1.542 + * None.
1.543 + *
1.544 + *----------------------------------------------------------------------
1.545 + */
1.546 +
1.547 +void
1.548 +Tcl_SetTimer(
1.549 + Tcl_Time *timePtr) /* Timeout value, may be NULL. */
1.550 +{
1.551 + /*
1.552 + * The interval timer doesn't do anything in this implementation, because
1.553 + * the only event loop is via Tcl_DoOneEvent, which passes timeout values
1.554 + * to Tcl_WaitForEvent.
1.555 + */
1.556 +
1.557 + if (tclStubs.tcl_SetTimer != tclOriginalNotifier.setTimerProc) {
1.558 + tclStubs.tcl_SetTimer(timePtr);
1.559 + }
1.560 +}
1.561 +
1.562 +/*
1.563 + *----------------------------------------------------------------------
1.564 + *
1.565 + * Tcl_ServiceModeHook --
1.566 + *
1.567 + * This function is invoked whenever the service mode changes.
1.568 + *
1.569 + * Results:
1.570 + * None.
1.571 + *
1.572 + * Side effects:
1.573 + * None.
1.574 + *
1.575 + *----------------------------------------------------------------------
1.576 + */
1.577 +
1.578 +void
1.579 +Tcl_ServiceModeHook(
1.580 + int mode) /* Either TCL_SERVICE_ALL, or
1.581 + * TCL_SERVICE_NONE. */
1.582 +{
1.583 +}
1.584 +
1.585 +/*
1.586 + *----------------------------------------------------------------------
1.587 + *
1.588 + * Tcl_CreateFileHandler --
1.589 + *
1.590 + * This function registers a file handler with the select notifier.
1.591 + *
1.592 + * Results:
1.593 + * None.
1.594 + *
1.595 + * Side effects:
1.596 + * Creates a new file handler structure.
1.597 + *
1.598 + *----------------------------------------------------------------------
1.599 + */
1.600 +
1.601 +void
1.602 +Tcl_CreateFileHandler(
1.603 + int fd, /* Handle of stream to watch. */
1.604 + int mask, /* OR'ed combination of TCL_READABLE,
1.605 + * TCL_WRITABLE, and TCL_EXCEPTION: indicates
1.606 + * conditions under which proc should be
1.607 + * called. */
1.608 + Tcl_FileProc *proc, /* Function to call for each selected
1.609 + * event. */
1.610 + ClientData clientData) /* Arbitrary data to pass to proc. */
1.611 +{
1.612 + ThreadSpecificData *tsdPtr = TCL_TSD_INIT(&dataKey);
1.613 + FileHandler *filePtr;
1.614 +
1.615 + if (tclStubs.tcl_CreateFileHandler !=
1.616 + tclOriginalNotifier.createFileHandlerProc) {
1.617 + tclStubs.tcl_CreateFileHandler(fd, mask, proc, clientData);
1.618 + return;
1.619 + }
1.620 +
1.621 + for (filePtr = tsdPtr->firstFileHandlerPtr; filePtr != NULL;
1.622 + filePtr = filePtr->nextPtr) {
1.623 + if (filePtr->fd == fd) {
1.624 + break;
1.625 + }
1.626 + }
1.627 + if (filePtr == NULL) {
1.628 + filePtr = (FileHandler*) ckalloc(sizeof(FileHandler));
1.629 + filePtr->fd = fd;
1.630 + filePtr->readyMask = 0;
1.631 + filePtr->nextPtr = tsdPtr->firstFileHandlerPtr;
1.632 + tsdPtr->firstFileHandlerPtr = filePtr;
1.633 + }
1.634 + filePtr->proc = proc;
1.635 + filePtr->clientData = clientData;
1.636 + filePtr->mask = mask;
1.637 +
1.638 + /*
1.639 + * Update the check masks for this file.
1.640 + */
1.641 +
1.642 + if (mask & TCL_READABLE) {
1.643 + FD_SET(fd, &(tsdPtr->checkMasks.readable));
1.644 + } else {
1.645 + FD_CLR(fd, &(tsdPtr->checkMasks.readable));
1.646 + }
1.647 + if (mask & TCL_WRITABLE) {
1.648 + FD_SET(fd, &(tsdPtr->checkMasks.writable));
1.649 + } else {
1.650 + FD_CLR(fd, &(tsdPtr->checkMasks.writable));
1.651 + }
1.652 + if (mask & TCL_EXCEPTION) {
1.653 + FD_SET(fd, &(tsdPtr->checkMasks.exceptional));
1.654 + } else {
1.655 + FD_CLR(fd, &(tsdPtr->checkMasks.exceptional));
1.656 + }
1.657 + if (tsdPtr->numFdBits <= fd) {
1.658 + tsdPtr->numFdBits = fd+1;
1.659 + }
1.660 +}
1.661 +
1.662 +/*
1.663 + *----------------------------------------------------------------------
1.664 + *
1.665 + * Tcl_DeleteFileHandler --
1.666 + *
1.667 + * Cancel a previously-arranged callback arrangement for a file.
1.668 + *
1.669 + * Results:
1.670 + * None.
1.671 + *
1.672 + * Side effects:
1.673 + * If a callback was previously registered on file, remove it.
1.674 + *
1.675 + *----------------------------------------------------------------------
1.676 + */
1.677 +
1.678 +void
1.679 +Tcl_DeleteFileHandler(
1.680 + int fd) /* Stream id for which to remove callback
1.681 + * function. */
1.682 +{
1.683 + FileHandler *filePtr, *prevPtr;
1.684 + int i;
1.685 + ThreadSpecificData *tsdPtr = TCL_TSD_INIT(&dataKey);
1.686 +
1.687 + if (tclStubs.tcl_DeleteFileHandler !=
1.688 + tclOriginalNotifier.deleteFileHandlerProc) {
1.689 + tclStubs.tcl_DeleteFileHandler(fd);
1.690 + return;
1.691 + }
1.692 +
1.693 + /*
1.694 + * Find the entry for the given file (and return if there isn't one).
1.695 + */
1.696 +
1.697 + for (prevPtr = NULL, filePtr = tsdPtr->firstFileHandlerPtr; ;
1.698 + prevPtr = filePtr, filePtr = filePtr->nextPtr) {
1.699 + if (filePtr == NULL) {
1.700 + return;
1.701 + }
1.702 + if (filePtr->fd == fd) {
1.703 + break;
1.704 + }
1.705 + }
1.706 +
1.707 + /*
1.708 + * Update the check masks for this file.
1.709 + */
1.710 +
1.711 + if (filePtr->mask & TCL_READABLE) {
1.712 + FD_CLR(fd, &(tsdPtr->checkMasks.readable));
1.713 + }
1.714 + if (filePtr->mask & TCL_WRITABLE) {
1.715 + FD_CLR(fd, &(tsdPtr->checkMasks.writable));
1.716 + }
1.717 + if (filePtr->mask & TCL_EXCEPTION) {
1.718 + FD_CLR(fd, &(tsdPtr->checkMasks.exceptional));
1.719 + }
1.720 +
1.721 + /*
1.722 + * Find current max fd.
1.723 + */
1.724 +
1.725 + if (fd+1 == tsdPtr->numFdBits) {
1.726 + tsdPtr->numFdBits = 0;
1.727 + for (i = fd-1; i >= 0; i--) {
1.728 + if (FD_ISSET(i, &(tsdPtr->checkMasks.readable))
1.729 + || FD_ISSET(i, &(tsdPtr->checkMasks.writable))
1.730 + || FD_ISSET(i, &(tsdPtr->checkMasks.exceptional))) {
1.731 + tsdPtr->numFdBits = i+1;
1.732 + break;
1.733 + }
1.734 + }
1.735 + }
1.736 +
1.737 + /*
1.738 + * Clean up information in the callback record.
1.739 + */
1.740 +
1.741 + if (prevPtr == NULL) {
1.742 + tsdPtr->firstFileHandlerPtr = filePtr->nextPtr;
1.743 + } else {
1.744 + prevPtr->nextPtr = filePtr->nextPtr;
1.745 + }
1.746 + ckfree((char *) filePtr);
1.747 +}
1.748 +
1.749 +/*
1.750 + *----------------------------------------------------------------------
1.751 + *
1.752 + * FileHandlerEventProc --
1.753 + *
1.754 + * This function is called by Tcl_ServiceEvent when a file event reaches
1.755 + * the front of the event queue. This function is responsible for
1.756 + * actually handling the event by invoking the callback for the file
1.757 + * handler.
1.758 + *
1.759 + * Results:
1.760 + * Returns 1 if the event was handled, meaning it should be removed from
1.761 + * the queue. Returns 0 if the event was not handled, meaning it should
1.762 + * stay on the queue. The only time the event isn't handled is if the
1.763 + * TCL_FILE_EVENTS flag bit isn't set.
1.764 + *
1.765 + * Side effects:
1.766 + * Whatever the file handler's callback function does.
1.767 + *
1.768 + *----------------------------------------------------------------------
1.769 + */
1.770 +
1.771 +static int
1.772 +FileHandlerEventProc(
1.773 + Tcl_Event *evPtr, /* Event to service. */
1.774 + int flags) /* Flags that indicate what events to handle,
1.775 + * such as TCL_FILE_EVENTS. */
1.776 +{
1.777 + int mask;
1.778 + FileHandler *filePtr;
1.779 + FileHandlerEvent *fileEvPtr = (FileHandlerEvent *) evPtr;
1.780 + ThreadSpecificData *tsdPtr;
1.781 +
1.782 + if (!(flags & TCL_FILE_EVENTS)) {
1.783 + return 0;
1.784 + }
1.785 +
1.786 + /*
1.787 + * Search through the file handlers to find the one whose handle matches
1.788 + * the event. We do this rather than keeping a pointer to the file handler
1.789 + * directly in the event, so that the handler can be deleted while the
1.790 + * event is queued without leaving a dangling pointer.
1.791 + */
1.792 +
1.793 + tsdPtr = TCL_TSD_INIT(&dataKey);
1.794 + for (filePtr = tsdPtr->firstFileHandlerPtr; filePtr != NULL;
1.795 + filePtr = filePtr->nextPtr) {
1.796 + if (filePtr->fd != fileEvPtr->fd) {
1.797 + continue;
1.798 + }
1.799 +
1.800 + /*
1.801 + * The code is tricky for two reasons:
1.802 + * 1. The file handler's desired events could have changed since the
1.803 + * time when the event was queued, so AND the ready mask with the
1.804 + * desired mask.
1.805 + * 2. The file could have been closed and re-opened since the time
1.806 + * when the event was queued. This is why the ready mask is stored
1.807 + * in the file handler rather than the queued event: it will be
1.808 + * zeroed when a new file handler is created for the newly opened
1.809 + * file.
1.810 + */
1.811 +
1.812 + mask = filePtr->readyMask & filePtr->mask;
1.813 + filePtr->readyMask = 0;
1.814 + if (mask != 0) {
1.815 + (*filePtr->proc)(filePtr->clientData, mask);
1.816 + }
1.817 + break;
1.818 + }
1.819 + return 1;
1.820 +}
1.821 +
1.822 +/*
1.823 + *----------------------------------------------------------------------
1.824 + *
1.825 + * Tcl_WaitForEvent --
1.826 + *
1.827 + * This function is called by Tcl_DoOneEvent to wait for new events on
1.828 + * the message queue. If the block time is 0, then Tcl_WaitForEvent just
1.829 + * polls without blocking.
1.830 + *
1.831 + * Results:
1.832 + * Returns -1 if the select would block forever, otherwise returns 0.
1.833 + *
1.834 + * Side effects:
1.835 + * Queues file events that are detected by the select.
1.836 + *
1.837 + *----------------------------------------------------------------------
1.838 + */
1.839 +
1.840 +int
1.841 +Tcl_WaitForEvent(
1.842 + Tcl_Time *timePtr) /* Maximum block time, or NULL. */
1.843 +{
1.844 + FileHandler *filePtr;
1.845 + FileHandlerEvent *fileEvPtr;
1.846 + int mask;
1.847 + int waitForFiles;
1.848 + ThreadSpecificData *tsdPtr = TCL_TSD_INIT(&dataKey);
1.849 +
1.850 + if (tclStubs.tcl_WaitForEvent != tclOriginalNotifier.waitForEventProc) {
1.851 + return tclStubs.tcl_WaitForEvent(timePtr);
1.852 + }
1.853 +
1.854 + /*
1.855 + * Start notifier thread if necessary.
1.856 + */
1.857 +
1.858 + LOCK_NOTIFIER_INIT;
1.859 + if (!notifierCount) {
1.860 + Tcl_Panic("Tcl_WaitForEvent: notifier not initialized");
1.861 + }
1.862 + if (!notifierThread) {
1.863 + int result;
1.864 + pthread_attr_t attr;
1.865 +
1.866 + pthread_attr_init(&attr);
1.867 + pthread_attr_setscope(&attr, PTHREAD_SCOPE_SYSTEM);
1.868 + pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_JOINABLE);
1.869 + pthread_attr_setstacksize(&attr, 60 * 1024);
1.870 + result = pthread_create(¬ifierThread, &attr,
1.871 + (void * (*)(void *))NotifierThreadProc, NULL);
1.872 + pthread_attr_destroy(&attr);
1.873 + if (result || !notifierThread) {
1.874 + Tcl_Panic("Tcl_WaitForEvent: unable to start notifier thread");
1.875 + }
1.876 + }
1.877 + UNLOCK_NOTIFIER_INIT;
1.878 +
1.879 + /*
1.880 + * Place this thread on the list of interested threads, signal the
1.881 + * notifier thread, and wait for a response or a timeout.
1.882 + */
1.883 +
1.884 + LOCK_NOTIFIER;
1.885 + if (!tsdPtr->runLoop) {
1.886 + Tcl_Panic("Tcl_WaitForEvent: CFRunLoop not initialized");
1.887 + }
1.888 + waitForFiles = (tsdPtr->numFdBits > 0);
1.889 + if (timePtr != NULL && timePtr->sec == 0 && timePtr->usec == 0) {
1.890 + /*
1.891 + * Cannot emulate a polling select with a polling condition variable.
1.892 + * Instead, pretend to wait for files and tell the notifier thread
1.893 + * what we are doing. The notifier thread makes sure it goes through
1.894 + * select with its select mask in the same state as ours currently is.
1.895 + * We block until that happens.
1.896 + */
1.897 +
1.898 + waitForFiles = 1;
1.899 + tsdPtr->pollState = POLL_WANT;
1.900 + timePtr = NULL;
1.901 + } else {
1.902 + tsdPtr->pollState = 0;
1.903 + }
1.904 +
1.905 + if (waitForFiles) {
1.906 + /*
1.907 + * Add the ThreadSpecificData structure of this thread to the list of
1.908 + * ThreadSpecificData structures of all threads that are waiting on
1.909 + * file events.
1.910 + */
1.911 +
1.912 + tsdPtr->nextPtr = waitingListPtr;
1.913 + if (waitingListPtr) {
1.914 + waitingListPtr->prevPtr = tsdPtr;
1.915 + }
1.916 + tsdPtr->prevPtr = 0;
1.917 + waitingListPtr = tsdPtr;
1.918 + tsdPtr->onList = 1;
1.919 +
1.920 + write(triggerPipe, "", 1);
1.921 + }
1.922 +
1.923 + FD_ZERO(&(tsdPtr->readyMasks.readable));
1.924 + FD_ZERO(&(tsdPtr->readyMasks.writable));
1.925 + FD_ZERO(&(tsdPtr->readyMasks.exceptional));
1.926 +
1.927 + if (!tsdPtr->eventReady) {
1.928 + CFTimeInterval waitTime;
1.929 + CFStringRef runLoopMode;
1.930 +
1.931 + if (timePtr == NULL) {
1.932 + waitTime = 1.0e10; /* Wait forever, as per CFRunLoop.c */
1.933 + } else {
1.934 + waitTime = timePtr->sec + 1.0e-6 * timePtr->usec;
1.935 + }
1.936 + /*
1.937 + * If the run loop is already running (e.g. if Tcl_WaitForEvent was
1.938 + * called recursively), re-run it in a custom run loop mode containing
1.939 + * only the source for the notifier thread, otherwise wakeups from other
1.940 + * sources added to the common run loop modes might get lost.
1.941 + */
1.942 + if ((runLoopMode = CFRunLoopCopyCurrentMode(tsdPtr->runLoop))) {
1.943 + CFRelease(runLoopMode);
1.944 + runLoopMode = tclEventsOnlyRunLoopMode;
1.945 + } else {
1.946 + runLoopMode = kCFRunLoopDefaultMode;
1.947 + }
1.948 + UNLOCK_NOTIFIER;
1.949 + CFRunLoopRunInMode(runLoopMode, waitTime, TRUE);
1.950 + LOCK_NOTIFIER;
1.951 + }
1.952 + tsdPtr->eventReady = 0;
1.953 +
1.954 + if (waitForFiles && tsdPtr->onList) {
1.955 + /*
1.956 + * Remove the ThreadSpecificData structure of this thread from the
1.957 + * waiting list. Alert the notifier thread to recompute its select
1.958 + * masks - skipping this caused a hang when trying to close a pipe
1.959 + * which the notifier thread was still doing a select on.
1.960 + */
1.961 +
1.962 + if (tsdPtr->prevPtr) {
1.963 + tsdPtr->prevPtr->nextPtr = tsdPtr->nextPtr;
1.964 + } else {
1.965 + waitingListPtr = tsdPtr->nextPtr;
1.966 + }
1.967 + if (tsdPtr->nextPtr) {
1.968 + tsdPtr->nextPtr->prevPtr = tsdPtr->prevPtr;
1.969 + }
1.970 + tsdPtr->nextPtr = tsdPtr->prevPtr = NULL;
1.971 + tsdPtr->onList = 0;
1.972 + write(triggerPipe, "", 1);
1.973 + }
1.974 +
1.975 + /*
1.976 + * Queue all detected file events before returning.
1.977 + */
1.978 +
1.979 + for (filePtr = tsdPtr->firstFileHandlerPtr; (filePtr != NULL);
1.980 + filePtr = filePtr->nextPtr) {
1.981 +
1.982 + mask = 0;
1.983 + if (FD_ISSET(filePtr->fd, &(tsdPtr->readyMasks.readable))) {
1.984 + mask |= TCL_READABLE;
1.985 + }
1.986 + if (FD_ISSET(filePtr->fd, &(tsdPtr->readyMasks.writable))) {
1.987 + mask |= TCL_WRITABLE;
1.988 + }
1.989 + if (FD_ISSET(filePtr->fd, &(tsdPtr->readyMasks.exceptional))) {
1.990 + mask |= TCL_EXCEPTION;
1.991 + }
1.992 +
1.993 + if (!mask) {
1.994 + continue;
1.995 + }
1.996 +
1.997 + /*
1.998 + * Don't bother to queue an event if the mask was previously non-zero
1.999 + * since an event must still be on the queue.
1.1000 + */
1.1001 +
1.1002 + if (filePtr->readyMask == 0) {
1.1003 + fileEvPtr = (FileHandlerEvent *) ckalloc(sizeof(FileHandlerEvent));
1.1004 + fileEvPtr->header.proc = FileHandlerEventProc;
1.1005 + fileEvPtr->fd = filePtr->fd;
1.1006 + Tcl_QueueEvent((Tcl_Event *) fileEvPtr, TCL_QUEUE_TAIL);
1.1007 + }
1.1008 + filePtr->readyMask = mask;
1.1009 + }
1.1010 + UNLOCK_NOTIFIER;
1.1011 + return 0;
1.1012 +}
1.1013 +
1.1014 +/*
1.1015 + *----------------------------------------------------------------------
1.1016 + *
1.1017 + * NotifierThreadProc --
1.1018 + *
1.1019 + * This routine is the initial (and only) function executed by the
1.1020 + * special notifier thread. Its job is to wait for file descriptors to
1.1021 + * become readable or writable or to have an exception condition and then
1.1022 + * to notify other threads who are interested in this information by
1.1023 + * signalling a condition variable. Other threads can signal this
1.1024 + * notifier thread of a change in their interests by writing a single
1.1025 + * byte to a special pipe that the notifier thread is monitoring.
1.1026 + *
1.1027 + * Result:
1.1028 + * None. Once started, this routine never exits. It dies with the overall
1.1029 + * process.
1.1030 + *
1.1031 + * Side effects:
1.1032 + * The trigger pipe used to signal the notifier thread is created when
1.1033 + * the notifier thread first starts.
1.1034 + *
1.1035 + *----------------------------------------------------------------------
1.1036 + */
1.1037 +
1.1038 +static void
1.1039 +NotifierThreadProc(
1.1040 + ClientData clientData) /* Not used. */
1.1041 +{
1.1042 + ThreadSpecificData *tsdPtr;
1.1043 + fd_set readableMask;
1.1044 + fd_set writableMask;
1.1045 + fd_set exceptionalMask;
1.1046 + int i, numFdBits = 0;
1.1047 + long found;
1.1048 + struct timeval poll = {0., 0.}, *timePtr;
1.1049 + char buf[2];
1.1050 +
1.1051 + /*
1.1052 + * Look for file events and report them to interested threads.
1.1053 + */
1.1054 +
1.1055 + while (1) {
1.1056 + FD_ZERO(&readableMask);
1.1057 + FD_ZERO(&writableMask);
1.1058 + FD_ZERO(&exceptionalMask);
1.1059 +
1.1060 + /*
1.1061 + * Compute the logical OR of the select masks from all the waiting
1.1062 + * notifiers.
1.1063 + */
1.1064 +
1.1065 + LOCK_NOTIFIER;
1.1066 + timePtr = NULL;
1.1067 + for (tsdPtr = waitingListPtr; tsdPtr; tsdPtr = tsdPtr->nextPtr) {
1.1068 + for (i = tsdPtr->numFdBits-1; i >= 0; --i) {
1.1069 + if (FD_ISSET(i, &(tsdPtr->checkMasks.readable))) {
1.1070 + FD_SET(i, &readableMask);
1.1071 + }
1.1072 + if (FD_ISSET(i, &(tsdPtr->checkMasks.writable))) {
1.1073 + FD_SET(i, &writableMask);
1.1074 + }
1.1075 + if (FD_ISSET(i, &(tsdPtr->checkMasks.exceptional))) {
1.1076 + FD_SET(i, &exceptionalMask);
1.1077 + }
1.1078 + }
1.1079 + if (tsdPtr->numFdBits > numFdBits) {
1.1080 + numFdBits = tsdPtr->numFdBits;
1.1081 + }
1.1082 + if (tsdPtr->pollState & POLL_WANT) {
1.1083 + /*
1.1084 + * Here we make sure we go through select() with the same mask
1.1085 + * bits that were present when the thread tried to poll.
1.1086 + */
1.1087 +
1.1088 + tsdPtr->pollState |= POLL_DONE;
1.1089 + timePtr = &poll;
1.1090 + }
1.1091 + }
1.1092 + UNLOCK_NOTIFIER;
1.1093 +
1.1094 + /*
1.1095 + * Set up the select mask to include the receive pipe.
1.1096 + */
1.1097 +
1.1098 + if (receivePipe >= numFdBits) {
1.1099 + numFdBits = receivePipe + 1;
1.1100 + }
1.1101 + FD_SET(receivePipe, &readableMask);
1.1102 +
1.1103 + if (select(numFdBits, &readableMask, &writableMask, &exceptionalMask,
1.1104 + timePtr) == -1) {
1.1105 + /*
1.1106 + * Try again immediately on an error.
1.1107 + */
1.1108 +
1.1109 + continue;
1.1110 + }
1.1111 +
1.1112 + /*
1.1113 + * Alert any threads that are waiting on a ready file descriptor.
1.1114 + */
1.1115 +
1.1116 + LOCK_NOTIFIER;
1.1117 + for (tsdPtr = waitingListPtr; tsdPtr; tsdPtr = tsdPtr->nextPtr) {
1.1118 + found = 0;
1.1119 +
1.1120 + for (i = tsdPtr->numFdBits-1; i >= 0; --i) {
1.1121 + if (FD_ISSET(i, &(tsdPtr->checkMasks.readable))
1.1122 + && FD_ISSET(i, &readableMask)) {
1.1123 + FD_SET(i, &(tsdPtr->readyMasks.readable));
1.1124 + found = 1;
1.1125 + }
1.1126 + if (FD_ISSET(i, &(tsdPtr->checkMasks.writable))
1.1127 + && FD_ISSET(i, &writableMask)) {
1.1128 + FD_SET(i, &(tsdPtr->readyMasks.writable));
1.1129 + found = 1;
1.1130 + }
1.1131 + if (FD_ISSET(i, &(tsdPtr->checkMasks.exceptional))
1.1132 + && FD_ISSET(i, &exceptionalMask)) {
1.1133 + FD_SET(i, &(tsdPtr->readyMasks.exceptional));
1.1134 + found = 1;
1.1135 + }
1.1136 + }
1.1137 +
1.1138 + if (found || (tsdPtr->pollState & POLL_DONE)) {
1.1139 + tsdPtr->eventReady = 1;
1.1140 + if (tsdPtr->onList) {
1.1141 + /*
1.1142 + * Remove the ThreadSpecificData structure of this thread
1.1143 + * from the waiting list. This prevents us from
1.1144 + * continuously spining on select until the other threads
1.1145 + * runs and services the file event.
1.1146 + */
1.1147 +
1.1148 + if (tsdPtr->prevPtr) {
1.1149 + tsdPtr->prevPtr->nextPtr = tsdPtr->nextPtr;
1.1150 + } else {
1.1151 + waitingListPtr = tsdPtr->nextPtr;
1.1152 + }
1.1153 + if (tsdPtr->nextPtr) {
1.1154 + tsdPtr->nextPtr->prevPtr = tsdPtr->prevPtr;
1.1155 + }
1.1156 + tsdPtr->nextPtr = tsdPtr->prevPtr = NULL;
1.1157 + tsdPtr->onList = 0;
1.1158 + tsdPtr->pollState = 0;
1.1159 + }
1.1160 + if (tsdPtr->runLoop) {
1.1161 + CFRunLoopSourceSignal(tsdPtr->runLoopSource);
1.1162 + CFRunLoopWakeUp(tsdPtr->runLoop);
1.1163 + }
1.1164 + }
1.1165 + }
1.1166 + UNLOCK_NOTIFIER;
1.1167 +
1.1168 + /*
1.1169 + * Consume the next byte from the notifier pipe if the pipe was
1.1170 + * readable. Note that there may be multiple bytes pending, but to
1.1171 + * avoid a race condition we only read one at a time.
1.1172 + */
1.1173 +
1.1174 + if (FD_ISSET(receivePipe, &readableMask)) {
1.1175 + i = read(receivePipe, buf, 1);
1.1176 +
1.1177 + if ((i == 0) || ((i == 1) && (buf[0] == 'q'))) {
1.1178 + /*
1.1179 + * Someone closed the write end of the pipe or sent us a Quit
1.1180 + * message [Bug: 4139] and then closed the write end of the
1.1181 + * pipe so we need to shut down the notifier thread.
1.1182 + */
1.1183 +
1.1184 + break;
1.1185 + }
1.1186 + }
1.1187 + }
1.1188 + pthread_exit(0);
1.1189 +}
1.1190 +
1.1191 +#ifdef HAVE_PTHREAD_ATFORK
1.1192 +/*
1.1193 + *----------------------------------------------------------------------
1.1194 + *
1.1195 + * AtForkPrepare --
1.1196 + *
1.1197 + * Lock the notifier in preparation for a fork.
1.1198 + *
1.1199 + * Results:
1.1200 + * None.
1.1201 + *
1.1202 + * Side effects:
1.1203 + * None.
1.1204 + *
1.1205 + *----------------------------------------------------------------------
1.1206 + */
1.1207 +
1.1208 +static void
1.1209 +AtForkPrepare(void)
1.1210 +{
1.1211 + LOCK_NOTIFIER_INIT;
1.1212 + LOCK_NOTIFIER;
1.1213 +}
1.1214 +
1.1215 +/*
1.1216 + *----------------------------------------------------------------------
1.1217 + *
1.1218 + * AtForkParent --
1.1219 + *
1.1220 + * Unlock the notifier in the parent after a fork.
1.1221 + *
1.1222 + * Results:
1.1223 + * None.
1.1224 + *
1.1225 + * Side effects:
1.1226 + * None.
1.1227 + *
1.1228 + *----------------------------------------------------------------------
1.1229 + */
1.1230 +
1.1231 +static void
1.1232 +AtForkParent(void)
1.1233 +{
1.1234 + UNLOCK_NOTIFIER;
1.1235 + UNLOCK_NOTIFIER_INIT;
1.1236 +}
1.1237 +
1.1238 +/*
1.1239 + *----------------------------------------------------------------------
1.1240 + *
1.1241 + * AtForkChild --
1.1242 + *
1.1243 + * Unlock and reinstall the notifier in the child after a fork.
1.1244 + *
1.1245 + * Results:
1.1246 + * None.
1.1247 + *
1.1248 + * Side effects:
1.1249 + * None.
1.1250 + *
1.1251 + *----------------------------------------------------------------------
1.1252 + */
1.1253 +
1.1254 +static void
1.1255 +AtForkChild(void)
1.1256 +{
1.1257 + ThreadSpecificData *tsdPtr = TCL_TSD_INIT(&dataKey);
1.1258 +
1.1259 + UNLOCK_NOTIFIER;
1.1260 + UNLOCK_NOTIFIER_INIT;
1.1261 + if (tsdPtr->runLoop) {
1.1262 + tsdPtr->runLoop = NULL;
1.1263 + CFRunLoopSourceInvalidate(tsdPtr->runLoopSource);
1.1264 + CFRelease(tsdPtr->runLoopSource);
1.1265 + tsdPtr->runLoopSource = NULL;
1.1266 + }
1.1267 + if (notifierCount > 0) {
1.1268 + notifierCount = 0;
1.1269 +
1.1270 + /*
1.1271 + * Assume that the return value of Tcl_InitNotifier in the child will
1.1272 + * be identical to the one stored as clientData in tclNotify.c's
1.1273 + * ThreadSpecificData by the parent's TclInitNotifier, so discard the
1.1274 + * return value here. This assumption may require the fork() to be
1.1275 + * executed in the main thread of the parent, otherwise
1.1276 + * Tcl_AlertNotifier may break in the child.
1.1277 + */
1.1278 +
1.1279 + Tcl_InitNotifier();
1.1280 + }
1.1281 +}
1.1282 +#endif /* HAVE_PTHREAD_ATFORK */
1.1283 +
1.1284 +#endif /* HAVE_COREFOUNDATION */