os/persistentdata/persistentstorage/sqlite3api/TEST/TCL/tcldistribution/macosx/tclMacOSXNotify.c
First public contribution.
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.
8 * Copyright (c) 1995-1997 Sun Microsystems, Inc.
9 * Copyright 2001, Apple Computer, Inc.
10 * Copyright (c) 2005-2007 Daniel A. Steffen <das@users.sourceforge.net>
12 * See the file "license.terms" for information on usage and redistribution of
13 * this file, and for a DISCLAIMER OF ALL WARRANTIES.
15 * RCS: @(#) $Id: tclMacOSXNotify.c,v 1.1.2.12 2007/04/29 02:21:33 das Exp $
20 #ifdef HAVE_COREFOUNDATION /* Traditional unix select-based notifier is
21 * in tclUnixNotfy.c */
22 #include <CoreFoundation/CoreFoundation.h>
25 extern TclStubs tclStubs;
26 extern Tcl_NotifierProcs tclOriginalNotifier;
29 * This structure is used to keep track of the notifier info for a registered
33 typedef struct FileHandler {
35 int mask; /* Mask of desired events: TCL_READABLE,
37 int readyMask; /* Mask of events that have been seen since
38 * the last time file handlers were invoked
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. */
47 * The following structure is what is added to the Tcl event queue when file
48 * handlers are ready to fire.
51 typedef struct FileHandlerEvent {
52 Tcl_Event header; /* Information that is standard for all
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). */
62 * The following structure contains a set of select() masks to track readable,
63 * writable, and exceptional conditions.
66 typedef struct SelectMasks {
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.
78 typedef 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
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
109 int eventReady; /* True if an event is ready to be
111 } ThreadSpecificData;
113 static Tcl_ThreadDataKey dataKey;
116 * The following static indicates the number of threads that have initialized
119 * You must hold the notifierInitLock before accessing this variable.
122 static int notifierCount = 0;
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
129 * You must hold the notifierLock before accessing this list.
132 static ThreadSpecificData *waitingListPtr = NULL;
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.
145 * You must hold the notifierLock lock before writing to the pipe.
148 static int triggerPipe = -1;
149 static int receivePipe = -1; /* Output end of triggerPipe */
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).
160 #if defined(HAVE_LIBKERN_OSATOMIC_H) && defined(HAVE_OSSPINLOCKLOCK)
162 * Use OSSpinLock API where available (Tiger or later).
165 #include <libkern/OSAtomic.h>
167 #if defined(HAVE_WEAK_IMPORT) && MAC_OS_X_VERSION_MIN_REQUIRED < 1040
169 * Support for weakly importing spinlock API.
171 #define WEAK_IMPORT_SPINLOCKLOCK
172 #if MAC_OS_X_VERSION_MAX_ALLOWED >= 1050
173 #define VOLATILE volatile
180 extern void OSSpinLockLock(VOLATILE OSSpinLock *lock) WEAK_IMPORT_ATTRIBUTE;
181 extern void OSSpinLockUnlock(VOLATILE OSSpinLock *lock) WEAK_IMPORT_ATTRIBUTE;
182 extern bool OSSpinLockTry(VOLATILE OSSpinLock *lock) WEAK_IMPORT_ATTRIBUTE;
183 extern void _spin_lock(VOLATILE OSSpinLock *lock) WEAK_IMPORT_ATTRIBUTE;
184 extern void _spin_unlock(VOLATILE OSSpinLock *lock) WEAK_IMPORT_ATTRIBUTE;
185 extern bool _spin_lock_try(VOLATILE OSSpinLock *lock) WEAK_IMPORT_ATTRIBUTE;
186 static void (* lockLock)(VOLATILE OSSpinLock *lock) = NULL;
187 static void (* lockUnlock)(VOLATILE OSSpinLock *lock) = NULL;
188 static bool (* lockTry)(VOLATILE OSSpinLock *lock) = NULL;
190 static pthread_once_t spinLockLockInitControl = PTHREAD_ONCE_INIT;
191 static 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");
199 #define SpinLockLock(p) lockLock(p)
200 #define SpinLockUnlock(p) lockUnlock(p)
201 #define SpinLockTry(p) lockTry(p)
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
211 * Otherwise, use commpage spinlock SPI directly.
214 typedef uint32_t OSSpinLock;
215 extern void _spin_lock(OSSpinLock *lock);
216 extern void _spin_unlock(OSSpinLock *lock);
217 extern 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
223 #endif /* HAVE_LIBKERN_OSATOMIC_H && HAVE_OSSPINLOCKLOCK */
226 * These spinlocks lock access to the global notifier state.
229 static OSSpinLock notifierInitLock = SPINLOCK_INIT;
230 static OSSpinLock notifierLock = SPINLOCK_INIT;
233 * Macros abstracting notifier locking/unlocking
236 #define LOCK_NOTIFIER_INIT SpinLockLock(¬ifierInitLock)
237 #define UNLOCK_NOTIFIER_INIT SpinLockUnlock(¬ifierInitLock)
238 #define LOCK_NOTIFIER SpinLockLock(¬ifierLock)
239 #define UNLOCK_NOTIFIER SpinLockUnlock(¬ifierLock)
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.
250 #define POLL_WANT 0x1
251 #define POLL_DONE 0x2
254 * This is the thread ID of the notifier thread that does select.
257 static pthread_t notifierThread;
260 * Custom run loop mode containing only the run loop source for the
264 #ifndef TCL_EVENTS_ONLY_RUN_LOOP_MODE
265 #define TCL_EVENTS_ONLY_RUN_LOOP_MODE "com.tcltk.tclEventsOnlyRunLoopMode"
267 #ifdef __CONSTANT_CFSTRINGS__
268 #define tclEventsOnlyRunLoopMode CFSTR(TCL_EVENTS_ONLY_RUN_LOOP_MODE)
270 static CFStringRef tclEventsOnlyRunLoopMode = NULL;
274 * Static routines defined in this file.
277 static void NotifierThreadProc(ClientData clientData)
278 __attribute__ ((__noreturn__));
279 static int FileHandlerEventProc(Tcl_Event *evPtr, int flags);
281 #ifdef HAVE_PTHREAD_ATFORK
282 static int atForkInit = 0;
283 static void AtForkPrepare(void);
284 static void AtForkParent(void);
285 static 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
289 extern int pthread_atfork(void (*prepare)(void), void (*parent)(void),
290 void (*child)(void)) WEAK_IMPORT_ATTRIBUTE;
291 #endif /* HAVE_WEAK_IMPORT */
292 #endif /* HAVE_PTHREAD_ATFORK */
295 *----------------------------------------------------------------------
297 * Tcl_InitNotifier --
299 * Initializes the platform specific notifier state.
302 * Returns a handle to the notifier state for this thread.
307 *----------------------------------------------------------------------
311 Tcl_InitNotifier(void)
313 ThreadSpecificData *tsdPtr = TCL_TSD_INIT(&dataKey);
315 tsdPtr->eventReady = 0;
317 #ifdef WEAK_IMPORT_SPINLOCKLOCK
319 * Initialize support for weakly imported spinlock API.
321 if (pthread_once(&spinLockLockInitControl, SpinLockLockInit)) {
322 Tcl_Panic("Tcl_InitNotifier: pthread_once failed");
326 #ifndef __CONSTANT_CFSTRINGS__
327 if (!tclEventsOnlyRunLoopMode) {
328 tclEventsOnlyRunLoopMode = CFSTR(TCL_EVENTS_ONLY_RUN_LOOP_MODE);
333 * Initialize CFRunLoopSource and add it to CFRunLoop of this thread.
336 if (!tsdPtr->runLoop) {
337 CFRunLoopRef runLoop = CFRunLoopGetCurrent();
338 CFRunLoopSourceRef runLoopSource;
339 CFRunLoopSourceContext runLoopSourceContext;
341 bzero(&runLoopSourceContext, sizeof(CFRunLoopSourceContext));
342 runLoopSourceContext.info = tsdPtr;
343 runLoopSource = CFRunLoopSourceCreate(NULL, 0, &runLoopSourceContext);
344 if (!runLoopSource) {
345 Tcl_Panic("Tcl_InitNotifier: could not create CFRunLoopSource");
347 CFRunLoopAddSource(runLoop, runLoopSource, kCFRunLoopCommonModes);
348 CFRunLoopAddSource(runLoop, runLoopSource, tclEventsOnlyRunLoopMode);
349 tsdPtr->runLoopSource = runLoopSource;
350 tsdPtr->runLoop = runLoop;
354 #ifdef HAVE_PTHREAD_ATFORK
356 * Install pthread_atfork handlers to reinitialize the notifier in the
361 #ifdef WEAK_IMPORT_PTHREAD_ATFORK
362 pthread_atfork != NULL &&
365 int result = pthread_atfork(AtForkPrepare, AtForkParent, AtForkChild);
367 Tcl_Panic("Tcl_InitNotifier: pthread_atfork failed");
372 if (notifierCount == 0) {
376 * Initialize trigger pipe.
379 if (pipe(fds) != 0) {
380 Tcl_Panic("Tcl_InitNotifier: could not create trigger pipe");
383 status = fcntl(fds[0], F_GETFL);
384 status |= O_NONBLOCK;
385 if (fcntl(fds[0], F_SETFL, status) < 0) {
386 Tcl_Panic("Tcl_InitNotifier: could not make receive pipe non blocking");
388 status = fcntl(fds[1], F_GETFL);
389 status |= O_NONBLOCK;
390 if (fcntl(fds[1], F_SETFL, status) < 0) {
391 Tcl_Panic("Tcl_InitNotifier: could not make trigger pipe non blocking");
394 receivePipe = fds[0];
395 triggerPipe = fds[1];
398 * Create notifier thread lazily in Tcl_WaitForEvent() to avoid
399 * interfering with fork() followed immediately by execve()
400 * (cannot execve() when more than one thread is present).
406 UNLOCK_NOTIFIER_INIT;
408 return (ClientData) tsdPtr;
412 *----------------------------------------------------------------------
414 * Tcl_FinalizeNotifier --
416 * This function is called to cleanup the notifier state before a thread
423 * May terminate the background notifier thread if this is the last
426 *----------------------------------------------------------------------
430 Tcl_FinalizeNotifier(
431 ClientData clientData) /* Not used. */
433 ThreadSpecificData *tsdPtr = TCL_TSD_INIT(&dataKey);
439 * If this is the last thread to use the notifier, close the notifier pipe
440 * and wait for the background thread to terminate.
443 if (notifierCount == 0) {
446 if (triggerPipe < 0) {
447 Tcl_Panic("Tcl_FinalizeNotifier: notifier pipe not initialized");
451 * Send "q" message to the notifier thread so that it will terminate.
452 * The notifier will return from its call to select() and notice that
453 * a "q" message has arrived, it will then close its side of the pipe
454 * and terminate its thread. Note the we can not just close the pipe
455 * and check for EOF in the notifier thread because if a background
456 * child process was created with exec, select() would not register
457 * the EOF on the pipe until the child processes had terminated. [Bug:
458 * 4139] [Bug: 1222872]
461 write(triggerPipe, "q", 1);
464 if (notifierThread) {
465 result = pthread_join(notifierThread, NULL);
467 Tcl_Panic("Tcl_FinalizeNotifier: unable to join notifier thread");
475 UNLOCK_NOTIFIER_INIT;
477 LOCK_NOTIFIER; /* for concurrency with Tcl_AlertNotifier */
478 if (tsdPtr->runLoop) {
479 tsdPtr->runLoop = NULL;
482 * Remove runLoopSource from all CFRunLoops and release it.
485 CFRunLoopSourceInvalidate(tsdPtr->runLoopSource);
486 CFRelease(tsdPtr->runLoopSource);
487 tsdPtr->runLoopSource = NULL;
493 *----------------------------------------------------------------------
495 * Tcl_AlertNotifier --
497 * Wake up the specified notifier from any thread. This routine is called
498 * by the platform independent notifier code whenever the Tcl_ThreadAlert
499 * routine is called. This routine is guaranteed not to be called on a
500 * given notifier after Tcl_FinalizeNotifier is called for that notifier.
506 * Signals the notifier condition variable for the specified notifier.
508 *----------------------------------------------------------------------
513 ClientData clientData)
515 ThreadSpecificData *tsdPtr = (ThreadSpecificData *) clientData;
518 if (tsdPtr->runLoop) {
519 tsdPtr->eventReady = 1;
520 CFRunLoopSourceSignal(tsdPtr->runLoopSource);
521 CFRunLoopWakeUp(tsdPtr->runLoop);
527 *----------------------------------------------------------------------
531 * This function sets the current notifier timer value. This interface is
532 * not implemented in this notifier because we are always running inside
541 *----------------------------------------------------------------------
546 Tcl_Time *timePtr) /* Timeout value, may be NULL. */
549 * The interval timer doesn't do anything in this implementation, because
550 * the only event loop is via Tcl_DoOneEvent, which passes timeout values
551 * to Tcl_WaitForEvent.
554 if (tclStubs.tcl_SetTimer != tclOriginalNotifier.setTimerProc) {
555 tclStubs.tcl_SetTimer(timePtr);
560 *----------------------------------------------------------------------
562 * Tcl_ServiceModeHook --
564 * This function is invoked whenever the service mode changes.
572 *----------------------------------------------------------------------
577 int mode) /* Either TCL_SERVICE_ALL, or
578 * TCL_SERVICE_NONE. */
583 *----------------------------------------------------------------------
585 * Tcl_CreateFileHandler --
587 * This function registers a file handler with the select notifier.
593 * Creates a new file handler structure.
595 *----------------------------------------------------------------------
599 Tcl_CreateFileHandler(
600 int fd, /* Handle of stream to watch. */
601 int mask, /* OR'ed combination of TCL_READABLE,
602 * TCL_WRITABLE, and TCL_EXCEPTION: indicates
603 * conditions under which proc should be
605 Tcl_FileProc *proc, /* Function to call for each selected
607 ClientData clientData) /* Arbitrary data to pass to proc. */
609 ThreadSpecificData *tsdPtr = TCL_TSD_INIT(&dataKey);
610 FileHandler *filePtr;
612 if (tclStubs.tcl_CreateFileHandler !=
613 tclOriginalNotifier.createFileHandlerProc) {
614 tclStubs.tcl_CreateFileHandler(fd, mask, proc, clientData);
618 for (filePtr = tsdPtr->firstFileHandlerPtr; filePtr != NULL;
619 filePtr = filePtr->nextPtr) {
620 if (filePtr->fd == fd) {
624 if (filePtr == NULL) {
625 filePtr = (FileHandler*) ckalloc(sizeof(FileHandler));
627 filePtr->readyMask = 0;
628 filePtr->nextPtr = tsdPtr->firstFileHandlerPtr;
629 tsdPtr->firstFileHandlerPtr = filePtr;
631 filePtr->proc = proc;
632 filePtr->clientData = clientData;
633 filePtr->mask = mask;
636 * Update the check masks for this file.
639 if (mask & TCL_READABLE) {
640 FD_SET(fd, &(tsdPtr->checkMasks.readable));
642 FD_CLR(fd, &(tsdPtr->checkMasks.readable));
644 if (mask & TCL_WRITABLE) {
645 FD_SET(fd, &(tsdPtr->checkMasks.writable));
647 FD_CLR(fd, &(tsdPtr->checkMasks.writable));
649 if (mask & TCL_EXCEPTION) {
650 FD_SET(fd, &(tsdPtr->checkMasks.exceptional));
652 FD_CLR(fd, &(tsdPtr->checkMasks.exceptional));
654 if (tsdPtr->numFdBits <= fd) {
655 tsdPtr->numFdBits = fd+1;
660 *----------------------------------------------------------------------
662 * Tcl_DeleteFileHandler --
664 * Cancel a previously-arranged callback arrangement for a file.
670 * If a callback was previously registered on file, remove it.
672 *----------------------------------------------------------------------
676 Tcl_DeleteFileHandler(
677 int fd) /* Stream id for which to remove callback
680 FileHandler *filePtr, *prevPtr;
682 ThreadSpecificData *tsdPtr = TCL_TSD_INIT(&dataKey);
684 if (tclStubs.tcl_DeleteFileHandler !=
685 tclOriginalNotifier.deleteFileHandlerProc) {
686 tclStubs.tcl_DeleteFileHandler(fd);
691 * Find the entry for the given file (and return if there isn't one).
694 for (prevPtr = NULL, filePtr = tsdPtr->firstFileHandlerPtr; ;
695 prevPtr = filePtr, filePtr = filePtr->nextPtr) {
696 if (filePtr == NULL) {
699 if (filePtr->fd == fd) {
705 * Update the check masks for this file.
708 if (filePtr->mask & TCL_READABLE) {
709 FD_CLR(fd, &(tsdPtr->checkMasks.readable));
711 if (filePtr->mask & TCL_WRITABLE) {
712 FD_CLR(fd, &(tsdPtr->checkMasks.writable));
714 if (filePtr->mask & TCL_EXCEPTION) {
715 FD_CLR(fd, &(tsdPtr->checkMasks.exceptional));
719 * Find current max fd.
722 if (fd+1 == tsdPtr->numFdBits) {
723 tsdPtr->numFdBits = 0;
724 for (i = fd-1; i >= 0; i--) {
725 if (FD_ISSET(i, &(tsdPtr->checkMasks.readable))
726 || FD_ISSET(i, &(tsdPtr->checkMasks.writable))
727 || FD_ISSET(i, &(tsdPtr->checkMasks.exceptional))) {
728 tsdPtr->numFdBits = i+1;
735 * Clean up information in the callback record.
738 if (prevPtr == NULL) {
739 tsdPtr->firstFileHandlerPtr = filePtr->nextPtr;
741 prevPtr->nextPtr = filePtr->nextPtr;
743 ckfree((char *) filePtr);
747 *----------------------------------------------------------------------
749 * FileHandlerEventProc --
751 * This function is called by Tcl_ServiceEvent when a file event reaches
752 * the front of the event queue. This function is responsible for
753 * actually handling the event by invoking the callback for the file
757 * Returns 1 if the event was handled, meaning it should be removed from
758 * the queue. Returns 0 if the event was not handled, meaning it should
759 * stay on the queue. The only time the event isn't handled is if the
760 * TCL_FILE_EVENTS flag bit isn't set.
763 * Whatever the file handler's callback function does.
765 *----------------------------------------------------------------------
769 FileHandlerEventProc(
770 Tcl_Event *evPtr, /* Event to service. */
771 int flags) /* Flags that indicate what events to handle,
772 * such as TCL_FILE_EVENTS. */
775 FileHandler *filePtr;
776 FileHandlerEvent *fileEvPtr = (FileHandlerEvent *) evPtr;
777 ThreadSpecificData *tsdPtr;
779 if (!(flags & TCL_FILE_EVENTS)) {
784 * Search through the file handlers to find the one whose handle matches
785 * the event. We do this rather than keeping a pointer to the file handler
786 * directly in the event, so that the handler can be deleted while the
787 * event is queued without leaving a dangling pointer.
790 tsdPtr = TCL_TSD_INIT(&dataKey);
791 for (filePtr = tsdPtr->firstFileHandlerPtr; filePtr != NULL;
792 filePtr = filePtr->nextPtr) {
793 if (filePtr->fd != fileEvPtr->fd) {
798 * The code is tricky for two reasons:
799 * 1. The file handler's desired events could have changed since the
800 * time when the event was queued, so AND the ready mask with the
802 * 2. The file could have been closed and re-opened since the time
803 * when the event was queued. This is why the ready mask is stored
804 * in the file handler rather than the queued event: it will be
805 * zeroed when a new file handler is created for the newly opened
809 mask = filePtr->readyMask & filePtr->mask;
810 filePtr->readyMask = 0;
812 (*filePtr->proc)(filePtr->clientData, mask);
820 *----------------------------------------------------------------------
822 * Tcl_WaitForEvent --
824 * This function is called by Tcl_DoOneEvent to wait for new events on
825 * the message queue. If the block time is 0, then Tcl_WaitForEvent just
826 * polls without blocking.
829 * Returns -1 if the select would block forever, otherwise returns 0.
832 * Queues file events that are detected by the select.
834 *----------------------------------------------------------------------
839 Tcl_Time *timePtr) /* Maximum block time, or NULL. */
841 FileHandler *filePtr;
842 FileHandlerEvent *fileEvPtr;
845 ThreadSpecificData *tsdPtr = TCL_TSD_INIT(&dataKey);
847 if (tclStubs.tcl_WaitForEvent != tclOriginalNotifier.waitForEventProc) {
848 return tclStubs.tcl_WaitForEvent(timePtr);
852 * Start notifier thread if necessary.
856 if (!notifierCount) {
857 Tcl_Panic("Tcl_WaitForEvent: notifier not initialized");
859 if (!notifierThread) {
863 pthread_attr_init(&attr);
864 pthread_attr_setscope(&attr, PTHREAD_SCOPE_SYSTEM);
865 pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_JOINABLE);
866 pthread_attr_setstacksize(&attr, 60 * 1024);
867 result = pthread_create(¬ifierThread, &attr,
868 (void * (*)(void *))NotifierThreadProc, NULL);
869 pthread_attr_destroy(&attr);
870 if (result || !notifierThread) {
871 Tcl_Panic("Tcl_WaitForEvent: unable to start notifier thread");
874 UNLOCK_NOTIFIER_INIT;
877 * Place this thread on the list of interested threads, signal the
878 * notifier thread, and wait for a response or a timeout.
882 if (!tsdPtr->runLoop) {
883 Tcl_Panic("Tcl_WaitForEvent: CFRunLoop not initialized");
885 waitForFiles = (tsdPtr->numFdBits > 0);
886 if (timePtr != NULL && timePtr->sec == 0 && timePtr->usec == 0) {
888 * Cannot emulate a polling select with a polling condition variable.
889 * Instead, pretend to wait for files and tell the notifier thread
890 * what we are doing. The notifier thread makes sure it goes through
891 * select with its select mask in the same state as ours currently is.
892 * We block until that happens.
896 tsdPtr->pollState = POLL_WANT;
899 tsdPtr->pollState = 0;
904 * Add the ThreadSpecificData structure of this thread to the list of
905 * ThreadSpecificData structures of all threads that are waiting on
909 tsdPtr->nextPtr = waitingListPtr;
910 if (waitingListPtr) {
911 waitingListPtr->prevPtr = tsdPtr;
914 waitingListPtr = tsdPtr;
917 write(triggerPipe, "", 1);
920 FD_ZERO(&(tsdPtr->readyMasks.readable));
921 FD_ZERO(&(tsdPtr->readyMasks.writable));
922 FD_ZERO(&(tsdPtr->readyMasks.exceptional));
924 if (!tsdPtr->eventReady) {
925 CFTimeInterval waitTime;
926 CFStringRef runLoopMode;
928 if (timePtr == NULL) {
929 waitTime = 1.0e10; /* Wait forever, as per CFRunLoop.c */
931 waitTime = timePtr->sec + 1.0e-6 * timePtr->usec;
934 * If the run loop is already running (e.g. if Tcl_WaitForEvent was
935 * called recursively), re-run it in a custom run loop mode containing
936 * only the source for the notifier thread, otherwise wakeups from other
937 * sources added to the common run loop modes might get lost.
939 if ((runLoopMode = CFRunLoopCopyCurrentMode(tsdPtr->runLoop))) {
940 CFRelease(runLoopMode);
941 runLoopMode = tclEventsOnlyRunLoopMode;
943 runLoopMode = kCFRunLoopDefaultMode;
946 CFRunLoopRunInMode(runLoopMode, waitTime, TRUE);
949 tsdPtr->eventReady = 0;
951 if (waitForFiles && tsdPtr->onList) {
953 * Remove the ThreadSpecificData structure of this thread from the
954 * waiting list. Alert the notifier thread to recompute its select
955 * masks - skipping this caused a hang when trying to close a pipe
956 * which the notifier thread was still doing a select on.
959 if (tsdPtr->prevPtr) {
960 tsdPtr->prevPtr->nextPtr = tsdPtr->nextPtr;
962 waitingListPtr = tsdPtr->nextPtr;
964 if (tsdPtr->nextPtr) {
965 tsdPtr->nextPtr->prevPtr = tsdPtr->prevPtr;
967 tsdPtr->nextPtr = tsdPtr->prevPtr = NULL;
969 write(triggerPipe, "", 1);
973 * Queue all detected file events before returning.
976 for (filePtr = tsdPtr->firstFileHandlerPtr; (filePtr != NULL);
977 filePtr = filePtr->nextPtr) {
980 if (FD_ISSET(filePtr->fd, &(tsdPtr->readyMasks.readable))) {
981 mask |= TCL_READABLE;
983 if (FD_ISSET(filePtr->fd, &(tsdPtr->readyMasks.writable))) {
984 mask |= TCL_WRITABLE;
986 if (FD_ISSET(filePtr->fd, &(tsdPtr->readyMasks.exceptional))) {
987 mask |= TCL_EXCEPTION;
995 * Don't bother to queue an event if the mask was previously non-zero
996 * since an event must still be on the queue.
999 if (filePtr->readyMask == 0) {
1000 fileEvPtr = (FileHandlerEvent *) ckalloc(sizeof(FileHandlerEvent));
1001 fileEvPtr->header.proc = FileHandlerEventProc;
1002 fileEvPtr->fd = filePtr->fd;
1003 Tcl_QueueEvent((Tcl_Event *) fileEvPtr, TCL_QUEUE_TAIL);
1005 filePtr->readyMask = mask;
1012 *----------------------------------------------------------------------
1014 * NotifierThreadProc --
1016 * This routine is the initial (and only) function executed by the
1017 * special notifier thread. Its job is to wait for file descriptors to
1018 * become readable or writable or to have an exception condition and then
1019 * to notify other threads who are interested in this information by
1020 * signalling a condition variable. Other threads can signal this
1021 * notifier thread of a change in their interests by writing a single
1022 * byte to a special pipe that the notifier thread is monitoring.
1025 * None. Once started, this routine never exits. It dies with the overall
1029 * The trigger pipe used to signal the notifier thread is created when
1030 * the notifier thread first starts.
1032 *----------------------------------------------------------------------
1037 ClientData clientData) /* Not used. */
1039 ThreadSpecificData *tsdPtr;
1040 fd_set readableMask;
1041 fd_set writableMask;
1042 fd_set exceptionalMask;
1043 int i, numFdBits = 0;
1045 struct timeval poll = {0., 0.}, *timePtr;
1049 * Look for file events and report them to interested threads.
1053 FD_ZERO(&readableMask);
1054 FD_ZERO(&writableMask);
1055 FD_ZERO(&exceptionalMask);
1058 * Compute the logical OR of the select masks from all the waiting
1064 for (tsdPtr = waitingListPtr; tsdPtr; tsdPtr = tsdPtr->nextPtr) {
1065 for (i = tsdPtr->numFdBits-1; i >= 0; --i) {
1066 if (FD_ISSET(i, &(tsdPtr->checkMasks.readable))) {
1067 FD_SET(i, &readableMask);
1069 if (FD_ISSET(i, &(tsdPtr->checkMasks.writable))) {
1070 FD_SET(i, &writableMask);
1072 if (FD_ISSET(i, &(tsdPtr->checkMasks.exceptional))) {
1073 FD_SET(i, &exceptionalMask);
1076 if (tsdPtr->numFdBits > numFdBits) {
1077 numFdBits = tsdPtr->numFdBits;
1079 if (tsdPtr->pollState & POLL_WANT) {
1081 * Here we make sure we go through select() with the same mask
1082 * bits that were present when the thread tried to poll.
1085 tsdPtr->pollState |= POLL_DONE;
1092 * Set up the select mask to include the receive pipe.
1095 if (receivePipe >= numFdBits) {
1096 numFdBits = receivePipe + 1;
1098 FD_SET(receivePipe, &readableMask);
1100 if (select(numFdBits, &readableMask, &writableMask, &exceptionalMask,
1103 * Try again immediately on an error.
1110 * Alert any threads that are waiting on a ready file descriptor.
1114 for (tsdPtr = waitingListPtr; tsdPtr; tsdPtr = tsdPtr->nextPtr) {
1117 for (i = tsdPtr->numFdBits-1; i >= 0; --i) {
1118 if (FD_ISSET(i, &(tsdPtr->checkMasks.readable))
1119 && FD_ISSET(i, &readableMask)) {
1120 FD_SET(i, &(tsdPtr->readyMasks.readable));
1123 if (FD_ISSET(i, &(tsdPtr->checkMasks.writable))
1124 && FD_ISSET(i, &writableMask)) {
1125 FD_SET(i, &(tsdPtr->readyMasks.writable));
1128 if (FD_ISSET(i, &(tsdPtr->checkMasks.exceptional))
1129 && FD_ISSET(i, &exceptionalMask)) {
1130 FD_SET(i, &(tsdPtr->readyMasks.exceptional));
1135 if (found || (tsdPtr->pollState & POLL_DONE)) {
1136 tsdPtr->eventReady = 1;
1137 if (tsdPtr->onList) {
1139 * Remove the ThreadSpecificData structure of this thread
1140 * from the waiting list. This prevents us from
1141 * continuously spining on select until the other threads
1142 * runs and services the file event.
1145 if (tsdPtr->prevPtr) {
1146 tsdPtr->prevPtr->nextPtr = tsdPtr->nextPtr;
1148 waitingListPtr = tsdPtr->nextPtr;
1150 if (tsdPtr->nextPtr) {
1151 tsdPtr->nextPtr->prevPtr = tsdPtr->prevPtr;
1153 tsdPtr->nextPtr = tsdPtr->prevPtr = NULL;
1155 tsdPtr->pollState = 0;
1157 if (tsdPtr->runLoop) {
1158 CFRunLoopSourceSignal(tsdPtr->runLoopSource);
1159 CFRunLoopWakeUp(tsdPtr->runLoop);
1166 * Consume the next byte from the notifier pipe if the pipe was
1167 * readable. Note that there may be multiple bytes pending, but to
1168 * avoid a race condition we only read one at a time.
1171 if (FD_ISSET(receivePipe, &readableMask)) {
1172 i = read(receivePipe, buf, 1);
1174 if ((i == 0) || ((i == 1) && (buf[0] == 'q'))) {
1176 * Someone closed the write end of the pipe or sent us a Quit
1177 * message [Bug: 4139] and then closed the write end of the
1178 * pipe so we need to shut down the notifier thread.
1188 #ifdef HAVE_PTHREAD_ATFORK
1190 *----------------------------------------------------------------------
1194 * Lock the notifier in preparation for a fork.
1202 *----------------------------------------------------------------------
1213 *----------------------------------------------------------------------
1217 * Unlock the notifier in the parent after a fork.
1225 *----------------------------------------------------------------------
1232 UNLOCK_NOTIFIER_INIT;
1236 *----------------------------------------------------------------------
1240 * Unlock and reinstall the notifier in the child after a fork.
1248 *----------------------------------------------------------------------
1254 ThreadSpecificData *tsdPtr = TCL_TSD_INIT(&dataKey);
1257 UNLOCK_NOTIFIER_INIT;
1258 if (tsdPtr->runLoop) {
1259 tsdPtr->runLoop = NULL;
1260 CFRunLoopSourceInvalidate(tsdPtr->runLoopSource);
1261 CFRelease(tsdPtr->runLoopSource);
1262 tsdPtr->runLoopSource = NULL;
1264 if (notifierCount > 0) {
1268 * Assume that the return value of Tcl_InitNotifier in the child will
1269 * be identical to the one stored as clientData in tclNotify.c's
1270 * ThreadSpecificData by the parent's TclInitNotifier, so discard the
1271 * return value here. This assumption may require the fork() to be
1272 * executed in the main thread of the parent, otherwise
1273 * Tcl_AlertNotifier may break in the child.
1279 #endif /* HAVE_PTHREAD_ATFORK */
1281 #endif /* HAVE_COREFOUNDATION */