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