os/persistentdata/persistentstorage/sqlite3api/TEST/TCL/tcldistribution/macosx/tclMacOSXNotify.c
author sl@SLION-WIN7.fritz.box
Fri, 15 Jun 2012 03:10:57 +0200
changeset 0 bde4ae8d615e
permissions -rw-r--r--
First public contribution.
     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-2007 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.12 2007/04/29 02:21:33 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 
    25 extern TclStubs tclStubs;
    26 extern Tcl_NotifierProcs tclOriginalNotifier;
    27 
    28 /*
    29  * This structure is used to keep track of the notifier info for a registered
    30  * file.
    31  */
    32 
    33 typedef 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 
    51 typedef 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 
    66 typedef 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 
    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
   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 
   113 static 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 
   122 static 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 
   132 static 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 
   148 static int triggerPipe = -1;
   149 static 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
   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;
   189 #undef VOLATILE
   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");
   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 
   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
   222 
   223 #endif /* HAVE_LIBKERN_OSATOMIC_H && HAVE_OSSPINLOCKLOCK */
   224 
   225 /*
   226  * These spinlocks lock access to the global notifier state.
   227  */
   228 
   229 static OSSpinLock notifierInitLock = SPINLOCK_INIT;
   230 static 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 
   257 static 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
   270 static CFStringRef tclEventsOnlyRunLoopMode = NULL;
   271 #endif
   272 
   273 /*
   274  * Static routines defined in this file.
   275  */
   276 
   277 static void	NotifierThreadProc(ClientData clientData)
   278 	__attribute__ ((__noreturn__));
   279 static int	FileHandlerEventProc(Tcl_Event *evPtr, int flags);
   280 
   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 */
   293 
   294 /*
   295  *----------------------------------------------------------------------
   296  *
   297  * Tcl_InitNotifier --
   298  *
   299  *	Initializes the platform specific notifier state.
   300  *
   301  * Results:
   302  *	Returns a handle to the notifier state for this thread.
   303  *
   304  * Side effects:
   305  *	None.
   306  *
   307  *----------------------------------------------------------------------
   308  */
   309 
   310 ClientData
   311 Tcl_InitNotifier(void)
   312 {
   313     ThreadSpecificData *tsdPtr = TCL_TSD_INIT(&dataKey);
   314 
   315     tsdPtr->eventReady = 0;
   316 
   317 #ifdef WEAK_IMPORT_SPINLOCKLOCK
   318     /*
   319      * Initialize support for weakly imported spinlock API.
   320      */
   321     if (pthread_once(&spinLockLockInitControl, SpinLockLockInit)) {
   322 	Tcl_Panic("Tcl_InitNotifier: pthread_once failed");
   323     }
   324 #endif
   325 
   326 #ifndef __CONSTANT_CFSTRINGS__
   327     if (!tclEventsOnlyRunLoopMode) {
   328 	tclEventsOnlyRunLoopMode = CFSTR(TCL_EVENTS_ONLY_RUN_LOOP_MODE);
   329     }
   330 #endif
   331 
   332     /*
   333      * Initialize CFRunLoopSource and add it to CFRunLoop of this thread.
   334      */
   335 
   336     if (!tsdPtr->runLoop) {
   337 	CFRunLoopRef runLoop = CFRunLoopGetCurrent();
   338 	CFRunLoopSourceRef runLoopSource;
   339 	CFRunLoopSourceContext runLoopSourceContext;
   340 
   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");
   346 	}
   347 	CFRunLoopAddSource(runLoop, runLoopSource, kCFRunLoopCommonModes);
   348 	CFRunLoopAddSource(runLoop, runLoopSource, tclEventsOnlyRunLoopMode);
   349 	tsdPtr->runLoopSource = runLoopSource;
   350 	tsdPtr->runLoop = runLoop;
   351     }
   352 
   353     LOCK_NOTIFIER_INIT;
   354 #ifdef HAVE_PTHREAD_ATFORK
   355     /*
   356      * Install pthread_atfork handlers to reinitialize the notifier in the
   357      * child of a fork.
   358      */
   359 
   360     if (
   361 #ifdef WEAK_IMPORT_PTHREAD_ATFORK
   362 	    pthread_atfork != NULL &&
   363 #endif
   364 	    !atForkInit) {
   365 	int result = pthread_atfork(AtForkPrepare, AtForkParent, AtForkChild);
   366 	if (result) {
   367 	    Tcl_Panic("Tcl_InitNotifier: pthread_atfork failed");
   368 	}
   369 	atForkInit = 1;
   370     }
   371 #endif
   372     if (notifierCount == 0) {
   373 	int fds[2], status;
   374 
   375 	/*
   376 	 * Initialize trigger pipe.
   377 	 */
   378 
   379 	if (pipe(fds) != 0) {
   380 	    Tcl_Panic("Tcl_InitNotifier: could not create trigger pipe");
   381 	}
   382 
   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");
   387 	}
   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");
   392 	}
   393 
   394 	receivePipe = fds[0];
   395 	triggerPipe = fds[1];
   396 
   397 	/*
   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).
   401 	 */
   402 
   403 	notifierThread = 0;
   404     }
   405     notifierCount++;
   406     UNLOCK_NOTIFIER_INIT;
   407 
   408     return (ClientData) tsdPtr;
   409 }
   410 
   411 /*
   412  *----------------------------------------------------------------------
   413  *
   414  * Tcl_FinalizeNotifier --
   415  *
   416  *	This function is called to cleanup the notifier state before a thread
   417  *	is terminated.
   418  *
   419  * Results:
   420  *	None.
   421  *
   422  * Side effects:
   423  *	May terminate the background notifier thread if this is the last
   424  *	notifier instance.
   425  *
   426  *----------------------------------------------------------------------
   427  */
   428 
   429 void
   430 Tcl_FinalizeNotifier(
   431     ClientData clientData)		/* Not used. */
   432 {
   433     ThreadSpecificData *tsdPtr = TCL_TSD_INIT(&dataKey);
   434 
   435     LOCK_NOTIFIER_INIT;
   436     notifierCount--;
   437 
   438     /*
   439      * If this is the last thread to use the notifier, close the notifier pipe
   440      * and wait for the background thread to terminate.
   441      */
   442 
   443     if (notifierCount == 0) {
   444 	int result;
   445 
   446 	if (triggerPipe < 0) {
   447 	    Tcl_Panic("Tcl_FinalizeNotifier: notifier pipe not initialized");
   448 	}
   449 
   450 	/*
   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]
   459 	 */
   460 
   461 	write(triggerPipe, "q", 1);
   462 	close(triggerPipe);
   463 
   464 	if (notifierThread) {
   465 	    result = pthread_join(notifierThread, NULL);
   466 	    if (result) {
   467 		Tcl_Panic("Tcl_FinalizeNotifier: unable to join notifier thread");
   468 	    }
   469 	    notifierThread = 0;
   470 	}
   471 
   472 	close(receivePipe);
   473 	triggerPipe = -1;
   474     }
   475     UNLOCK_NOTIFIER_INIT;
   476 
   477     LOCK_NOTIFIER;		/* for concurrency with Tcl_AlertNotifier */
   478     if (tsdPtr->runLoop) {
   479 	tsdPtr->runLoop = NULL;
   480 
   481 	/*
   482 	 * Remove runLoopSource from all CFRunLoops and release it.
   483 	 */
   484 
   485 	CFRunLoopSourceInvalidate(tsdPtr->runLoopSource);
   486 	CFRelease(tsdPtr->runLoopSource);
   487 	tsdPtr->runLoopSource = NULL;
   488     }
   489     UNLOCK_NOTIFIER;
   490 }
   491 
   492 /*
   493  *----------------------------------------------------------------------
   494  *
   495  * Tcl_AlertNotifier --
   496  *
   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.
   501  *
   502  * Results:
   503  *	None.
   504  *
   505  * Side effects:
   506  *	Signals the notifier condition variable for the specified notifier.
   507  *
   508  *----------------------------------------------------------------------
   509  */
   510 
   511 void
   512 Tcl_AlertNotifier(
   513     ClientData clientData)
   514 {
   515     ThreadSpecificData *tsdPtr = (ThreadSpecificData *) clientData;
   516 
   517     LOCK_NOTIFIER;
   518     if (tsdPtr->runLoop) {
   519 	tsdPtr->eventReady = 1;
   520 	CFRunLoopSourceSignal(tsdPtr->runLoopSource);
   521 	CFRunLoopWakeUp(tsdPtr->runLoop);
   522     }
   523     UNLOCK_NOTIFIER;
   524 }
   525 
   526 /*
   527  *----------------------------------------------------------------------
   528  *
   529  * Tcl_SetTimer --
   530  *
   531  *	This function sets the current notifier timer value. This interface is
   532  *	not implemented in this notifier because we are always running inside
   533  *	of Tcl_DoOneEvent.
   534  *
   535  * Results:
   536  *	None.
   537  *
   538  * Side effects:
   539  *	None.
   540  *
   541  *----------------------------------------------------------------------
   542  */
   543 
   544 void
   545 Tcl_SetTimer(
   546     Tcl_Time *timePtr)		/* Timeout value, may be NULL. */
   547 {
   548     /*
   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.
   552      */
   553 
   554     if (tclStubs.tcl_SetTimer != tclOriginalNotifier.setTimerProc) {
   555 	tclStubs.tcl_SetTimer(timePtr);
   556     }
   557 }
   558 
   559 /*
   560  *----------------------------------------------------------------------
   561  *
   562  * Tcl_ServiceModeHook --
   563  *
   564  *	This function is invoked whenever the service mode changes.
   565  *
   566  * Results:
   567  *	None.
   568  *
   569  * Side effects:
   570  *	None.
   571  *
   572  *----------------------------------------------------------------------
   573  */
   574 
   575 void
   576 Tcl_ServiceModeHook(
   577     int mode)			/* Either TCL_SERVICE_ALL, or
   578 				 * TCL_SERVICE_NONE. */
   579 {
   580 }
   581 
   582 /*
   583  *----------------------------------------------------------------------
   584  *
   585  * Tcl_CreateFileHandler --
   586  *
   587  *	This function registers a file handler with the select notifier.
   588  *
   589  * Results:
   590  *	None.
   591  *
   592  * Side effects:
   593  *	Creates a new file handler structure.
   594  *
   595  *----------------------------------------------------------------------
   596  */
   597 
   598 void
   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
   604 				 * called. */
   605     Tcl_FileProc *proc,		/* Function to call for each selected
   606 				 * event. */
   607     ClientData clientData)	/* Arbitrary data to pass to proc. */
   608 {
   609     ThreadSpecificData *tsdPtr = TCL_TSD_INIT(&dataKey);
   610     FileHandler *filePtr;
   611 
   612     if (tclStubs.tcl_CreateFileHandler !=
   613 	    tclOriginalNotifier.createFileHandlerProc) {
   614 	tclStubs.tcl_CreateFileHandler(fd, mask, proc, clientData);
   615 	return;
   616     }
   617 
   618     for (filePtr = tsdPtr->firstFileHandlerPtr; filePtr != NULL;
   619 	    filePtr = filePtr->nextPtr) {
   620 	if (filePtr->fd == fd) {
   621 	    break;
   622 	}
   623     }
   624     if (filePtr == NULL) {
   625 	filePtr = (FileHandler*) ckalloc(sizeof(FileHandler));
   626 	filePtr->fd = fd;
   627 	filePtr->readyMask = 0;
   628 	filePtr->nextPtr = tsdPtr->firstFileHandlerPtr;
   629 	tsdPtr->firstFileHandlerPtr = filePtr;
   630     }
   631     filePtr->proc = proc;
   632     filePtr->clientData = clientData;
   633     filePtr->mask = mask;
   634 
   635     /*
   636      * Update the check masks for this file.
   637      */
   638 
   639     if (mask & TCL_READABLE) {
   640 	FD_SET(fd, &(tsdPtr->checkMasks.readable));
   641     } else {
   642 	FD_CLR(fd, &(tsdPtr->checkMasks.readable));
   643     }
   644     if (mask & TCL_WRITABLE) {
   645 	FD_SET(fd, &(tsdPtr->checkMasks.writable));
   646     } else {
   647 	FD_CLR(fd, &(tsdPtr->checkMasks.writable));
   648     }
   649     if (mask & TCL_EXCEPTION) {
   650 	FD_SET(fd, &(tsdPtr->checkMasks.exceptional));
   651     } else {
   652 	FD_CLR(fd, &(tsdPtr->checkMasks.exceptional));
   653     }
   654     if (tsdPtr->numFdBits <= fd) {
   655 	tsdPtr->numFdBits = fd+1;
   656     }
   657 }
   658 
   659 /*
   660  *----------------------------------------------------------------------
   661  *
   662  * Tcl_DeleteFileHandler --
   663  *
   664  *	Cancel a previously-arranged callback arrangement for a file.
   665  *
   666  * Results:
   667  *	None.
   668  *
   669  * Side effects:
   670  *	If a callback was previously registered on file, remove it.
   671  *
   672  *----------------------------------------------------------------------
   673  */
   674 
   675 void
   676 Tcl_DeleteFileHandler(
   677     int fd)			/* Stream id for which to remove callback
   678 				 * function. */
   679 {
   680     FileHandler *filePtr, *prevPtr;
   681     int i;
   682     ThreadSpecificData *tsdPtr = TCL_TSD_INIT(&dataKey);
   683 
   684     if (tclStubs.tcl_DeleteFileHandler !=
   685 	    tclOriginalNotifier.deleteFileHandlerProc) {
   686 	tclStubs.tcl_DeleteFileHandler(fd);
   687 	return;
   688     }
   689 
   690     /*
   691      * Find the entry for the given file (and return if there isn't one).
   692      */
   693 
   694     for (prevPtr = NULL, filePtr = tsdPtr->firstFileHandlerPtr; ;
   695 	 prevPtr = filePtr, filePtr = filePtr->nextPtr) {
   696 	if (filePtr == NULL) {
   697 	    return;
   698 	}
   699 	if (filePtr->fd == fd) {
   700 	    break;
   701 	}
   702     }
   703 
   704     /*
   705      * Update the check masks for this file.
   706      */
   707 
   708     if (filePtr->mask & TCL_READABLE) {
   709 	FD_CLR(fd, &(tsdPtr->checkMasks.readable));
   710     }
   711     if (filePtr->mask & TCL_WRITABLE) {
   712 	FD_CLR(fd, &(tsdPtr->checkMasks.writable));
   713     }
   714     if (filePtr->mask & TCL_EXCEPTION) {
   715 	FD_CLR(fd, &(tsdPtr->checkMasks.exceptional));
   716     }
   717 
   718     /*
   719      * Find current max fd.
   720      */
   721 
   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;
   729 		break;
   730 	    }
   731 	}
   732     }
   733 
   734     /*
   735      * Clean up information in the callback record.
   736      */
   737 
   738     if (prevPtr == NULL) {
   739 	tsdPtr->firstFileHandlerPtr = filePtr->nextPtr;
   740     } else {
   741 	prevPtr->nextPtr = filePtr->nextPtr;
   742     }
   743     ckfree((char *) filePtr);
   744 }
   745 
   746 /*
   747  *----------------------------------------------------------------------
   748  *
   749  * FileHandlerEventProc --
   750  *
   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
   754  *	handler.
   755  *
   756  * Results:
   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.
   761  *
   762  * Side effects:
   763  *	Whatever the file handler's callback function does.
   764  *
   765  *----------------------------------------------------------------------
   766  */
   767 
   768 static int
   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. */
   773 {
   774     int mask;
   775     FileHandler *filePtr;
   776     FileHandlerEvent *fileEvPtr = (FileHandlerEvent *) evPtr;
   777     ThreadSpecificData *tsdPtr;
   778 
   779     if (!(flags & TCL_FILE_EVENTS)) {
   780 	return 0;
   781     }
   782 
   783     /*
   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.
   788      */
   789 
   790     tsdPtr = TCL_TSD_INIT(&dataKey);
   791     for (filePtr = tsdPtr->firstFileHandlerPtr; filePtr != NULL;
   792 	    filePtr = filePtr->nextPtr) {
   793 	if (filePtr->fd != fileEvPtr->fd) {
   794 	    continue;
   795 	}
   796 
   797 	/*
   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
   801 	 *    desired mask.
   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
   806 	 *    file.
   807 	 */
   808 
   809 	mask = filePtr->readyMask & filePtr->mask;
   810 	filePtr->readyMask = 0;
   811 	if (mask != 0) {
   812 	    (*filePtr->proc)(filePtr->clientData, mask);
   813 	}
   814 	break;
   815     }
   816     return 1;
   817 }
   818 
   819 /*
   820  *----------------------------------------------------------------------
   821  *
   822  * Tcl_WaitForEvent --
   823  *
   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.
   827  *
   828  * Results:
   829  *	Returns -1 if the select would block forever, otherwise returns 0.
   830  *
   831  * Side effects:
   832  *	Queues file events that are detected by the select.
   833  *
   834  *----------------------------------------------------------------------
   835  */
   836 
   837 int
   838 Tcl_WaitForEvent(
   839     Tcl_Time *timePtr)		/* Maximum block time, or NULL. */
   840 {
   841     FileHandler *filePtr;
   842     FileHandlerEvent *fileEvPtr;
   843     int mask;
   844     int waitForFiles;
   845     ThreadSpecificData *tsdPtr = TCL_TSD_INIT(&dataKey);
   846 
   847     if (tclStubs.tcl_WaitForEvent != tclOriginalNotifier.waitForEventProc) {
   848 	return tclStubs.tcl_WaitForEvent(timePtr);
   849     }
   850 
   851     /*
   852      * Start notifier thread if necessary.
   853      */
   854 
   855     LOCK_NOTIFIER_INIT;
   856     if (!notifierCount) {
   857         Tcl_Panic("Tcl_WaitForEvent: notifier not initialized");
   858     }
   859     if (!notifierThread) {
   860 	int result;
   861 	pthread_attr_t attr;
   862 
   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(&notifierThread, &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");
   872 	}
   873     }
   874     UNLOCK_NOTIFIER_INIT;
   875 
   876     /*
   877      * Place this thread on the list of interested threads, signal the
   878      * notifier thread, and wait for a response or a timeout.
   879      */
   880 
   881     LOCK_NOTIFIER;
   882     if (!tsdPtr->runLoop) {
   883         Tcl_Panic("Tcl_WaitForEvent: CFRunLoop not initialized");
   884     }
   885     waitForFiles = (tsdPtr->numFdBits > 0);
   886     if (timePtr != NULL && timePtr->sec == 0 && timePtr->usec == 0) {
   887 	/*
   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.
   893 	 */
   894 
   895 	waitForFiles = 1;
   896 	tsdPtr->pollState = POLL_WANT;
   897 	timePtr = NULL;
   898     } else {
   899 	tsdPtr->pollState = 0;
   900     }
   901 
   902     if (waitForFiles) {
   903 	/*
   904 	 * Add the ThreadSpecificData structure of this thread to the list of
   905 	 * ThreadSpecificData structures of all threads that are waiting on
   906 	 * file events.
   907 	 */
   908 
   909 	tsdPtr->nextPtr = waitingListPtr;
   910 	if (waitingListPtr) {
   911 	    waitingListPtr->prevPtr = tsdPtr;
   912 	}
   913 	tsdPtr->prevPtr = 0;
   914 	waitingListPtr = tsdPtr;
   915 	tsdPtr->onList = 1;
   916 
   917 	write(triggerPipe, "", 1);
   918     }
   919 
   920     FD_ZERO(&(tsdPtr->readyMasks.readable));
   921     FD_ZERO(&(tsdPtr->readyMasks.writable));
   922     FD_ZERO(&(tsdPtr->readyMasks.exceptional));
   923 
   924     if (!tsdPtr->eventReady) {
   925 	CFTimeInterval waitTime;
   926 	CFStringRef runLoopMode;
   927 
   928 	if (timePtr == NULL) {
   929 	    waitTime = 1.0e10; /* Wait forever, as per CFRunLoop.c */
   930 	} else {
   931 	    waitTime = timePtr->sec + 1.0e-6 * timePtr->usec;
   932 	}
   933 	/*
   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.
   938 	 */
   939 	if ((runLoopMode = CFRunLoopCopyCurrentMode(tsdPtr->runLoop))) {
   940 	    CFRelease(runLoopMode);
   941 	    runLoopMode = tclEventsOnlyRunLoopMode;
   942 	} else {
   943 	    runLoopMode = kCFRunLoopDefaultMode;
   944 	}
   945 	UNLOCK_NOTIFIER;
   946 	CFRunLoopRunInMode(runLoopMode, waitTime, TRUE);
   947 	LOCK_NOTIFIER;
   948     }
   949     tsdPtr->eventReady = 0;
   950 
   951     if (waitForFiles && tsdPtr->onList) {
   952 	/*
   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.
   957 	 */
   958 
   959 	if (tsdPtr->prevPtr) {
   960 	    tsdPtr->prevPtr->nextPtr = tsdPtr->nextPtr;
   961 	} else {
   962 	    waitingListPtr = tsdPtr->nextPtr;
   963 	}
   964 	if (tsdPtr->nextPtr) {
   965 	    tsdPtr->nextPtr->prevPtr = tsdPtr->prevPtr;
   966 	}
   967 	tsdPtr->nextPtr = tsdPtr->prevPtr = NULL;
   968 	tsdPtr->onList = 0;
   969 	write(triggerPipe, "", 1);
   970     }
   971 
   972     /*
   973      * Queue all detected file events before returning.
   974      */
   975 
   976     for (filePtr = tsdPtr->firstFileHandlerPtr; (filePtr != NULL);
   977 	    filePtr = filePtr->nextPtr) {
   978 
   979 	mask = 0;
   980 	if (FD_ISSET(filePtr->fd, &(tsdPtr->readyMasks.readable))) {
   981 	    mask |= TCL_READABLE;
   982 	}
   983 	if (FD_ISSET(filePtr->fd, &(tsdPtr->readyMasks.writable))) {
   984 	    mask |= TCL_WRITABLE;
   985 	}
   986 	if (FD_ISSET(filePtr->fd, &(tsdPtr->readyMasks.exceptional))) {
   987 	    mask |= TCL_EXCEPTION;
   988 	}
   989 
   990 	if (!mask) {
   991 	    continue;
   992 	}
   993 
   994 	/*
   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.
   997 	 */
   998 
   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);
  1004 	}
  1005 	filePtr->readyMask = mask;
  1006     }
  1007     UNLOCK_NOTIFIER;
  1008     return 0;
  1009 }
  1010 
  1011 /*
  1012  *----------------------------------------------------------------------
  1013  *
  1014  * NotifierThreadProc --
  1015  *
  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.
  1023  *
  1024  * Result:
  1025  *	None. Once started, this routine never exits. It dies with the overall
  1026  *	process.
  1027  *
  1028  * Side effects:
  1029  *	The trigger pipe used to signal the notifier thread is created when
  1030  *	the notifier thread first starts.
  1031  *
  1032  *----------------------------------------------------------------------
  1033  */
  1034 
  1035 static void
  1036 NotifierThreadProc(
  1037     ClientData clientData)	/* Not used. */
  1038 {
  1039     ThreadSpecificData *tsdPtr;
  1040     fd_set readableMask;
  1041     fd_set writableMask;
  1042     fd_set exceptionalMask;
  1043     int i, numFdBits = 0;
  1044     long found;
  1045     struct timeval poll = {0., 0.}, *timePtr;
  1046     char buf[2];
  1047 
  1048     /*
  1049      * Look for file events and report them to interested threads.
  1050      */
  1051 
  1052     while (1) {
  1053 	FD_ZERO(&readableMask);
  1054 	FD_ZERO(&writableMask);
  1055 	FD_ZERO(&exceptionalMask);
  1056 
  1057 	/*
  1058 	 * Compute the logical OR of the select masks from all the waiting
  1059 	 * notifiers.
  1060 	 */
  1061 
  1062 	LOCK_NOTIFIER;
  1063 	timePtr = NULL;
  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);
  1068 		}
  1069 		if (FD_ISSET(i, &(tsdPtr->checkMasks.writable))) {
  1070 		    FD_SET(i, &writableMask);
  1071 		}
  1072 		if (FD_ISSET(i, &(tsdPtr->checkMasks.exceptional))) {
  1073 		    FD_SET(i, &exceptionalMask);
  1074 		}
  1075 	    }
  1076 	    if (tsdPtr->numFdBits > numFdBits) {
  1077 		numFdBits = tsdPtr->numFdBits;
  1078 	    }
  1079 	    if (tsdPtr->pollState & POLL_WANT) {
  1080 		/*
  1081 		 * Here we make sure we go through select() with the same mask
  1082 		 * bits that were present when the thread tried to poll.
  1083 		 */
  1084 
  1085 		tsdPtr->pollState |= POLL_DONE;
  1086 		timePtr = &poll;
  1087 	    }
  1088 	}
  1089 	UNLOCK_NOTIFIER;
  1090 
  1091 	/*
  1092 	 * Set up the select mask to include the receive pipe.
  1093 	 */
  1094 
  1095 	if (receivePipe >= numFdBits) {
  1096 	    numFdBits = receivePipe + 1;
  1097 	}
  1098 	FD_SET(receivePipe, &readableMask);
  1099 
  1100 	if (select(numFdBits, &readableMask, &writableMask, &exceptionalMask,
  1101 		timePtr) == -1) {
  1102 	    /*
  1103 	     * Try again immediately on an error.
  1104 	     */
  1105 
  1106 	    continue;
  1107 	}
  1108 
  1109 	/*
  1110 	 * Alert any threads that are waiting on a ready file descriptor.
  1111 	 */
  1112 
  1113 	LOCK_NOTIFIER;
  1114 	for (tsdPtr = waitingListPtr; tsdPtr; tsdPtr = tsdPtr->nextPtr) {
  1115 	    found = 0;
  1116 
  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));
  1121 		    found = 1;
  1122 		}
  1123 		if (FD_ISSET(i, &(tsdPtr->checkMasks.writable))
  1124 			&& FD_ISSET(i, &writableMask)) {
  1125 		    FD_SET(i, &(tsdPtr->readyMasks.writable));
  1126 		    found = 1;
  1127 		}
  1128 		if (FD_ISSET(i, &(tsdPtr->checkMasks.exceptional))
  1129 			&& FD_ISSET(i, &exceptionalMask)) {
  1130 		    FD_SET(i, &(tsdPtr->readyMasks.exceptional));
  1131 		    found = 1;
  1132 		}
  1133 	    }
  1134 
  1135 	    if (found || (tsdPtr->pollState & POLL_DONE)) {
  1136 		tsdPtr->eventReady = 1;
  1137 		if (tsdPtr->onList) {
  1138 		    /*
  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.
  1143 		     */
  1144 
  1145 		    if (tsdPtr->prevPtr) {
  1146 			tsdPtr->prevPtr->nextPtr = tsdPtr->nextPtr;
  1147 		    } else {
  1148 			waitingListPtr = tsdPtr->nextPtr;
  1149 		    }
  1150 		    if (tsdPtr->nextPtr) {
  1151 			tsdPtr->nextPtr->prevPtr = tsdPtr->prevPtr;
  1152 		    }
  1153 		    tsdPtr->nextPtr = tsdPtr->prevPtr = NULL;
  1154 		    tsdPtr->onList = 0;
  1155 		    tsdPtr->pollState = 0;
  1156 		}
  1157 		if (tsdPtr->runLoop) {
  1158 		    CFRunLoopSourceSignal(tsdPtr->runLoopSource);
  1159 		    CFRunLoopWakeUp(tsdPtr->runLoop);
  1160 		}
  1161 	    }
  1162 	}
  1163 	UNLOCK_NOTIFIER;
  1164 
  1165 	/*
  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.
  1169 	 */
  1170 
  1171 	if (FD_ISSET(receivePipe, &readableMask)) {
  1172 	    i = read(receivePipe, buf, 1);
  1173 
  1174 	    if ((i == 0) || ((i == 1) && (buf[0] == 'q'))) {
  1175 		/*
  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.
  1179 		 */
  1180 
  1181 		break;
  1182 	    }
  1183 	}
  1184     }
  1185     pthread_exit(0);
  1186 }
  1187 
  1188 #ifdef HAVE_PTHREAD_ATFORK
  1189 /*
  1190  *----------------------------------------------------------------------
  1191  *
  1192  * AtForkPrepare --
  1193  *
  1194  *	Lock the notifier in preparation for a fork.
  1195  *
  1196  * Results:
  1197  *	None.
  1198  *
  1199  * Side effects:
  1200  *	None.
  1201  *
  1202  *----------------------------------------------------------------------
  1203  */
  1204 
  1205 static void
  1206 AtForkPrepare(void)
  1207 {
  1208     LOCK_NOTIFIER_INIT;
  1209     LOCK_NOTIFIER;
  1210 }
  1211 
  1212 /*
  1213  *----------------------------------------------------------------------
  1214  *
  1215  * AtForkParent --
  1216  *
  1217  *	Unlock the notifier in the parent after a fork.
  1218  *
  1219  * Results:
  1220  *	None.
  1221  *
  1222  * Side effects:
  1223  *	None.
  1224  *
  1225  *----------------------------------------------------------------------
  1226  */
  1227 
  1228 static void
  1229 AtForkParent(void)
  1230 {
  1231     UNLOCK_NOTIFIER;
  1232     UNLOCK_NOTIFIER_INIT;
  1233 }
  1234 
  1235 /*
  1236  *----------------------------------------------------------------------
  1237  *
  1238  * AtForkChild --
  1239  *
  1240  *	Unlock and reinstall the notifier in the child after a fork.
  1241  *
  1242  * Results:
  1243  *	None.
  1244  *
  1245  * Side effects:
  1246  *	None.
  1247  *
  1248  *----------------------------------------------------------------------
  1249  */
  1250 
  1251 static void
  1252 AtForkChild(void)
  1253 {
  1254     ThreadSpecificData *tsdPtr = TCL_TSD_INIT(&dataKey);
  1255 
  1256     UNLOCK_NOTIFIER;
  1257     UNLOCK_NOTIFIER_INIT;
  1258     if (tsdPtr->runLoop) {
  1259 	tsdPtr->runLoop = NULL;
  1260 	CFRunLoopSourceInvalidate(tsdPtr->runLoopSource);
  1261 	CFRelease(tsdPtr->runLoopSource);
  1262 	tsdPtr->runLoopSource = NULL;
  1263     }
  1264     if (notifierCount > 0) {
  1265 	notifierCount = 0;
  1266 
  1267 	/*
  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.
  1274 	 */
  1275 
  1276 	Tcl_InitNotifier();
  1277     }
  1278 }
  1279 #endif /* HAVE_PTHREAD_ATFORK */
  1280 
  1281 #endif /* HAVE_COREFOUNDATION */