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-2009, Apple Inc.
10 * Copyright (c) 2005-2009 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.2.5 2009/08/24 00:27:53 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/* #define TCL_MAC_DEBUG_NOTIFIER 1 */
25
26extern TclStubs tclStubs;
27extern Tcl_NotifierProcs tclOriginalNotifier;
28
29/*
30 * We use the Darwin-native spinlock API rather than pthread mutexes for
31 * notifier locking: this radically simplifies the implementation and lowers
32 * overhead. Note that these are not pure spinlocks, they employ various
33 * strategies to back off and relinquish the processor, making them immune to
34 * most priority-inversion livelocks (c.f. 'man 3 OSSpinLockLock' and Darwin
35 * sources: xnu/osfmk/{ppc,i386}/commpage/spinlocks.s).
36 */
37
38#if defined(HAVE_LIBKERN_OSATOMIC_H) && defined(HAVE_OSSPINLOCKLOCK)
39/*
40 * Use OSSpinLock API where available (Tiger or later).
41 */
42
43#include <libkern/OSAtomic.h>
44
45#if defined(HAVE_WEAK_IMPORT) && MAC_OS_X_VERSION_MIN_REQUIRED < 1040
46/*
47 * Support for weakly importing spinlock API.
48 */
49#define WEAK_IMPORT_SPINLOCKLOCK
50#if MAC_OS_X_VERSION_MAX_ALLOWED >= 1050
51#define VOLATILE volatile
52#else
53#define VOLATILE
54#endif
55#ifndef bool
56#define bool int
57#endif
58extern void OSSpinLockLock(VOLATILE OSSpinLock *lock) WEAK_IMPORT_ATTRIBUTE;
59extern void OSSpinLockUnlock(VOLATILE OSSpinLock *lock) WEAK_IMPORT_ATTRIBUTE;
60extern bool OSSpinLockTry(VOLATILE OSSpinLock *lock) WEAK_IMPORT_ATTRIBUTE;
61extern void _spin_lock(VOLATILE OSSpinLock *lock) WEAK_IMPORT_ATTRIBUTE;
62extern void _spin_unlock(VOLATILE OSSpinLock *lock) WEAK_IMPORT_ATTRIBUTE;
63extern bool _spin_lock_try(VOLATILE OSSpinLock *lock) WEAK_IMPORT_ATTRIBUTE;
64static void (* lockLock)(VOLATILE OSSpinLock *lock) = NULL;
65static void (* lockUnlock)(VOLATILE OSSpinLock *lock) = NULL;
66static bool (* lockTry)(VOLATILE OSSpinLock *lock) = NULL;
67#undef VOLATILE
68static pthread_once_t spinLockLockInitControl = PTHREAD_ONCE_INIT;
69static void SpinLockLockInit(void) {
70    lockLock   = OSSpinLockLock   != NULL ? OSSpinLockLock   : _spin_lock;
71    lockUnlock = OSSpinLockUnlock != NULL ? OSSpinLockUnlock : _spin_unlock;
72    lockTry    = OSSpinLockTry    != NULL ? OSSpinLockTry    : _spin_lock_try;
73    if (lockLock == NULL || lockUnlock == NULL) {
74	Tcl_Panic("SpinLockLockInit: no spinlock API available");
75    }
76}
77#define SpinLockLock(p) 	lockLock(p)
78#define SpinLockUnlock(p)	lockUnlock(p)
79#define SpinLockTry(p)  	lockTry(p)
80#else
81#define SpinLockLock(p) 	OSSpinLockLock(p)
82#define SpinLockUnlock(p)	OSSpinLockUnlock(p)
83#define SpinLockTry(p)  	OSSpinLockTry(p)
84#endif /* HAVE_WEAK_IMPORT */
85#define SPINLOCK_INIT   	OS_SPINLOCK_INIT
86
87#else
88/*
89 * Otherwise, use commpage spinlock SPI directly.
90 */
91
92typedef uint32_t OSSpinLock;
93extern void _spin_lock(OSSpinLock *lock);
94extern void _spin_unlock(OSSpinLock *lock);
95extern int  _spin_lock_try(OSSpinLock *lock);
96#define SpinLockLock(p) 	_spin_lock(p)
97#define SpinLockUnlock(p)	_spin_unlock(p)
98#define SpinLockTry(p)  	_spin_lock_try(p)
99#define SPINLOCK_INIT   	0
100
101#endif /* HAVE_LIBKERN_OSATOMIC_H && HAVE_OSSPINLOCKLOCK */
102
103/*
104 * These spinlocks lock access to the global notifier state.
105 */
106
107static OSSpinLock notifierInitLock = SPINLOCK_INIT;
108static OSSpinLock notifierLock     = SPINLOCK_INIT;
109
110/*
111 * Macros abstracting notifier locking/unlocking
112 */
113
114#define LOCK_NOTIFIER_INIT	SpinLockLock(&notifierInitLock)
115#define UNLOCK_NOTIFIER_INIT	SpinLockUnlock(&notifierInitLock)
116#define LOCK_NOTIFIER		SpinLockLock(&notifierLock)
117#define UNLOCK_NOTIFIER		SpinLockUnlock(&notifierLock)
118#define LOCK_NOTIFIER_TSD	SpinLockLock(&tsdPtr->tsdLock)
119#define UNLOCK_NOTIFIER_TSD	SpinLockUnlock(&tsdPtr->tsdLock)
120
121#ifdef TCL_MAC_DEBUG_NOTIFIER
122#define TclMacOSXNotifierDbgMsg(m, ...) do { \
123	    fprintf(notifierLog?notifierLog:stderr, "tclMacOSXNotify.c:%d: " \
124	    "%s() pid %5d thread %10p: " m "\n", __LINE__, __func__, \
125	    getpid(), pthread_self(), ##__VA_ARGS__); \
126	    fflush(notifierLog?notifierLog:stderr); \
127	} while (0)
128
129/*
130 * Debug version of SpinLockLock that logs the time spent waiting for the lock
131 */
132
133#define SpinLockLockDbg(p)	if (!SpinLockTry(p)) { \
134				    Tcl_WideInt s = TclpGetWideClicks(), e; \
135				    SpinLockLock(p); e = TclpGetWideClicks(); \
136				    TclMacOSXNotifierDbgMsg("waited on %s for %8.0f ns", \
137				    #p, TclpWideClicksToNanoseconds(e-s)); \
138				}
139#undef LOCK_NOTIFIER_INIT
140#define LOCK_NOTIFIER_INIT	SpinLockLockDbg(&notifierInitLock)
141#undef LOCK_NOTIFIER
142#define LOCK_NOTIFIER		SpinLockLockDbg(&notifierLock)
143#undef LOCK_NOTIFIER_TSD
144#define LOCK_NOTIFIER_TSD	SpinLockLockDbg(&tsdPtr->tsdLock)
145#include <asl.h>
146static FILE *notifierLog = NULL;
147#ifndef NOTIFIER_LOG
148#define NOTIFIER_LOG "/tmp/tclMacOSXNotify.log"
149#endif
150#define OPEN_NOTIFIER_LOG	if (!notifierLog) { \
151				    notifierLog = fopen(NOTIFIER_LOG, "a"); \
152				    /*TclMacOSXNotifierDbgMsg("open log"); \
153				    asl_set_filter(NULL, \
154				    ASL_FILTER_MASK_UPTO(ASL_LEVEL_DEBUG)); \
155				    asl_add_log_file(NULL, \
156					    fileno(notifierLog));*/ \
157				}
158#define CLOSE_NOTIFIER_LOG	if (notifierLog) { \
159				    /*asl_remove_log_file(NULL, \
160					    fileno(notifierLog)); \
161				    TclMacOSXNotifierDbgMsg("close log");*/ \
162				    fclose(notifierLog); \
163				    notifierLog = NULL; \
164				}
165#define ENABLE_ASL		if (notifierLog) { \
166				    /*tsdPtr->asl = asl_open(NULL, "com.apple.console", ASL_OPT_NO_REMOTE); \
167				    asl_set_filter(tsdPtr->asl, \
168				    ASL_FILTER_MASK_UPTO(ASL_LEVEL_DEBUG)); \
169				    asl_add_log_file(tsdPtr->asl, \
170					    fileno(notifierLog));*/ \
171				}
172#define DISABLE_ASL		/*if (tsdPtr->asl) { \
173				    if (notifierLog) { \
174					asl_remove_log_file(tsdPtr->asl, \
175						fileno(notifierLog)); \
176				    } \
177				    asl_close(tsdPtr->asl); \
178				}*/
179#define ASLCLIENT		/*aslclient asl*/
180#else
181#define TclMacOSXNotifierDbgMsg(m, ...)
182#define OPEN_NOTIFIER_LOG
183#define CLOSE_NOTIFIER_LOG
184#define ENABLE_ASL
185#define DISABLE_ASL
186#endif /* TCL_MAC_DEBUG_NOTIFIER */
187
188/*
189 * This structure is used to keep track of the notifier info for a registered
190 * file.
191 */
192
193typedef struct FileHandler {
194    int fd;
195    int mask;			/* Mask of desired events: TCL_READABLE,
196				 * etc. */
197    int readyMask;		/* Mask of events that have been seen since
198				 * the last time file handlers were invoked
199				 * for this file. */
200    Tcl_FileProc *proc;		/* Function to call, in the style of
201				 * Tcl_CreateFileHandler. */
202    ClientData clientData;	/* Argument to pass to proc. */
203    struct FileHandler *nextPtr;/* Next in list of all files we care about. */
204} FileHandler;
205
206/*
207 * The following structure is what is added to the Tcl event queue when file
208 * handlers are ready to fire.
209 */
210
211typedef struct FileHandlerEvent {
212    Tcl_Event header;		/* Information that is standard for all
213				 * events. */
214    int fd;			/* File descriptor that is ready. Used to find
215				 * the FileHandler structure for the file
216				 * (can't point directly to the FileHandler
217				 * structure because it could go away while
218				 * the event is queued). */
219} FileHandlerEvent;
220
221/*
222 * The following structure contains a set of select() masks to track readable,
223 * writable, and exceptional conditions.
224 */
225
226typedef struct SelectMasks {
227    fd_set readable;
228    fd_set writable;
229    fd_set exceptional;
230} SelectMasks;
231
232/*
233 * The following static structure contains the state information for the
234 * select based implementation of the Tcl notifier. One of these structures is
235 * created for each thread that is using the notifier.
236 */
237
238typedef struct ThreadSpecificData {
239    FileHandler *firstFileHandlerPtr;
240				/* Pointer to head of file handler list. */
241    int polled;			/* True if the notifier thread has polled for
242				 * this thread.
243				 */
244    int sleeping;		/* True if runloop is inside Tcl_Sleep. */
245    int runLoopSourcePerformed;	/* True after the runLoopSource callack was
246				 * performed. */
247    int runLoopRunning;		/* True if this thread's Tcl runLoop is running */
248    int runLoopNestingLevel;	/* Level of nested runLoop invocations */
249    int runLoopServicingEvents;	/* True if this thread's runLoop is servicing
250				 * tcl events */
251    /* Must hold the notifierLock before accessing the following fields: */
252    /* Start notifierLock section */
253    int onList;			/* True if this thread is on the waitingList */
254    struct ThreadSpecificData *nextPtr, *prevPtr;
255				/* All threads that are currently waiting on
256				 * an event have their ThreadSpecificData
257				 * structure on a doubly-linked listed formed
258				 * from these pointers.
259				 */
260    /* End notifierLock section */
261    OSSpinLock tsdLock;		/* Must hold this lock before acessing the
262				 * following fields from more than one thread.
263				 */
264    /* Start tsdLock section */
265    SelectMasks checkMasks;	/* This structure is used to build up the
266				 * masks to be used in the next call to
267				 * select. Bits are set in response to calls
268				 * to Tcl_CreateFileHandler. */
269    SelectMasks readyMasks;	/* This array reflects the readable/writable
270				 * conditions that were found to exist by the
271				 * last call to select. */
272    int numFdBits;		/* Number of valid bits in checkMasks (one
273				 * more than highest fd for which
274				 * Tcl_WatchFile has been called). */
275    int polling;		/* True if this thread is polling for events */
276    CFRunLoopRef runLoop;	/* This thread's CFRunLoop, needs to be woken
277				 * up whenever the runLoopSource is signaled */
278    CFRunLoopSourceRef runLoopSource;
279				/* Any other thread alerts a notifier that an
280				 * event is ready to be processed by signaling
281				 * this CFRunLoopSource. */
282    CFRunLoopObserverRef runLoopObserver, runLoopObserverTcl;
283				/* Adds/removes this thread from waitingList
284				 * when the CFRunLoop starts/stops. */
285    CFRunLoopTimerRef runLoopTimer;
286				/* Wakes up CFRunLoop after given timeout when
287				 * running embedded. */
288    /* End tsdLock section */
289    CFTimeInterval waitTime;	/* runLoopTimer wait time when running
290				 * embedded. */
291#ifdef TCL_MAC_DEBUG_NOTIFIER
292    ASLCLIENT;
293#endif
294} ThreadSpecificData;
295
296static Tcl_ThreadDataKey dataKey;
297
298/*
299 * The following static indicates the number of threads that have initialized
300 * notifiers.
301 *
302 * You must hold the notifierInitLock before accessing this variable.
303 */
304
305static int notifierCount = 0;
306
307/*
308 * The following variable points to the head of a doubly-linked list of
309 * ThreadSpecificData structures for all threads that are currently waiting on
310 * an event.
311 *
312 * You must hold the notifierLock before accessing this list.
313 */
314
315static ThreadSpecificData *waitingListPtr = NULL;
316
317/*
318 * The notifier thread spends all its time in select() waiting for a file
319 * descriptor associated with one of the threads on the waitingListPtr list to
320 * do something interesting. But if the contents of the waitingListPtr list
321 * ever changes, we need to wake up and restart the select() system call. You
322 * can wake up the notifier thread by writing a single byte to the file
323 * descriptor defined below. This file descriptor is the input-end of a pipe
324 * and the notifier thread is listening for data on the output-end of the same
325 * pipe. Hence writing to this file descriptor will cause the select() system
326 * call to return and wake up the notifier thread.
327 *
328 * You must hold the notifierLock lock before writing to the pipe.
329 */
330
331static int triggerPipe = -1;
332static int receivePipe = -1; /* Output end of triggerPipe */
333
334/*
335 * The following static indicates if the notifier thread is running.
336 *
337 * You must hold the notifierInitLock before accessing this variable.
338 */
339
340static int notifierThreadRunning;
341
342/*
343 * This is the thread ID of the notifier thread that does select.
344 * Only valid when notifierThreadRunning is non-zero.
345 *
346 * You must hold the notifierInitLock before accessing this variable.
347 */
348
349static pthread_t notifierThread;
350
351/*
352 * Custom runloop mode for running with only the runloop source for the
353 * notifier thread
354 */
355
356#ifndef TCL_EVENTS_ONLY_RUN_LOOP_MODE
357#define TCL_EVENTS_ONLY_RUN_LOOP_MODE	"com.tcltk.tclEventsOnlyRunLoopMode"
358#endif
359#ifdef __CONSTANT_CFSTRINGS__
360#define tclEventsOnlyRunLoopMode	CFSTR(TCL_EVENTS_ONLY_RUN_LOOP_MODE)
361#else
362static CFStringRef tclEventsOnlyRunLoopMode = NULL;
363#endif
364
365/*
366 * CFTimeInterval to wait forever.
367 */
368
369#define CF_TIMEINTERVAL_FOREVER 5.05e8
370
371/*
372 * Static routines defined in this file.
373 */
374
375static void	StartNotifierThread(void);
376static void	NotifierThreadProc(ClientData clientData)
377			__attribute__ ((__noreturn__));
378static int	FileHandlerEventProc(Tcl_Event *evPtr, int flags);
379static void	TimerWakeUp(CFRunLoopTimerRef timer, void *info);
380static void	QueueFileEvents(void *info);
381static void	UpdateWaitingListAndServiceEvents(CFRunLoopObserverRef observer,
382			CFRunLoopActivity activity, void *info);
383static int	OnOffWaitingList(ThreadSpecificData *tsdPtr, int onList,
384			int signalNotifier);
385
386#ifdef HAVE_PTHREAD_ATFORK
387static int	atForkInit = 0;
388static void	AtForkPrepare(void);
389static void	AtForkParent(void);
390static void	AtForkChild(void);
391#if defined(HAVE_WEAK_IMPORT) && MAC_OS_X_VERSION_MIN_REQUIRED < 1040
392/* Support for weakly importing pthread_atfork. */
393#define WEAK_IMPORT_PTHREAD_ATFORK
394extern int	pthread_atfork(void (*prepare)(void), void (*parent)(void),
395		    void (*child)(void)) WEAK_IMPORT_ATTRIBUTE;
396#endif /* HAVE_WEAK_IMPORT */
397/*
398 * On Darwin 9 and later, it is not possible to call CoreFoundation after
399 * a fork.
400 */
401#if !defined(MAC_OS_X_VERSION_MIN_REQUIRED) || \
402	MAC_OS_X_VERSION_MIN_REQUIRED < 1050
403MODULE_SCOPE long tclMacOSXDarwinRelease;
404#define noCFafterFork (tclMacOSXDarwinRelease >= 9)
405#else /* MAC_OS_X_VERSION_MIN_REQUIRED */
406#define noCFafterFork 1
407#endif /* MAC_OS_X_VERSION_MIN_REQUIRED */
408#endif /* HAVE_PTHREAD_ATFORK */
409
410/*
411 *----------------------------------------------------------------------
412 *
413 * Tcl_InitNotifier --
414 *
415 *	Initializes the platform specific notifier state.
416 *
417 * Results:
418 *	Returns a handle to the notifier state for this thread.
419 *
420 * Side effects:
421 *	None.
422 *
423 *----------------------------------------------------------------------
424 */
425
426ClientData
427Tcl_InitNotifier(void)
428{
429    ThreadSpecificData *tsdPtr;
430
431    tsdPtr = TCL_TSD_INIT(&dataKey);
432
433#ifdef WEAK_IMPORT_SPINLOCKLOCK
434    /*
435     * Initialize support for weakly imported spinlock API.
436     */
437
438    if (pthread_once(&spinLockLockInitControl, SpinLockLockInit)) {
439	Tcl_Panic("Tcl_InitNotifier: pthread_once failed");
440    }
441#endif
442
443#ifndef __CONSTANT_CFSTRINGS__
444    if (!tclEventsOnlyRunLoopMode) {
445	tclEventsOnlyRunLoopMode = CFSTR(TCL_EVENTS_ONLY_RUN_LOOP_MODE);
446    }
447#endif
448
449    /*
450     * Initialize CFRunLoopSource and add it to CFRunLoop of this thread.
451     */
452
453    if (!tsdPtr->runLoop) {
454	CFRunLoopRef runLoop = CFRunLoopGetCurrent();
455	CFRunLoopSourceRef runLoopSource;
456	CFRunLoopSourceContext runLoopSourceContext;
457	CFRunLoopObserverContext runLoopObserverContext;
458	CFRunLoopObserverRef runLoopObserver, runLoopObserverTcl;
459
460	bzero(&runLoopSourceContext, sizeof(CFRunLoopSourceContext));
461	runLoopSourceContext.info = tsdPtr;
462	runLoopSourceContext.perform = QueueFileEvents;
463	runLoopSource = CFRunLoopSourceCreate(NULL, LONG_MIN,
464		&runLoopSourceContext);
465	if (!runLoopSource) {
466	    Tcl_Panic("Tcl_InitNotifier: could not create CFRunLoopSource");
467	}
468	CFRunLoopAddSource(runLoop, runLoopSource, kCFRunLoopCommonModes);
469	CFRunLoopAddSource(runLoop, runLoopSource, tclEventsOnlyRunLoopMode);
470
471	bzero(&runLoopObserverContext, sizeof(CFRunLoopObserverContext));
472	runLoopObserverContext.info = tsdPtr;
473	runLoopObserver = CFRunLoopObserverCreate(NULL,
474		kCFRunLoopEntry|kCFRunLoopExit|kCFRunLoopBeforeWaiting, TRUE,
475		LONG_MIN, UpdateWaitingListAndServiceEvents,
476		&runLoopObserverContext);
477	if (!runLoopObserver) {
478	    Tcl_Panic("Tcl_InitNotifier: could not create "
479		    "CFRunLoopObserver");
480	}
481	CFRunLoopAddObserver(runLoop, runLoopObserver, kCFRunLoopCommonModes);
482
483	/*
484	 * Create a second CFRunLoopObserver with the same callback as above
485	 * for the tclEventsOnlyRunLoopMode to ensure that the callback can be
486	 * re-entered via Tcl_ServiceAll() in the kCFRunLoopBeforeWaiting case
487	 * (CFRunLoop prevents observer callback re-entry of a given observer
488	 * instance).
489	 */
490
491	runLoopObserverTcl = CFRunLoopObserverCreate(NULL,
492		kCFRunLoopEntry|kCFRunLoopExit|kCFRunLoopBeforeWaiting, TRUE,
493		LONG_MIN, UpdateWaitingListAndServiceEvents,
494		&runLoopObserverContext);
495	if (!runLoopObserverTcl) {
496	    Tcl_Panic("Tcl_InitNotifier: could not create "
497		    "CFRunLoopObserver");
498	}
499	CFRunLoopAddObserver(runLoop, runLoopObserverTcl,
500		tclEventsOnlyRunLoopMode);
501
502	tsdPtr->runLoop = runLoop;
503	tsdPtr->runLoopSource = runLoopSource;
504	tsdPtr->runLoopObserver = runLoopObserver;
505	tsdPtr->runLoopObserverTcl = runLoopObserverTcl;
506	tsdPtr->runLoopTimer = NULL;
507	tsdPtr->waitTime = CF_TIMEINTERVAL_FOREVER;
508	tsdPtr->tsdLock = SPINLOCK_INIT;
509    }
510
511    LOCK_NOTIFIER_INIT;
512#ifdef HAVE_PTHREAD_ATFORK
513    /*
514     * Install pthread_atfork handlers to reinitialize the notifier in the
515     * child of a fork.
516     */
517
518    if (
519#ifdef WEAK_IMPORT_PTHREAD_ATFORK
520	    pthread_atfork != NULL &&
521#endif
522	    !atForkInit) {
523	int result = pthread_atfork(AtForkPrepare, AtForkParent, AtForkChild);
524
525	if (result) {
526	    Tcl_Panic("Tcl_InitNotifier: pthread_atfork failed");
527	}
528	atForkInit = 1;
529    }
530#endif
531    if (notifierCount == 0) {
532	int fds[2], status;
533
534	/*
535	 * Initialize trigger pipe.
536	 */
537
538	if (pipe(fds) != 0) {
539	    Tcl_Panic("Tcl_InitNotifier: could not create trigger pipe");
540	}
541
542	status = fcntl(fds[0], F_GETFL);
543	status |= O_NONBLOCK;
544	if (fcntl(fds[0], F_SETFL, status) < 0) {
545	    Tcl_Panic("Tcl_InitNotifier: could not make receive pipe non "
546		    "blocking");
547	}
548	status = fcntl(fds[1], F_GETFL);
549	status |= O_NONBLOCK;
550	if (fcntl(fds[1], F_SETFL, status) < 0) {
551	    Tcl_Panic("Tcl_InitNotifier: could not make trigger pipe non "
552		    "blocking");
553	}
554
555	receivePipe = fds[0];
556	triggerPipe = fds[1];
557
558	/*
559	 * Create notifier thread lazily in Tcl_WaitForEvent() to avoid
560	 * interfering with fork() followed immediately by execve() (we cannot
561	 * execve() when more than one thread is present).
562	 */
563
564	notifierThreadRunning = 0;
565	OPEN_NOTIFIER_LOG;
566    }
567    ENABLE_ASL;
568    notifierCount++;
569    UNLOCK_NOTIFIER_INIT;
570
571    return (ClientData) tsdPtr;
572}
573
574/*
575 *----------------------------------------------------------------------
576 *
577 * TclMacOSXNotifierAddRunLoopMode --
578 *
579 *	Add the tcl notifier RunLoop source, observer and timer (if any)
580 *	to the given RunLoop mode.
581 *
582 * Results:
583 *	None.
584 *
585 * Side effects:
586 *	None.
587 *
588 *----------------------------------------------------------------------
589 */
590
591void
592TclMacOSXNotifierAddRunLoopMode(
593    CONST void *runLoopMode)
594{
595    ThreadSpecificData *tsdPtr = TCL_TSD_INIT(&dataKey);
596    CFStringRef mode = (CFStringRef) runLoopMode;
597
598    if (tsdPtr->runLoop) {
599	CFRunLoopAddSource(tsdPtr->runLoop, tsdPtr->runLoopSource, mode);
600	CFRunLoopAddObserver(tsdPtr->runLoop, tsdPtr->runLoopObserver, mode);
601	if (tsdPtr->runLoopTimer) {
602	    CFRunLoopAddTimer(tsdPtr->runLoop, tsdPtr->runLoopTimer, mode);
603	}
604    }
605}
606
607/*
608 *----------------------------------------------------------------------
609 *
610 * StartNotifierThread --
611 *
612 *	Start notifier thread if necessary.
613 *
614 * Results:
615 *	None.
616 *
617 * Side effects:
618 *	None.
619 *
620 *----------------------------------------------------------------------
621 */
622
623static void
624StartNotifierThread(void)
625{
626    LOCK_NOTIFIER_INIT;
627    if (!notifierCount) {
628	Tcl_Panic("StartNotifierThread: notifier not initialized");
629    }
630    if (!notifierThreadRunning) {
631	int result;
632	pthread_attr_t attr;
633
634	pthread_attr_init(&attr);
635	pthread_attr_setscope(&attr, PTHREAD_SCOPE_SYSTEM);
636	pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_JOINABLE);
637	pthread_attr_setstacksize(&attr, 60 * 1024);
638	result = pthread_create(&notifierThread, &attr,
639		(void * (*)(void *))NotifierThreadProc, NULL);
640	pthread_attr_destroy(&attr);
641	if (result) {
642	    Tcl_Panic("StartNotifierThread: unable to start notifier thread");
643	}
644	notifierThreadRunning = 1;
645    }
646    UNLOCK_NOTIFIER_INIT;
647}
648
649
650/*
651 *----------------------------------------------------------------------
652 *
653 * Tcl_FinalizeNotifier --
654 *
655 *	This function is called to cleanup the notifier state before a thread
656 *	is terminated.
657 *
658 * Results:
659 *	None.
660 *
661 * Side effects:
662 *	May terminate the background notifier thread if this is the last
663 *	notifier instance.
664 *
665 *----------------------------------------------------------------------
666 */
667
668void
669Tcl_FinalizeNotifier(
670    ClientData clientData)		/* Not used. */
671{
672    ThreadSpecificData *tsdPtr;
673
674    tsdPtr = TCL_TSD_INIT(&dataKey);
675
676    LOCK_NOTIFIER_INIT;
677    notifierCount--;
678    DISABLE_ASL;
679
680    /*
681     * If this is the last thread to use the notifier, close the notifier pipe
682     * and wait for the background thread to terminate.
683     */
684
685    if (notifierCount == 0) {
686	if (triggerPipe != -1) {
687	    /*
688	     * Send "q" message to the notifier thread so that it will
689	     * terminate. The notifier will return from its call to select()
690	     * and notice that a "q" message has arrived, it will then close
691	     * its side of the pipe and terminate its thread. Note the we can
692	     * not just close the pipe and check for EOF in the notifier thread
693	     * because if a background child process was created with exec,
694	     * select() would not register the EOF on the pipe until the child
695	     * processes had terminated. [Bug: 4139] [Bug: 1222872]
696	     */
697
698	    write(triggerPipe, "q", 1);
699	    close(triggerPipe);
700
701	    if (notifierThreadRunning) {
702		int result = pthread_join(notifierThread, NULL);
703
704		if (result) {
705		    Tcl_Panic("Tcl_FinalizeNotifier: unable to join notifier "
706			    "thread");
707		}
708		notifierThreadRunning = 0;
709	    }
710
711	    close(receivePipe);
712	    triggerPipe = -1;
713	}
714	CLOSE_NOTIFIER_LOG;
715    }
716    UNLOCK_NOTIFIER_INIT;
717
718    LOCK_NOTIFIER_TSD;		/* For concurrency with Tcl_AlertNotifier */
719    if (tsdPtr->runLoop) {
720	tsdPtr->runLoop = NULL;
721
722	/*
723	 * Remove runLoopSource, runLoopObserver and runLoopTimer from all
724	 * CFRunLoops.
725	 */
726
727	CFRunLoopSourceInvalidate(tsdPtr->runLoopSource);
728	CFRelease(tsdPtr->runLoopSource);
729	tsdPtr->runLoopSource = NULL;
730	CFRunLoopObserverInvalidate(tsdPtr->runLoopObserver);
731	CFRelease(tsdPtr->runLoopObserver);
732	tsdPtr->runLoopObserver = NULL;
733	CFRunLoopObserverInvalidate(tsdPtr->runLoopObserverTcl);
734	CFRelease(tsdPtr->runLoopObserverTcl);
735	tsdPtr->runLoopObserverTcl = NULL;
736	if (tsdPtr->runLoopTimer) {
737	    CFRunLoopTimerInvalidate(tsdPtr->runLoopTimer);
738	    CFRelease(tsdPtr->runLoopTimer);
739	    tsdPtr->runLoopTimer = NULL;
740	}
741    }
742    UNLOCK_NOTIFIER_TSD;
743}
744
745/*
746 *----------------------------------------------------------------------
747 *
748 * Tcl_AlertNotifier --
749 *
750 *	Wake up the specified notifier from any thread. This routine is called
751 *	by the platform independent notifier code whenever the Tcl_ThreadAlert
752 *	routine is called. This routine is guaranteed not to be called on a
753 *	given notifier after Tcl_FinalizeNotifier is called for that notifier.
754 *
755 * Results:
756 *	None.
757 *
758 * Side effects:
759 *	Signals the notifier condition variable for the specified notifier.
760 *
761 *----------------------------------------------------------------------
762 */
763
764void
765Tcl_AlertNotifier(
766    ClientData clientData)
767{
768    ThreadSpecificData *tsdPtr = clientData;
769
770    LOCK_NOTIFIER_TSD;
771    if (tsdPtr->runLoop) {
772	CFRunLoopSourceSignal(tsdPtr->runLoopSource);
773	CFRunLoopWakeUp(tsdPtr->runLoop);
774    }
775    UNLOCK_NOTIFIER_TSD;
776}
777
778/*
779 *----------------------------------------------------------------------
780 *
781 * Tcl_SetTimer --
782 *
783 *	This function sets the current notifier timer value.
784 *
785 * Results:
786 *	None.
787 *
788 * Side effects:
789 *	Replaces any previous timer.
790 *
791 *----------------------------------------------------------------------
792 */
793
794void
795Tcl_SetTimer(
796    Tcl_Time *timePtr)		/* Timeout value, may be NULL. */
797{
798    ThreadSpecificData *tsdPtr;
799    CFRunLoopTimerRef runLoopTimer;
800    CFTimeInterval waitTime;
801
802    if (tclStubs.tcl_SetTimer != tclOriginalNotifier.setTimerProc) {
803	tclStubs.tcl_SetTimer(timePtr);
804	return;
805    }
806
807    tsdPtr = TCL_TSD_INIT(&dataKey);
808    runLoopTimer = tsdPtr->runLoopTimer;
809    if (!runLoopTimer) {
810	return;
811    }
812    if (timePtr) {
813	Tcl_Time vTime  = *timePtr;
814
815	if (vTime.sec != 0 || vTime.usec != 0) {
816	    tclScaleTimeProcPtr(&vTime, tclTimeClientData);
817	    waitTime = vTime.sec + 1.0e-6 * vTime.usec;
818	} else {
819	    waitTime = 0;
820	}
821    } else {
822	waitTime = CF_TIMEINTERVAL_FOREVER;
823    }
824    tsdPtr->waitTime = waitTime;
825    CFRunLoopTimerSetNextFireDate(runLoopTimer,
826	    CFAbsoluteTimeGetCurrent() + waitTime);
827}
828
829/*
830 *----------------------------------------------------------------------
831 *
832 * TimerWakeUp --
833 *
834 *	CFRunLoopTimer callback.
835 *
836 * Results:
837 *	None.
838 *
839 * Side effects:
840 *	None.
841 *
842 *----------------------------------------------------------------------
843 */
844
845static void
846TimerWakeUp(
847    CFRunLoopTimerRef timer,
848    void *info)
849{
850}
851
852/*
853 *----------------------------------------------------------------------
854 *
855 * Tcl_ServiceModeHook --
856 *
857 *	This function is invoked whenever the service mode changes.
858 *
859 * Results:
860 *	None.
861 *
862 * Side effects:
863 *	None.
864 *
865 *----------------------------------------------------------------------
866 */
867
868void
869Tcl_ServiceModeHook(
870    int mode)			/* Either TCL_SERVICE_ALL, or
871				 * TCL_SERVICE_NONE. */
872{
873    ThreadSpecificData *tsdPtr;
874
875    tsdPtr = TCL_TSD_INIT(&dataKey);
876
877    if (mode == TCL_SERVICE_ALL && !tsdPtr->runLoopTimer) {
878	if (!tsdPtr->runLoop) {
879	    Tcl_Panic("Tcl_ServiceModeHook: Notifier not initialized");
880	}
881	tsdPtr->runLoopTimer = CFRunLoopTimerCreate(NULL,
882		CFAbsoluteTimeGetCurrent() + CF_TIMEINTERVAL_FOREVER,
883		CF_TIMEINTERVAL_FOREVER, 0, 0, TimerWakeUp, NULL);
884	if (tsdPtr->runLoopTimer) {
885	    CFRunLoopAddTimer(tsdPtr->runLoop, tsdPtr->runLoopTimer,
886		    kCFRunLoopCommonModes);
887	    StartNotifierThread();
888	}
889    }
890}
891
892/*
893 *----------------------------------------------------------------------
894 *
895 * Tcl_CreateFileHandler --
896 *
897 *	This function registers a file handler with the select notifier.
898 *
899 * Results:
900 *	None.
901 *
902 * Side effects:
903 *	Creates a new file handler structure.
904 *
905 *----------------------------------------------------------------------
906 */
907
908void
909Tcl_CreateFileHandler(
910    int fd,			/* Handle of stream to watch. */
911    int mask,			/* OR'ed combination of TCL_READABLE,
912				 * TCL_WRITABLE, and TCL_EXCEPTION: indicates
913				 * conditions under which proc should be
914				 * called. */
915    Tcl_FileProc *proc,		/* Function to call for each selected
916				 * event. */
917    ClientData clientData)	/* Arbitrary data to pass to proc. */
918{
919    ThreadSpecificData *tsdPtr;
920    FileHandler *filePtr;
921
922    if (tclStubs.tcl_CreateFileHandler !=
923	    tclOriginalNotifier.createFileHandlerProc) {
924	tclStubs.tcl_CreateFileHandler(fd, mask, proc, clientData);
925	return;
926    }
927
928    tsdPtr = TCL_TSD_INIT(&dataKey);
929
930    for (filePtr = tsdPtr->firstFileHandlerPtr; filePtr != NULL;
931	    filePtr = filePtr->nextPtr) {
932	if (filePtr->fd == fd) {
933	    break;
934	}
935    }
936    if (filePtr == NULL) {
937	filePtr = (FileHandler *) ckalloc(sizeof(FileHandler));
938	filePtr->fd = fd;
939	filePtr->readyMask = 0;
940	filePtr->nextPtr = tsdPtr->firstFileHandlerPtr;
941	tsdPtr->firstFileHandlerPtr = filePtr;
942    }
943    filePtr->proc = proc;
944    filePtr->clientData = clientData;
945    filePtr->mask = mask;
946
947    /*
948     * Update the check masks for this file.
949     */
950
951    LOCK_NOTIFIER_TSD;
952    if (mask & TCL_READABLE) {
953	FD_SET(fd, &(tsdPtr->checkMasks.readable));
954    } else {
955	FD_CLR(fd, &(tsdPtr->checkMasks.readable));
956    }
957    if (mask & TCL_WRITABLE) {
958	FD_SET(fd, &(tsdPtr->checkMasks.writable));
959    } else {
960	FD_CLR(fd, &(tsdPtr->checkMasks.writable));
961    }
962    if (mask & TCL_EXCEPTION) {
963	FD_SET(fd, &(tsdPtr->checkMasks.exceptional));
964    } else {
965	FD_CLR(fd, &(tsdPtr->checkMasks.exceptional));
966    }
967    if (tsdPtr->numFdBits <= fd) {
968	tsdPtr->numFdBits = fd+1;
969    }
970    UNLOCK_NOTIFIER_TSD;
971}
972
973/*
974 *----------------------------------------------------------------------
975 *
976 * Tcl_DeleteFileHandler --
977 *
978 *	Cancel a previously-arranged callback arrangement for a file.
979 *
980 * Results:
981 *	None.
982 *
983 * Side effects:
984 *	If a callback was previously registered on file, remove it.
985 *
986 *----------------------------------------------------------------------
987 */
988
989void
990Tcl_DeleteFileHandler(
991    int fd)			/* Stream id for which to remove callback
992				 * function. */
993{
994    FileHandler *filePtr, *prevPtr;
995    int i, numFdBits;
996    ThreadSpecificData *tsdPtr;
997
998    if (tclStubs.tcl_DeleteFileHandler !=
999	    tclOriginalNotifier.deleteFileHandlerProc) {
1000	tclStubs.tcl_DeleteFileHandler(fd);
1001	return;
1002    }
1003
1004    tsdPtr = TCL_TSD_INIT(&dataKey);
1005    numFdBits = -1;
1006
1007    /*
1008     * Find the entry for the given file (and return if there isn't one).
1009     */
1010
1011    for (prevPtr = NULL, filePtr = tsdPtr->firstFileHandlerPtr; ;
1012	    prevPtr = filePtr, filePtr = filePtr->nextPtr) {
1013	if (filePtr == NULL) {
1014	    return;
1015	}
1016	if (filePtr->fd == fd) {
1017	    break;
1018	}
1019    }
1020
1021    /*
1022     * Find current max fd.
1023     */
1024
1025    if (fd+1 == tsdPtr->numFdBits) {
1026	numFdBits = 0;
1027	for (i = fd-1; i >= 0; i--) {
1028	    if (FD_ISSET(i, &(tsdPtr->checkMasks.readable))
1029		    || FD_ISSET(i, &(tsdPtr->checkMasks.writable))
1030		    || FD_ISSET(i, &(tsdPtr->checkMasks.exceptional))) {
1031		numFdBits = i+1;
1032		break;
1033	    }
1034	}
1035    }
1036
1037    LOCK_NOTIFIER_TSD;
1038    if (numFdBits != -1) {
1039	tsdPtr->numFdBits = numFdBits;
1040    }
1041
1042    /*
1043     * Update the check masks for this file.
1044     */
1045
1046    if (filePtr->mask & TCL_READABLE) {
1047	FD_CLR(fd, &(tsdPtr->checkMasks.readable));
1048    }
1049    if (filePtr->mask & TCL_WRITABLE) {
1050	FD_CLR(fd, &(tsdPtr->checkMasks.writable));
1051    }
1052    if (filePtr->mask & TCL_EXCEPTION) {
1053	FD_CLR(fd, &(tsdPtr->checkMasks.exceptional));
1054    }
1055    UNLOCK_NOTIFIER_TSD;
1056
1057    /*
1058     * Clean up information in the callback record.
1059     */
1060
1061    if (prevPtr == NULL) {
1062	tsdPtr->firstFileHandlerPtr = filePtr->nextPtr;
1063    } else {
1064	prevPtr->nextPtr = filePtr->nextPtr;
1065    }
1066    ckfree((char *) filePtr);
1067}
1068
1069/*
1070 *----------------------------------------------------------------------
1071 *
1072 * FileHandlerEventProc --
1073 *
1074 *	This function is called by Tcl_ServiceEvent when a file event reaches
1075 *	the front of the event queue. This function is responsible for
1076 *	actually handling the event by invoking the callback for the file
1077 *	handler.
1078 *
1079 * Results:
1080 *	Returns 1 if the event was handled, meaning it should be removed from
1081 *	the queue. Returns 0 if the event was not handled, meaning it should
1082 *	stay on the queue. The only time the event isn't handled is if the
1083 *	TCL_FILE_EVENTS flag bit isn't set.
1084 *
1085 * Side effects:
1086 *	Whatever the file handler's callback function does.
1087 *
1088 *----------------------------------------------------------------------
1089 */
1090
1091static int
1092FileHandlerEventProc(
1093    Tcl_Event *evPtr,		/* Event to service. */
1094    int flags)			/* Flags that indicate what events to handle,
1095				 * such as TCL_FILE_EVENTS. */
1096{
1097    int mask;
1098    FileHandler *filePtr;
1099    FileHandlerEvent *fileEvPtr = (FileHandlerEvent *) evPtr;
1100    ThreadSpecificData *tsdPtr;
1101
1102    if (!(flags & TCL_FILE_EVENTS)) {
1103	return 0;
1104    }
1105
1106    /*
1107     * Search through the file handlers to find the one whose handle matches
1108     * the event. We do this rather than keeping a pointer to the file handler
1109     * directly in the event, so that the handler can be deleted while the
1110     * event is queued without leaving a dangling pointer.
1111     */
1112
1113    tsdPtr = TCL_TSD_INIT(&dataKey);
1114    for (filePtr = tsdPtr->firstFileHandlerPtr; filePtr != NULL;
1115	    filePtr = filePtr->nextPtr) {
1116	if (filePtr->fd != fileEvPtr->fd) {
1117	    continue;
1118	}
1119
1120	/*
1121	 * The code is tricky for two reasons:
1122	 * 1. The file handler's desired events could have changed since the
1123	 *    time when the event was queued, so AND the ready mask with the
1124	 *    desired mask.
1125	 * 2. The file could have been closed and re-opened since the time
1126	 *    when the event was queued. This is why the ready mask is stored
1127	 *    in the file handler rather than the queued event: it will be
1128	 *    zeroed when a new file handler is created for the newly opened
1129	 *    file.
1130	 */
1131
1132	mask = filePtr->readyMask & filePtr->mask;
1133	filePtr->readyMask = 0;
1134	if (mask != 0) {
1135	    LOCK_NOTIFIER_TSD;
1136	    if (mask & TCL_READABLE) {
1137		FD_CLR(filePtr->fd, &(tsdPtr->readyMasks.readable));
1138	    }
1139	    if (mask & TCL_WRITABLE) {
1140		FD_CLR(filePtr->fd, &(tsdPtr->readyMasks.writable));
1141	    }
1142	    if (mask & TCL_EXCEPTION) {
1143		FD_CLR(filePtr->fd, &(tsdPtr->readyMasks.exceptional));
1144	    }
1145	    UNLOCK_NOTIFIER_TSD;
1146	    filePtr->proc(filePtr->clientData, mask);
1147	}
1148	break;
1149    }
1150    return 1;
1151}
1152
1153/*
1154 *----------------------------------------------------------------------
1155 *
1156 * Tcl_WaitForEvent --
1157 *
1158 *	This function is called by Tcl_DoOneEvent to wait for new events on
1159 *	the message queue. If the block time is 0, then Tcl_WaitForEvent just
1160 *	polls without blocking.
1161 *
1162 * Results:
1163 *	Returns 0 if a tcl event or timeout ocurred and 1 if a non-tcl
1164 *	CFRunLoop source was processed.
1165 *
1166 * Side effects:
1167 *	None.
1168 *
1169 *----------------------------------------------------------------------
1170 */
1171
1172int
1173Tcl_WaitForEvent(
1174    Tcl_Time *timePtr)		/* Maximum block time, or NULL. */
1175{
1176    int result, polling, runLoopRunning;
1177    CFTimeInterval waitTime;
1178    SInt32 runLoopStatus;
1179    ThreadSpecificData *tsdPtr;
1180
1181    if (tclStubs.tcl_WaitForEvent != tclOriginalNotifier.waitForEventProc) {
1182	return tclStubs.tcl_WaitForEvent(timePtr);
1183    }
1184    result = -1;
1185    polling = 0;
1186    waitTime = CF_TIMEINTERVAL_FOREVER;
1187    tsdPtr = TCL_TSD_INIT(&dataKey);
1188
1189    if (!tsdPtr->runLoop) {
1190	Tcl_Panic("Tcl_WaitForEvent: Notifier not initialized");
1191    }
1192
1193    if (timePtr) {
1194	Tcl_Time vTime  = *timePtr;
1195
1196	/*
1197	 * TIP #233 (Virtualized Time). Is virtual time in effect? And do we
1198	 * actually have something to scale? If yes to both then we call the
1199	 * handler to do this scaling.
1200	 */
1201
1202	if (vTime.sec != 0 || vTime.usec != 0) {
1203	    tclScaleTimeProcPtr(&vTime, tclTimeClientData);
1204	    waitTime = vTime.sec + 1.0e-6 * vTime.usec;
1205	} else {
1206	    /*
1207	     * Polling: pretend to wait for files and tell the notifier thread
1208	     * what we are doing. The notifier thread makes sure it goes
1209	     * through select with its select mask in the same state as ours
1210	     * currently is. We block until that happens.
1211	     */
1212
1213	    polling = 1;
1214	}
1215    }
1216
1217    StartNotifierThread();
1218
1219    LOCK_NOTIFIER_TSD;
1220    tsdPtr->polling = polling;
1221    UNLOCK_NOTIFIER_TSD;
1222    tsdPtr->runLoopSourcePerformed = 0;
1223
1224    /*
1225     * If the Tcl runloop is already running (e.g. if Tcl_WaitForEvent was
1226     * called recursively) or is servicing events via the runloop observer,
1227     * re-run it in a custom runloop mode containing only the source for the
1228     * notifier thread, otherwise wakeups from other sources added to the
1229     * common runloop modes might get lost or 3rd party event handlers might
1230     * get called when they do not expect to be.
1231     */
1232
1233    runLoopRunning = tsdPtr->runLoopRunning;
1234    tsdPtr->runLoopRunning = 1;
1235    runLoopStatus = CFRunLoopRunInMode(tsdPtr->runLoopServicingEvents ||
1236	    runLoopRunning ? tclEventsOnlyRunLoopMode : kCFRunLoopDefaultMode,
1237	    waitTime, TRUE);
1238    tsdPtr->runLoopRunning = runLoopRunning;
1239
1240    LOCK_NOTIFIER_TSD;
1241    tsdPtr->polling = 0;
1242    UNLOCK_NOTIFIER_TSD;
1243    switch (runLoopStatus) {
1244    case kCFRunLoopRunFinished:
1245       Tcl_Panic("Tcl_WaitForEvent: CFRunLoop finished");
1246	break;
1247    case kCFRunLoopRunTimedOut:
1248	QueueFileEvents(tsdPtr);
1249	result = 0;
1250	break;
1251    case kCFRunLoopRunStopped:
1252    case kCFRunLoopRunHandledSource:
1253	result = tsdPtr->runLoopSourcePerformed ? 0 : 1;
1254	break;
1255    }
1256
1257    return result;
1258}
1259
1260/*
1261 *----------------------------------------------------------------------
1262 *
1263 * QueueFileEvents --
1264 *
1265 *	CFRunLoopSource callback for queueing file events.
1266 *
1267 * Results:
1268 *	None.
1269 *
1270 * Side effects:
1271 *	Queues file events that are detected by the select.
1272 *
1273 *----------------------------------------------------------------------
1274 */
1275
1276static void
1277QueueFileEvents(
1278    void *info)
1279{
1280    SelectMasks readyMasks;
1281    FileHandler *filePtr;
1282    ThreadSpecificData *tsdPtr = (ThreadSpecificData *) info;
1283
1284    /*
1285     * Queue all detected file events.
1286     */
1287
1288    LOCK_NOTIFIER_TSD;
1289    FD_COPY(&(tsdPtr->readyMasks.readable), &readyMasks.readable);
1290    FD_COPY(&(tsdPtr->readyMasks.writable), &readyMasks.writable);
1291    FD_COPY(&(tsdPtr->readyMasks.exceptional), &readyMasks.exceptional);
1292    FD_ZERO(&(tsdPtr->readyMasks.readable));
1293    FD_ZERO(&(tsdPtr->readyMasks.writable));
1294    FD_ZERO(&(tsdPtr->readyMasks.exceptional));
1295    UNLOCK_NOTIFIER_TSD;
1296    tsdPtr->runLoopSourcePerformed = 1;
1297
1298    for (filePtr = tsdPtr->firstFileHandlerPtr; (filePtr != NULL);
1299	    filePtr = filePtr->nextPtr) {
1300	int mask = 0;
1301
1302	if (FD_ISSET(filePtr->fd, &readyMasks.readable)) {
1303	    mask |= TCL_READABLE;
1304	}
1305	if (FD_ISSET(filePtr->fd, &readyMasks.writable)) {
1306	    mask |= TCL_WRITABLE;
1307	}
1308	if (FD_ISSET(filePtr->fd, &readyMasks.exceptional)) {
1309	    mask |= TCL_EXCEPTION;
1310	}
1311	if (!mask) {
1312	    continue;
1313	}
1314
1315	/*
1316	 * Don't bother to queue an event if the mask was previously non-zero
1317	 * since an event must still be on the queue.
1318	 */
1319
1320	if (filePtr->readyMask == 0) {
1321	    FileHandlerEvent *fileEvPtr = (FileHandlerEvent *)
1322		    ckalloc(sizeof(FileHandlerEvent));
1323	    fileEvPtr->header.proc = FileHandlerEventProc;
1324	    fileEvPtr->fd = filePtr->fd;
1325	    Tcl_QueueEvent((Tcl_Event *) fileEvPtr, TCL_QUEUE_TAIL);
1326	}
1327	filePtr->readyMask = mask;
1328    }
1329}
1330
1331/*
1332 *----------------------------------------------------------------------
1333 *
1334 * UpdateWaitingListAndServiceEvents --
1335 *
1336 *	CFRunLoopObserver callback for updating waitingList and
1337 *	servicing Tcl events.
1338 *
1339 * Results:
1340 *	None.
1341 *
1342 * Side effects:
1343 *	None.
1344 *
1345 *----------------------------------------------------------------------
1346 */
1347
1348static void
1349UpdateWaitingListAndServiceEvents(
1350    CFRunLoopObserverRef observer,
1351    CFRunLoopActivity activity,
1352    void *info)
1353{
1354    ThreadSpecificData *tsdPtr = (ThreadSpecificData*) info;
1355
1356    if (tsdPtr->sleeping) {
1357	return;
1358    }
1359    switch (activity) {
1360    case kCFRunLoopEntry:
1361	tsdPtr->runLoopNestingLevel++;
1362	if (tsdPtr->numFdBits > 0 || tsdPtr->polling) {
1363	    LOCK_NOTIFIER;
1364	    if (!OnOffWaitingList(tsdPtr, 1, 1) && tsdPtr->polling) {
1365	       write(triggerPipe, "", 1);
1366	    }
1367	    UNLOCK_NOTIFIER;
1368	}
1369	break;
1370    case kCFRunLoopExit:
1371	if (tsdPtr->runLoopNestingLevel == 1) {
1372	    LOCK_NOTIFIER;
1373	    OnOffWaitingList(tsdPtr, 0, 1);
1374	    UNLOCK_NOTIFIER;
1375	}
1376	tsdPtr->runLoopNestingLevel--;
1377	break;
1378    case kCFRunLoopBeforeWaiting:
1379	if (tsdPtr->runLoopTimer && !tsdPtr->runLoopServicingEvents &&
1380		(tsdPtr->runLoopNestingLevel > 1 || !tsdPtr->runLoopRunning)) {
1381	    tsdPtr->runLoopServicingEvents = 1;
1382	    while (Tcl_ServiceAll() && tsdPtr->waitTime == 0) {}
1383	    tsdPtr->runLoopServicingEvents = 0;
1384	}
1385	break;
1386    default:
1387	break;
1388    }
1389}
1390
1391/*
1392 *----------------------------------------------------------------------
1393 *
1394 * OnOffWaitingList --
1395 *
1396 *	Add/remove the specified thread to/from the global waitingList
1397 *	and optionally signal the notifier.
1398 *
1399 *	!!! Requires notifierLock to be held !!!
1400 *
1401 * Results:
1402 *	Boolean indicating whether the waitingList was changed.
1403 *
1404 * Side effects:
1405 *	None.
1406 *
1407 *----------------------------------------------------------------------
1408 */
1409
1410static int
1411OnOffWaitingList(
1412    ThreadSpecificData *tsdPtr,
1413    int onList,
1414    int signalNotifier)
1415{
1416    int changeWaitingList;
1417#ifdef TCL_MAC_DEBUG_NOTIFIER
1418    if(SpinLockTry(&notifierLock)) {
1419	Tcl_Panic("OnOffWaitingList: notifierLock unlocked");
1420    }
1421#endif
1422    changeWaitingList = (!onList ^ !tsdPtr->onList);
1423    if (changeWaitingList) {
1424	if (onList) {
1425	    tsdPtr->nextPtr = waitingListPtr;
1426	    if (waitingListPtr) {
1427		waitingListPtr->prevPtr = tsdPtr;
1428	    }
1429	    tsdPtr->prevPtr = NULL;
1430	    waitingListPtr = tsdPtr;
1431	    tsdPtr->onList = 1;
1432	} else {
1433	    if (tsdPtr->prevPtr) {
1434		tsdPtr->prevPtr->nextPtr = tsdPtr->nextPtr;
1435	    } else {
1436		waitingListPtr = tsdPtr->nextPtr;
1437	    }
1438	    if (tsdPtr->nextPtr) {
1439		tsdPtr->nextPtr->prevPtr = tsdPtr->prevPtr;
1440	    }
1441	    tsdPtr->nextPtr = tsdPtr->prevPtr = NULL;
1442	    tsdPtr->onList = 0;
1443	}
1444	if (signalNotifier) {
1445	   write(triggerPipe, "", 1);
1446	}
1447    }
1448
1449    return changeWaitingList;
1450}
1451
1452/*
1453 *----------------------------------------------------------------------
1454 *
1455 * Tcl_Sleep --
1456 *
1457 *	Delay execution for the specified number of milliseconds.
1458 *
1459 * Results:
1460 *	None.
1461 *
1462 * Side effects:
1463 *	Time passes.
1464 *
1465 *----------------------------------------------------------------------
1466 */
1467
1468void
1469Tcl_Sleep(
1470    int ms)			/* Number of milliseconds to sleep. */
1471{
1472    Tcl_Time vdelay;
1473    ThreadSpecificData *tsdPtr = TCL_TSD_INIT(&dataKey);
1474
1475    if (ms <= 0) {
1476	return;
1477    }
1478
1479    /*
1480     * TIP #233: Scale from virtual time to real-time.
1481     */
1482
1483    vdelay.sec  = ms / 1000;
1484    vdelay.usec = (ms % 1000) * 1000;
1485    tclScaleTimeProcPtr(&vdelay, tclTimeClientData);
1486
1487
1488    if (tsdPtr->runLoop) {
1489	CFTimeInterval waitTime;
1490	CFRunLoopTimerRef runLoopTimer = tsdPtr->runLoopTimer;
1491	CFAbsoluteTime nextTimerFire = 0, waitEnd, now;
1492	SInt32 runLoopStatus;
1493
1494	waitTime = vdelay.sec + 1.0e-6 * vdelay.usec;
1495 	now = CFAbsoluteTimeGetCurrent();
1496	waitEnd = now + waitTime;
1497
1498	if (runLoopTimer) {
1499	    nextTimerFire = CFRunLoopTimerGetNextFireDate(runLoopTimer);
1500	    if (nextTimerFire < waitEnd) {
1501		CFRunLoopTimerSetNextFireDate(runLoopTimer, now +
1502			CF_TIMEINTERVAL_FOREVER);
1503	    } else {
1504		runLoopTimer = NULL;
1505	    }
1506	}
1507	tsdPtr->sleeping = 1;
1508	do {
1509	    runLoopStatus = CFRunLoopRunInMode(kCFRunLoopDefaultMode, waitTime,
1510		    FALSE);
1511	    switch (runLoopStatus) {
1512	    case kCFRunLoopRunFinished:
1513		Tcl_Panic("Tcl_Sleep: CFRunLoop finished");
1514		break;
1515	    case kCFRunLoopRunStopped:
1516		TclMacOSXNotifierDbgMsg("CFRunLoop stopped");
1517		waitTime = waitEnd - CFAbsoluteTimeGetCurrent();
1518		break;
1519	    case kCFRunLoopRunTimedOut:
1520		waitTime = 0;
1521		break;
1522	    }
1523	} while (waitTime > 0);
1524	tsdPtr->sleeping = 0;
1525 	if (runLoopTimer) {
1526	    CFRunLoopTimerSetNextFireDate(runLoopTimer, nextTimerFire);
1527	}
1528    } else {
1529	struct timespec waitTime;
1530
1531	waitTime.tv_sec = vdelay.sec;
1532	waitTime.tv_nsec = vdelay.usec * 1000;
1533	while (nanosleep(&waitTime, &waitTime));
1534    }
1535}
1536
1537/*
1538 *----------------------------------------------------------------------
1539 *
1540 * TclUnixWaitForFile --
1541 *
1542 *	This function waits synchronously for a file to become readable or
1543 *	writable, with an optional timeout.
1544 *
1545 * Results:
1546 *	The return value is an OR'ed combination of TCL_READABLE,
1547 *	TCL_WRITABLE, and TCL_EXCEPTION, indicating the conditions that are
1548 *	present on file at the time of the return. This function will not
1549 *	return until either "timeout" milliseconds have elapsed or at least
1550 *	one of the conditions given by mask has occurred for file (a return
1551 *	value of 0 means that a timeout occurred). No normal events will be
1552 *	serviced during the execution of this function.
1553 *
1554 * Side effects:
1555 *	Time passes.
1556 *
1557 *----------------------------------------------------------------------
1558 */
1559
1560int
1561TclUnixWaitForFile(
1562    int fd,			/* Handle for file on which to wait. */
1563    int mask,			/* What to wait for: OR'ed combination of
1564				 * TCL_READABLE, TCL_WRITABLE, and
1565				 * TCL_EXCEPTION. */
1566    int timeout)		/* Maximum amount of time to wait for one of
1567				 * the conditions in mask to occur, in
1568				 * milliseconds. A value of 0 means don't wait
1569				 * at all, and a value of -1 means wait
1570				 * forever. */
1571{
1572    Tcl_Time abortTime = {0, 0}, now; /* silence gcc 4 warning */
1573    struct timeval blockTime, *timeoutPtr;
1574    int numFound, result = 0;
1575    fd_set readableMask;
1576    fd_set writableMask;
1577    fd_set exceptionalMask;
1578
1579#define SET_BITS(var, bits)	((var) |= (bits))
1580#define CLEAR_BITS(var, bits)	((var) &= ~(bits))
1581
1582#ifndef _DARWIN_C_SOURCE
1583    /*
1584     * Sanity check fd.
1585     */
1586
1587    if (fd >= FD_SETSIZE) {
1588	Tcl_Panic("TclUnixWaitForFile can't handle file id %d", fd);
1589	/* must never get here, or select masks overrun will occur below */
1590    }
1591#endif
1592
1593    /*
1594     * If there is a non-zero finite timeout, compute the time when we give
1595     * up.
1596     */
1597
1598    if (timeout > 0) {
1599	Tcl_GetTime(&now);
1600	abortTime.sec = now.sec + timeout/1000;
1601	abortTime.usec = now.usec + (timeout%1000)*1000;
1602	if (abortTime.usec >= 1000000) {
1603	    abortTime.usec -= 1000000;
1604	    abortTime.sec += 1;
1605	}
1606	timeoutPtr = &blockTime;
1607    } else if (timeout == 0) {
1608	timeoutPtr = &blockTime;
1609	blockTime.tv_sec = 0;
1610	blockTime.tv_usec = 0;
1611    } else {
1612	timeoutPtr = NULL;
1613    }
1614
1615    /*
1616     * Initialize the select masks.
1617     */
1618
1619    FD_ZERO(&readableMask);
1620    FD_ZERO(&writableMask);
1621    FD_ZERO(&exceptionalMask);
1622
1623    /*
1624     * Loop in a mini-event loop of our own, waiting for either the file to
1625     * become ready or a timeout to occur.
1626     */
1627
1628    while (1) {
1629	if (timeout > 0) {
1630	    blockTime.tv_sec = abortTime.sec - now.sec;
1631	    blockTime.tv_usec = abortTime.usec - now.usec;
1632	    if (blockTime.tv_usec < 0) {
1633		blockTime.tv_sec -= 1;
1634		blockTime.tv_usec += 1000000;
1635	    }
1636	    if (blockTime.tv_sec < 0) {
1637		blockTime.tv_sec = 0;
1638		blockTime.tv_usec = 0;
1639	    }
1640	}
1641
1642	/*
1643	 * Setup the select masks for the fd.
1644	 */
1645
1646	if (mask & TCL_READABLE)  {
1647	    FD_SET(fd, &readableMask);
1648	}
1649	if (mask & TCL_WRITABLE)  {
1650	    FD_SET(fd, &writableMask);
1651	}
1652	if (mask & TCL_EXCEPTION) {
1653	    FD_SET(fd, &exceptionalMask);
1654	}
1655
1656	/*
1657	 * Wait for the event or a timeout.
1658	 */
1659
1660	numFound = select(fd + 1, &readableMask, &writableMask,
1661		&exceptionalMask, timeoutPtr);
1662	if (numFound == 1) {
1663	    if (FD_ISSET(fd, &readableMask))   {
1664		SET_BITS(result, TCL_READABLE);
1665	    }
1666	    if (FD_ISSET(fd, &writableMask))  {
1667		SET_BITS(result, TCL_WRITABLE);
1668	    }
1669	    if (FD_ISSET(fd, &exceptionalMask)) {
1670		SET_BITS(result, TCL_EXCEPTION);
1671	    }
1672	    result &= mask;
1673	    if (result) {
1674		break;
1675	    }
1676	}
1677	if (timeout == 0) {
1678	    break;
1679	}
1680	if (timeout < 0) {
1681	    continue;
1682	}
1683
1684	/*
1685	 * The select returned early, so we need to recompute the timeout.
1686	 */
1687
1688	Tcl_GetTime(&now);
1689	if ((abortTime.sec < now.sec)
1690		|| (abortTime.sec==now.sec && abortTime.usec<=now.usec)) {
1691	    break;
1692	}
1693    }
1694    return result;
1695}
1696
1697/*
1698 *----------------------------------------------------------------------
1699 *
1700 * NotifierThreadProc --
1701 *
1702 *	This routine is the initial (and only) function executed by the
1703 *	special notifier thread. Its job is to wait for file descriptors to
1704 *	become readable or writable or to have an exception condition and then
1705 *	to notify other threads who are interested in this information by
1706 *	signalling a condition variable. Other threads can signal this
1707 *	notifier thread of a change in their interests by writing a single
1708 *	byte to a special pipe that the notifier thread is monitoring.
1709 *
1710 * Result:
1711 *	None. Once started, this routine never exits. It dies with the overall
1712 *	process.
1713 *
1714 * Side effects:
1715 *	The trigger pipe used to signal the notifier thread is created when
1716 *	the notifier thread first starts.
1717 *
1718 *----------------------------------------------------------------------
1719 */
1720
1721static void
1722NotifierThreadProc(
1723    ClientData clientData)	/* Not used. */
1724{
1725    ThreadSpecificData *tsdPtr;
1726    fd_set readableMask, writableMask, exceptionalMask;
1727    int i, numFdBits = 0, polling;
1728    struct timeval poll = {0., 0.}, *timePtr;
1729    char buf[2];
1730
1731    /*
1732     * Look for file events and report them to interested threads.
1733     */
1734
1735    while (1) {
1736	FD_ZERO(&readableMask);
1737	FD_ZERO(&writableMask);
1738	FD_ZERO(&exceptionalMask);
1739
1740	/*
1741	 * Compute the logical OR of the select masks from all the waiting
1742	 * notifiers.
1743	 */
1744
1745	timePtr = NULL;
1746	LOCK_NOTIFIER;
1747	for (tsdPtr = waitingListPtr; tsdPtr; tsdPtr = tsdPtr->nextPtr) {
1748	    LOCK_NOTIFIER_TSD;
1749	    for (i = tsdPtr->numFdBits-1; i >= 0; --i) {
1750		if (FD_ISSET(i, &(tsdPtr->checkMasks.readable))) {
1751		    FD_SET(i, &readableMask);
1752		}
1753		if (FD_ISSET(i, &(tsdPtr->checkMasks.writable))) {
1754		    FD_SET(i, &writableMask);
1755		}
1756		if (FD_ISSET(i, &(tsdPtr->checkMasks.exceptional))) {
1757		    FD_SET(i, &exceptionalMask);
1758		}
1759	    }
1760	    if (tsdPtr->numFdBits > numFdBits) {
1761		numFdBits = tsdPtr->numFdBits;
1762	    }
1763	    polling = tsdPtr->polling;
1764	    UNLOCK_NOTIFIER_TSD;
1765	    if ((tsdPtr->polled = polling)) {
1766		timePtr = &poll;
1767	    }
1768	}
1769	UNLOCK_NOTIFIER;
1770
1771	/*
1772	 * Set up the select mask to include the receive pipe.
1773	 */
1774
1775	if (receivePipe >= numFdBits) {
1776	    numFdBits = receivePipe + 1;
1777	}
1778	FD_SET(receivePipe, &readableMask);
1779
1780	if (select(numFdBits, &readableMask, &writableMask, &exceptionalMask,
1781		timePtr) == -1) {
1782	    /*
1783	     * Try again immediately on an error.
1784	     */
1785
1786	    continue;
1787	}
1788
1789	/*
1790	 * Alert any threads that are waiting on a ready file descriptor.
1791	 */
1792
1793	LOCK_NOTIFIER;
1794	for (tsdPtr = waitingListPtr; tsdPtr; tsdPtr = tsdPtr->nextPtr) {
1795	    int found = 0;
1796	    SelectMasks readyMasks, checkMasks;
1797
1798	    LOCK_NOTIFIER_TSD;
1799	    FD_COPY(&(tsdPtr->checkMasks.readable), &checkMasks.readable);
1800	    FD_COPY(&(tsdPtr->checkMasks.writable), &checkMasks.writable);
1801	    FD_COPY(&(tsdPtr->checkMasks.exceptional), &checkMasks.exceptional);
1802	    UNLOCK_NOTIFIER_TSD;
1803	    found = tsdPtr->polled;
1804	    FD_ZERO(&readyMasks.readable);
1805	    FD_ZERO(&readyMasks.writable);
1806	    FD_ZERO(&readyMasks.exceptional);
1807
1808	    for (i = tsdPtr->numFdBits-1; i >= 0; --i) {
1809		if (FD_ISSET(i, &checkMasks.readable)
1810			&& FD_ISSET(i, &readableMask)) {
1811		    FD_SET(i, &readyMasks.readable);
1812		    found = 1;
1813		}
1814		if (FD_ISSET(i, &checkMasks.writable)
1815			&& FD_ISSET(i, &writableMask)) {
1816		    FD_SET(i, &readyMasks.writable);
1817		    found = 1;
1818		}
1819		if (FD_ISSET(i, &checkMasks.exceptional)
1820			&& FD_ISSET(i, &exceptionalMask)) {
1821		    FD_SET(i, &readyMasks.exceptional);
1822		    found = 1;
1823		}
1824	    }
1825
1826	    if (found) {
1827		/*
1828		 * Remove the ThreadSpecificData structure of this thread from
1829		 * the waiting list. This prevents us from spinning
1830		 * continuously on select until the other threads runs and
1831		 * services the file event.
1832		 */
1833
1834		OnOffWaitingList(tsdPtr, 0, 0);
1835
1836		LOCK_NOTIFIER_TSD;
1837		FD_COPY(&readyMasks.readable, &(tsdPtr->readyMasks.readable));
1838		FD_COPY(&readyMasks.writable, &(tsdPtr->readyMasks.writable));
1839		FD_COPY(&readyMasks.exceptional, &(tsdPtr->readyMasks.exceptional));
1840		UNLOCK_NOTIFIER_TSD;
1841		tsdPtr->polled = 0;
1842		if (tsdPtr->runLoop) {
1843		    CFRunLoopSourceSignal(tsdPtr->runLoopSource);
1844		    CFRunLoopWakeUp(tsdPtr->runLoop);
1845		}
1846	    }
1847	}
1848	UNLOCK_NOTIFIER;
1849
1850	/*
1851	 * Consume the next byte from the notifier pipe if the pipe was
1852	 * readable. Note that there may be multiple bytes pending, but to
1853	 * avoid a race condition we only read one at a time.
1854	 */
1855
1856	if (FD_ISSET(receivePipe, &readableMask)) {
1857	    i = read(receivePipe, buf, 1);
1858
1859	    if ((i == 0) || ((i == 1) && (buf[0] == 'q'))) {
1860		/*
1861		 * Someone closed the write end of the pipe or sent us a Quit
1862		 * message [Bug: 4139] and then closed the write end of the
1863		 * pipe so we need to shut down the notifier thread.
1864		 */
1865
1866		break;
1867	    }
1868	}
1869    }
1870    pthread_exit(0);
1871}
1872
1873#ifdef HAVE_PTHREAD_ATFORK
1874/*
1875 *----------------------------------------------------------------------
1876 *
1877 * AtForkPrepare --
1878 *
1879 *	Lock the notifier in preparation for a fork.
1880 *
1881 * Results:
1882 *	None.
1883 *
1884 * Side effects:
1885 *	None.
1886 *
1887 *----------------------------------------------------------------------
1888 */
1889
1890static void
1891AtForkPrepare(void)
1892{
1893    ThreadSpecificData *tsdPtr = TCL_TSD_INIT(&dataKey);
1894
1895    LOCK_NOTIFIER_INIT;
1896    LOCK_NOTIFIER;
1897    LOCK_NOTIFIER_TSD;
1898}
1899
1900/*
1901 *----------------------------------------------------------------------
1902 *
1903 * AtForkParent --
1904 *
1905 *	Unlock the notifier in the parent after a fork.
1906 *
1907 * Results:
1908 *	None.
1909 *
1910 * Side effects:
1911 *	None.
1912 *
1913 *----------------------------------------------------------------------
1914 */
1915
1916static void
1917AtForkParent(void)
1918{
1919    ThreadSpecificData *tsdPtr = TCL_TSD_INIT(&dataKey);
1920
1921    UNLOCK_NOTIFIER_TSD;
1922    UNLOCK_NOTIFIER;
1923    UNLOCK_NOTIFIER_INIT;
1924}
1925
1926/*
1927 *----------------------------------------------------------------------
1928 *
1929 * AtForkChild --
1930 *
1931 *	Unlock and reinstall the notifier in the child after a fork.
1932 *
1933 * Results:
1934 *	None.
1935 *
1936 * Side effects:
1937 *	None.
1938 *
1939 *----------------------------------------------------------------------
1940 */
1941
1942static void
1943AtForkChild(void)
1944{
1945    ThreadSpecificData *tsdPtr = TCL_TSD_INIT(&dataKey);
1946
1947    UNLOCK_NOTIFIER_TSD;
1948    UNLOCK_NOTIFIER;
1949    UNLOCK_NOTIFIER_INIT;
1950    if (tsdPtr->runLoop) {
1951	tsdPtr->runLoop = NULL;
1952	if (!noCFafterFork) {
1953	    CFRunLoopSourceInvalidate(tsdPtr->runLoopSource);
1954	    CFRelease(tsdPtr->runLoopSource);
1955	    if (tsdPtr->runLoopTimer) {
1956		CFRunLoopTimerInvalidate(tsdPtr->runLoopTimer);
1957		CFRelease(tsdPtr->runLoopTimer);
1958	    }
1959	}
1960	tsdPtr->runLoopSource = NULL;
1961	tsdPtr->runLoopTimer = NULL;
1962    }
1963    if (notifierCount > 0) {
1964	notifierCount = 1;
1965	notifierThreadRunning = 0;
1966
1967	/*
1968	 * Assume that the return value of Tcl_InitNotifier in the child will
1969	 * be identical to the one stored as clientData in tclNotify.c's
1970	 * ThreadSpecificData by the parent's TclInitNotifier, so discard the
1971	 * return value here. This assumption may require the fork() to be
1972	 * executed in the main thread of the parent, otherwise
1973	 * Tcl_AlertNotifier may break in the child.
1974	 */
1975
1976	if (!noCFafterFork) {
1977	    Tcl_InitNotifier();
1978	}
1979    }
1980}
1981#endif /* HAVE_PTHREAD_ATFORK */
1982
1983#else /* HAVE_COREFOUNDATION */
1984
1985void
1986TclMacOSXNotifierAddRunLoopMode(
1987    CONST void *runLoopMode)
1988{
1989    Tcl_Panic("TclMacOSXNotifierAddRunLoopMode: "
1990	    "Tcl not built with CoreFoundation support");
1991}
1992
1993#endif /* HAVE_COREFOUNDATION */
1994
1995/*
1996 * Local Variables:
1997 * mode: c
1998 * c-basic-offset: 4
1999 * fill-column: 78
2000 * End:
2001 */
2002