tclMacOSXNotify.c
上传用户:rrhhcc
上传日期:2015-12-11
资源大小:54129k
文件大小:35k
源码类别:

通讯编程

开发平台:

Visual C++

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