os/persistentdata/persistentstorage/sqlite3api/TEST/TCL/tcldistribution/macosx/tclMacOSXNotify.c
changeset 0 bde4ae8d615e
     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(&notifierInitLock)
   1.240 +#define UNLOCK_NOTIFIER_INIT	SpinLockUnlock(&notifierInitLock)
   1.241 +#define LOCK_NOTIFIER		SpinLockLock(&notifierLock)
   1.242 +#define UNLOCK_NOTIFIER		SpinLockUnlock(&notifierLock)
   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(&notifierThread, &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 */