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

通讯编程

开发平台:

Visual C++

  1. /*
  2.  * tclUnixNotify.c --
  3.  *
  4.  * This file contains the implementation of the select-based
  5.  * Unix-specific notifier, which is the lowest-level part of the
  6.  * Tcl event loop.  This file works together with
  7.  * ../generic/tclNotify.c.
  8.  *
  9.  * Copyright (c) 1995-1997 Sun Microsystems, Inc.
  10.  *
  11.  * See the file "license.terms" for information on usage and redistribution
  12.  * of this file, and for a DISCLAIMER OF ALL WARRANTIES.
  13.  *
  14.  * RCS: @(#) $Id: tclUnixNotfy.c,v 1.11.2.16 2006/08/22 17:45:02 andreas_kupries Exp $
  15.  */
  16. #include "tclInt.h"
  17. #include "tclPort.h"
  18. #ifndef HAVE_COREFOUNDATION /* Darwin/Mac OS X CoreFoundation notifier
  19.                              * is in tclMacOSXNotify.c */
  20. #include <signal.h> 
  21. extern TclStubs tclStubs;
  22. extern Tcl_NotifierProcs tclOriginalNotifier;
  23. /*
  24.  * This structure is used to keep track of the notifier info for a 
  25.  * a registered file.
  26.  */
  27. typedef struct FileHandler {
  28.     int fd;
  29.     int mask; /* Mask of desired events: TCL_READABLE,
  30.  * etc. */
  31.     int readyMask; /* Mask of events that have been seen since the
  32.  * last time file handlers were invoked for
  33.  * this file. */
  34.     Tcl_FileProc *proc; /* Procedure to call, in the style of
  35.  * Tcl_CreateFileHandler. */
  36.     ClientData clientData; /* Argument to pass to proc. */
  37.     struct FileHandler *nextPtr;/* Next in list of all files we care about. */
  38. } FileHandler;
  39. /*
  40.  * The following structure is what is added to the Tcl event queue when
  41.  * file handlers are ready to fire.
  42.  */
  43. typedef struct FileHandlerEvent {
  44.     Tcl_Event header; /* Information that is standard for
  45.  * all events. */
  46.     int fd; /* File descriptor that is ready.  Used
  47.  * to find the FileHandler structure for
  48.  * the file (can't point directly to the
  49.  * FileHandler structure because it could
  50.  * go away while the event is queued). */
  51. } FileHandlerEvent;
  52. /*
  53.  *
  54.  * The following structure contains a set of select() masks to track
  55.  * readable, writable, and exceptional conditions.
  56.  */
  57. typedef struct SelectMasks {
  58.     fd_set readable;
  59.     fd_set writable;
  60.     fd_set exceptional;
  61. } SelectMasks;
  62. /*
  63.  * The following static structure contains the state information for the
  64.  * select based implementation of the Tcl notifier.  One of these structures
  65.  * is created for each thread that is using the notifier.  
  66.  */
  67. typedef struct ThreadSpecificData {
  68.     FileHandler *firstFileHandlerPtr;
  69. /* Pointer to head of file handler list. */
  70.     
  71.     SelectMasks checkMasks; /* This structure is used to build up the masks
  72.  * to be used in the next call to select.
  73.  * Bits are set in response to calls to
  74.  * 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
  79.  * (one more than highest fd for which
  80.  * Tcl_WatchFile has been called). */
  81. #ifdef TCL_THREADS
  82.     int onList; /* True if it is in this list */
  83.     unsigned int pollState; /* pollState is used to implement a polling 
  84.  * handshake between each thread and the
  85.  * notifier thread. Bits defined below. */
  86.     struct ThreadSpecificData *nextPtr, *prevPtr;
  87.                                 /* All threads that are currently waiting on 
  88.                                  * an event have their ThreadSpecificData
  89.                                  * structure on a doubly-linked listed formed
  90.                                  * from these pointers.  You must hold the
  91.                                  * notifierMutex lock before accessing these
  92.                                  * fields. */
  93.     Tcl_Condition waitCV;     /* Any other thread alerts a notifier
  94.  * that an event is ready to be processed
  95.  * by signaling this condition variable. */
  96.     int eventReady;           /* True if an event is ready to be processed.
  97.                                * Used as condition flag together with
  98.                                * waitCV above. */
  99. #endif
  100. } ThreadSpecificData;
  101. static Tcl_ThreadDataKey dataKey;
  102. #ifdef TCL_THREADS
  103. /*
  104.  * The following static indicates the number of threads that have
  105.  * initialized notifiers.
  106.  *
  107.  * You must hold the notifierMutex lock 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.  * of ThreadSpecificData structures for all threads that are currently
  113.  * waiting on an event.
  114.  *
  115.  * You must hold the notifierMutex lock before accessing this list.
  116.  */
  117. static ThreadSpecificData *waitingListPtr = NULL;
  118. /*
  119.  * The notifier thread spends all its time in select() waiting for a
  120.  * file descriptor associated with one of the threads on the waitingListPtr
  121.  * list to do something interesting.  But if the contents of the
  122.  * waitingListPtr list ever changes, we need to wake up and restart
  123.  * the select() system call.  You can wake up the notifier thread by
  124.  * writing a single byte to the file descriptor defined below.  This
  125.  * file descriptor is the input-end of a pipe and the notifier thread is
  126.  * listening for data on the output-end of the same pipe.  Hence writing
  127.  * to this file descriptor will cause the select() system call to return
  128.  * and wake up the notifier thread.
  129.  *
  130.  * You must hold the notifierMutex lock before accessing this list.
  131.  */
  132. static int triggerPipe = -1;
  133. /*
  134.  * The notifierMutex locks access to all of the global notifier state. 
  135.  */
  136. TCL_DECLARE_MUTEX(notifierMutex)
  137. /*
  138.  * The notifier thread signals the notifierCV when it has finished
  139.  * initializing the triggerPipe and right before the notifier
  140.  * thread terminates.
  141.  */
  142. static Tcl_Condition notifierCV;
  143. /*
  144.  * The pollState bits
  145.  * POLL_WANT is set by each thread before it waits on its condition
  146.  * variable.  It is checked by the notifier before it does
  147.  * select.
  148.  * POLL_DONE is set by the notifier if it goes into select after
  149.  * seeing POLL_WANT.  The idea is to ensure it tries a select
  150.  * with the same bits the initial thread had set.
  151.  */
  152. #define POLL_WANT 0x1
  153. #define POLL_DONE 0x2
  154. /*
  155.  * This is the thread ID of the notifier thread that does select.
  156.  */
  157. static Tcl_ThreadId notifierThread;
  158. #endif
  159. /*
  160.  * Static routines defined in this file.
  161.  */
  162. #ifdef TCL_THREADS
  163. static void NotifierThreadProc _ANSI_ARGS_((ClientData clientData));
  164. #endif
  165. static int FileHandlerEventProc _ANSI_ARGS_((Tcl_Event *evPtr,
  166.     int flags));
  167. /*
  168.  *----------------------------------------------------------------------
  169.  *
  170.  * Tcl_InitNotifier --
  171.  *
  172.  * Initializes the platform specific notifier state.
  173.  *
  174.  * Results:
  175.  * Returns a handle to the notifier state for this thread..
  176.  *
  177.  * Side effects:
  178.  * None.
  179.  *
  180.  *----------------------------------------------------------------------
  181.  */
  182. ClientData
  183. Tcl_InitNotifier()
  184. {
  185.     ThreadSpecificData *tsdPtr = TCL_TSD_INIT(&dataKey);
  186. #ifdef TCL_THREADS
  187.     tsdPtr->eventReady = 0;
  188.     /*
  189.      * Start the Notifier thread if necessary.
  190.      */
  191.     Tcl_MutexLock(&notifierMutex);
  192.     if (notifierCount == 0) {
  193. if (TclpThreadCreate(&notifierThread, NotifierThreadProc, NULL,
  194.      TCL_THREAD_STACK_DEFAULT, TCL_THREAD_JOINABLE) != TCL_OK) {
  195.     panic("Tcl_InitNotifier: unable to start notifier thread");
  196. }
  197.     }
  198.     notifierCount++;
  199.     /*
  200.      * Wait for the notifier pipe to be created.
  201.      */
  202.     while (triggerPipe < 0) {
  203. Tcl_ConditionWait(&notifierCV, &notifierMutex, NULL);
  204.     }
  205.     Tcl_MutexUnlock(&notifierMutex);
  206. #endif
  207.     return (ClientData) tsdPtr;
  208. }
  209. /*
  210.  *----------------------------------------------------------------------
  211.  *
  212.  * Tcl_FinalizeNotifier --
  213.  *
  214.  * This function is called to cleanup the notifier state before
  215.  * a thread is terminated.
  216.  *
  217.  * Results:
  218.  * None.
  219.  *
  220.  * Side effects:
  221.  * May terminate the background notifier thread if this is the
  222.  * last notifier instance.
  223.  *
  224.  *----------------------------------------------------------------------
  225.  */
  226. void
  227. Tcl_FinalizeNotifier(clientData)
  228.     ClientData clientData; /* Not used. */
  229. {
  230. #ifdef TCL_THREADS
  231.     ThreadSpecificData *tsdPtr = TCL_TSD_INIT(&dataKey);
  232.     Tcl_MutexLock(&notifierMutex);
  233.     notifierCount--;
  234.     /*
  235.      * If this is the last thread to use the notifier, close the notifier
  236.      * pipe and wait for the background thread to terminate.
  237.      */
  238.     if (notifierCount == 0) {
  239. int result;
  240. if (triggerPipe < 0) {
  241.     panic("Tcl_FinalizeNotifier: notifier pipe not initialized");
  242. }
  243. /*
  244.  * Send "q" message to the notifier thread so that it will
  245.  * terminate.  The notifier will return from its call to select()
  246.  * and notice that a "q" message has arrived, it will then close
  247.  * its side of the pipe and terminate its thread.  Note the we can
  248.  * not just close the pipe and check for EOF in the notifier
  249.  * thread because if a background child process was created with
  250.  * exec, select() would not register the EOF on the pipe until the
  251.  * child processes had terminated. [Bug: 4139] [Bug: 1222872]
  252.  */
  253. write(triggerPipe, "q", 1);
  254. close(triggerPipe);
  255. while(triggerPipe >= 0) {
  256.     Tcl_ConditionWait(&notifierCV, &notifierMutex, NULL);
  257. }
  258. result = Tcl_JoinThread(notifierThread, NULL);
  259. if (result) {
  260.     Tcl_Panic("Tcl_FinalizeNotifier: unable to join notifier thread");
  261. }
  262.     }
  263.     /*
  264.      * Clean up any synchronization objects in the thread local storage.
  265.      */
  266.     Tcl_ConditionFinalize(&(tsdPtr->waitCV));
  267.     Tcl_MutexUnlock(&notifierMutex);
  268. #endif
  269. }
  270. /*
  271.  *----------------------------------------------------------------------
  272.  *
  273.  * Tcl_AlertNotifier --
  274.  *
  275.  * Wake up the specified notifier from any thread. This routine
  276.  * is called by the platform independent notifier code whenever
  277.  * the Tcl_ThreadAlert routine is called.  This routine is
  278.  * guaranteed not to be called on a given notifier after
  279.  * Tcl_FinalizeNotifier is called for that notifier.
  280.  *
  281.  * Results:
  282.  * None.
  283.  *
  284.  * Side effects:
  285.  * Signals the notifier condition variable for the specified
  286.  * notifier.
  287.  *
  288.  *----------------------------------------------------------------------
  289.  */
  290. void
  291. Tcl_AlertNotifier(clientData)
  292.     ClientData clientData;
  293. {
  294. #ifdef TCL_THREADS
  295.     ThreadSpecificData *tsdPtr = (ThreadSpecificData *) clientData;
  296.     Tcl_MutexLock(&notifierMutex);
  297.     tsdPtr->eventReady = 1;
  298.     Tcl_ConditionNotify(&tsdPtr->waitCV);
  299.     Tcl_MutexUnlock(&notifierMutex);
  300. #endif
  301. }
  302. /*
  303.  *----------------------------------------------------------------------
  304.  *
  305.  * Tcl_SetTimer --
  306.  *
  307.  * This procedure sets the current notifier timer value.  This
  308.  * interface is not implemented in this notifier because we are
  309.  * always running inside of Tcl_DoOneEvent.
  310.  *
  311.  * Results:
  312.  * None.
  313.  *
  314.  * Side effects:
  315.  * None.
  316.  *
  317.  *----------------------------------------------------------------------
  318.  */
  319. void
  320. Tcl_SetTimer(timePtr)
  321.     Tcl_Time *timePtr; /* Timeout value, may be NULL. */
  322. {
  323.     /*
  324.      * The interval timer doesn't do anything in this implementation,
  325.      * because the only event loop is via Tcl_DoOneEvent, which passes
  326.      * timeout values to Tcl_WaitForEvent.
  327.      */
  328.     if (tclStubs.tcl_SetTimer != tclOriginalNotifier.setTimerProc) {
  329. tclStubs.tcl_SetTimer(timePtr);
  330.     }
  331. }
  332. /*
  333.  *----------------------------------------------------------------------
  334.  *
  335.  * Tcl_ServiceModeHook --
  336.  *
  337.  * This function is invoked whenever the service mode changes.
  338.  *
  339.  * Results:
  340.  * None.
  341.  *
  342.  * Side effects:
  343.  * None.
  344.  *
  345.  *----------------------------------------------------------------------
  346.  */
  347. void
  348. Tcl_ServiceModeHook(mode)
  349.     int mode; /* Either TCL_SERVICE_ALL, or
  350.  * TCL_SERVICE_NONE. */
  351. {
  352. }
  353. /*
  354.  *----------------------------------------------------------------------
  355.  *
  356.  * Tcl_CreateFileHandler --
  357.  *
  358.  * This procedure registers a file handler with the select notifier.
  359.  *
  360.  * Results:
  361.  * None.
  362.  *
  363.  * Side effects:
  364.  * Creates a new file handler structure.
  365.  *
  366.  *----------------------------------------------------------------------
  367.  */
  368. void
  369. Tcl_CreateFileHandler(fd, mask, proc, clientData)
  370.     int fd; /* Handle of stream to watch. */
  371.     int mask; /* OR'ed combination of TCL_READABLE,
  372.  * TCL_WRITABLE, and TCL_EXCEPTION:
  373.  * indicates conditions under which
  374.  * proc should be called. */
  375.     Tcl_FileProc *proc; /* Procedure to call for each
  376.  * selected event. */
  377.     ClientData clientData; /* Arbitrary data to pass to proc. */
  378. {
  379.     ThreadSpecificData *tsdPtr = TCL_TSD_INIT(&dataKey);
  380.     FileHandler *filePtr;
  381.     if (tclStubs.tcl_CreateFileHandler != tclOriginalNotifier.createFileHandlerProc) {
  382. tclStubs.tcl_CreateFileHandler(fd, mask, proc, clientData);
  383. return;
  384.     }
  385.     for (filePtr = tsdPtr->firstFileHandlerPtr; filePtr != NULL;
  386.  filePtr = filePtr->nextPtr) {
  387. if (filePtr->fd == fd) {
  388.     break;
  389. }
  390.     }
  391.     if (filePtr == NULL) {
  392. filePtr = (FileHandler*) ckalloc(sizeof(FileHandler));
  393. filePtr->fd = fd;
  394. filePtr->readyMask = 0;
  395. filePtr->nextPtr = tsdPtr->firstFileHandlerPtr;
  396. tsdPtr->firstFileHandlerPtr = filePtr;
  397.     }
  398.     filePtr->proc = proc;
  399.     filePtr->clientData = clientData;
  400.     filePtr->mask = mask;
  401.     /*
  402.      * Update the check masks for this file.
  403.      */
  404.     if ( mask & TCL_READABLE ) {
  405. FD_SET( fd, &(tsdPtr->checkMasks.readable) );
  406.     } else {
  407. FD_CLR( fd, &(tsdPtr->checkMasks.readable) );
  408.     }
  409.     if ( mask & TCL_WRITABLE ) {
  410. FD_SET( fd, &(tsdPtr->checkMasks.writable) );
  411.     } else {
  412. FD_CLR( fd, &(tsdPtr->checkMasks.writable) );
  413.     }
  414.     if ( mask & TCL_EXCEPTION ) {
  415. FD_SET( fd, &(tsdPtr->checkMasks.exceptional) );
  416.     } else {
  417. FD_CLR( fd, &(tsdPtr->checkMasks.exceptional) );
  418.     }
  419.     if (tsdPtr->numFdBits <= fd) {
  420. tsdPtr->numFdBits = fd+1;
  421.     }
  422. }
  423. /*
  424.  *----------------------------------------------------------------------
  425.  *
  426.  * Tcl_DeleteFileHandler --
  427.  *
  428.  * Cancel a previously-arranged callback arrangement for
  429.  * a file.
  430.  *
  431.  * Results:
  432.  * None.
  433.  *
  434.  * Side effects:
  435.  * If a callback was previously registered on file, remove it.
  436.  *
  437.  *----------------------------------------------------------------------
  438.  */
  439. void
  440. Tcl_DeleteFileHandler(fd)
  441.     int fd; /* Stream id for which to remove callback procedure. */
  442. {
  443.     FileHandler *filePtr, *prevPtr;
  444.     int i;
  445.     ThreadSpecificData *tsdPtr = TCL_TSD_INIT(&dataKey);
  446.     if (tclStubs.tcl_DeleteFileHandler != tclOriginalNotifier.deleteFileHandlerProc) {
  447. tclStubs.tcl_DeleteFileHandler(fd);
  448. return;
  449.     }
  450.     /*
  451.      * Find the entry for the given file (and return if there isn't one).
  452.      */
  453.     for (prevPtr = NULL, filePtr = tsdPtr->firstFileHandlerPtr; ;
  454.  prevPtr = filePtr, filePtr = filePtr->nextPtr) {
  455. if (filePtr == NULL) {
  456.     return;
  457. }
  458. if (filePtr->fd == fd) {
  459.     break;
  460. }
  461.     }
  462.     /*
  463.      * Update the check masks for this file.
  464.      */
  465.     if (filePtr->mask & TCL_READABLE) {
  466. FD_CLR( fd, &(tsdPtr->checkMasks.readable) );
  467.     }
  468.     if (filePtr->mask & TCL_WRITABLE) {
  469. FD_CLR( fd, &(tsdPtr->checkMasks.writable) );
  470.     }
  471.     if (filePtr->mask & TCL_EXCEPTION) {
  472. FD_CLR( fd, &(tsdPtr->checkMasks.exceptional) );
  473.     }
  474.     /*
  475.      * Find current max fd.
  476.      */
  477.     if (fd+1 == tsdPtr->numFdBits) {
  478. tsdPtr->numFdBits = 0;
  479. for (i = fd-1; i >= 0; i--) {
  480.     if ( FD_ISSET( i, &(tsdPtr->checkMasks.readable) )
  481.  || FD_ISSET( i, &(tsdPtr->checkMasks.writable) )
  482.  || FD_ISSET( i, &(tsdPtr->checkMasks.exceptional ) ) ) {
  483. tsdPtr->numFdBits = i+1;
  484. break;
  485.     }
  486. }
  487.     }
  488.     /*
  489.      * Clean up information in the callback record.
  490.      */
  491.     if (prevPtr == NULL) {
  492. tsdPtr->firstFileHandlerPtr = filePtr->nextPtr;
  493.     } else {
  494. prevPtr->nextPtr = filePtr->nextPtr;
  495.     }
  496.     ckfree((char *) filePtr);
  497. }
  498. /*
  499.  *----------------------------------------------------------------------
  500.  *
  501.  * FileHandlerEventProc --
  502.  *
  503.  * This procedure is called by Tcl_ServiceEvent when a file event
  504.  * reaches the front of the event queue.  This procedure is
  505.  * responsible for actually handling the event by invoking the
  506.  * callback for the file handler.
  507.  *
  508.  * Results:
  509.  * Returns 1 if the event was handled, meaning it should be removed
  510.  * from the queue.  Returns 0 if the event was not handled, meaning
  511.  * it should stay on the queue.  The only time the event isn't
  512.  * handled is if the TCL_FILE_EVENTS flag bit isn't set.
  513.  *
  514.  * Side effects:
  515.  * Whatever the file handler's callback procedure does.
  516.  *
  517.  *----------------------------------------------------------------------
  518.  */
  519. static int
  520. FileHandlerEventProc(evPtr, flags)
  521.     Tcl_Event *evPtr; /* Event to service. */
  522.     int flags; /* Flags that indicate what events to
  523.  * handle, such as TCL_FILE_EVENTS. */
  524. {
  525.     int mask;
  526.     FileHandler *filePtr;
  527.     FileHandlerEvent *fileEvPtr = (FileHandlerEvent *) evPtr;
  528.     ThreadSpecificData *tsdPtr;
  529.     if (!(flags & TCL_FILE_EVENTS)) {
  530. return 0;
  531.     }
  532.     /*
  533.      * Search through the file handlers to find the one whose handle matches
  534.      * the event.  We do this rather than keeping a pointer to the file
  535.      * handler directly in the event, so that the handler can be deleted
  536.      * while the event is queued without leaving a dangling pointer.
  537.      */
  538.     tsdPtr = TCL_TSD_INIT(&dataKey);
  539.     for (filePtr = tsdPtr->firstFileHandlerPtr; filePtr != NULL;
  540.  filePtr = filePtr->nextPtr) {
  541. if (filePtr->fd != fileEvPtr->fd) {
  542.     continue;
  543. }
  544. /*
  545.  * The code is tricky for two reasons:
  546.  * 1. The file handler's desired events could have changed
  547.  *    since the time when the event was queued, so AND the
  548.  *    ready mask with the desired mask.
  549.  * 2. The file could have been closed and re-opened since
  550.  *    the time when the event was queued.  This is why the
  551.  *    ready mask is stored in the file handler rather than
  552.  *    the queued event:  it will be zeroed when a new
  553.  *    file handler is created for the newly opened file.
  554.  */
  555. mask = filePtr->readyMask & filePtr->mask;
  556. filePtr->readyMask = 0;
  557. if (mask != 0) {
  558.     (*filePtr->proc)(filePtr->clientData, mask);
  559. }
  560. break;
  561.     }
  562.     return 1;
  563. }
  564. /*
  565.  *----------------------------------------------------------------------
  566.  *
  567.  * Tcl_WaitForEvent --
  568.  *
  569.  * This function is called by Tcl_DoOneEvent to wait for new
  570.  * events on the message queue.  If the block time is 0, then
  571.  * Tcl_WaitForEvent just polls without blocking.
  572.  *
  573.  * Results:
  574.  * Returns -1 if the select would block forever, otherwise
  575.  * returns 0.
  576.  *
  577.  * Side effects:
  578.  * Queues file events that are detected by the select.
  579.  *
  580.  *----------------------------------------------------------------------
  581.  */
  582. int
  583. Tcl_WaitForEvent(timePtr)
  584.     Tcl_Time *timePtr; /* Maximum block time, or NULL. */
  585. {
  586.     FileHandler *filePtr;
  587.     FileHandlerEvent *fileEvPtr;
  588.     int mask;
  589. #ifdef TCL_THREADS
  590.     int waitForFiles;
  591. #else
  592.     /* Impl. notes: timeout & timeoutPtr are used if, and only if
  593.      * threads are not enabled. They are the arguments for the regular
  594.      * select() used when the core is not thread-enabled. */
  595.     struct timeval timeout, *timeoutPtr;
  596.     int numFound;
  597. #endif
  598.     ThreadSpecificData *tsdPtr = TCL_TSD_INIT(&dataKey);
  599.     if (tclStubs.tcl_WaitForEvent != tclOriginalNotifier.waitForEventProc) {
  600. return tclStubs.tcl_WaitForEvent(timePtr);
  601.     }
  602. #ifndef TCL_THREADS
  603.     /*
  604.      * Set up the timeout structure.  Note that if there are no events to
  605.      * check for, we return with a negative result rather than blocking
  606.      * forever.
  607.      */
  608.     if (timePtr) {
  609. timeout.tv_sec = timePtr->sec;
  610. timeout.tv_usec = timePtr->usec;
  611. timeoutPtr = &timeout;
  612.     } else if (tsdPtr->numFdBits == 0) {
  613. /*
  614.  * If there are no threads, no timeout, and no fds registered,
  615.  * then there are no events possible and we must avoid deadlock.
  616.  * Note that this is not entirely correct because there might
  617.  * be a signal that could interrupt the select call, but we
  618.  * don't handle that case if we aren't using threads.
  619.  */
  620. return -1;
  621.     } else {
  622. timeoutPtr = NULL;
  623.     }
  624. #endif
  625. #ifdef TCL_THREADS
  626.     /*
  627.      * Place this thread on the list of interested threads, signal the
  628.      * notifier thread, and wait for a response or a timeout.
  629.      */
  630.     Tcl_MutexLock(&notifierMutex);
  631.     waitForFiles = (tsdPtr->numFdBits > 0);
  632.     if (timePtr != NULL && timePtr->sec == 0 && (timePtr->usec == 0
  633. #if defined(__APPLE__) && defined(__LP64__)
  634.     /*
  635.      * On 64-bit Darwin, pthread_cond_timedwait() appears to have a bug
  636.      * that causes it to wait forever when passed an absolute time which
  637.      * has already been exceeded by the system time; as a workaround,
  638.      * when given a very brief timeout, just do a poll. [Bug 1457797]
  639.      */
  640.     || timePtr->usec < 10
  641. #endif
  642.     )) {
  643. /*
  644.  * Cannot emulate a polling select with a polling condition variable.
  645.  * Instead, pretend to wait for files and tell the notifier
  646.  * thread what we are doing.  The notifier thread makes sure
  647.  * it goes through select with its select mask in the same state
  648.  * as ours currently is.  We block until that happens.
  649.  */
  650. waitForFiles = 1;
  651. tsdPtr->pollState = POLL_WANT;
  652. timePtr = NULL;
  653.     } else {
  654. tsdPtr->pollState = 0;
  655.     }
  656.     if (waitForFiles) {
  657.         /*
  658.          * Add the ThreadSpecificData structure of this thread to the list
  659.          * of ThreadSpecificData structures of all threads that are waiting
  660.          * on file events.
  661.          */
  662.         tsdPtr->nextPtr = waitingListPtr;
  663.         if (waitingListPtr) {
  664.             waitingListPtr->prevPtr = tsdPtr;
  665.         }
  666.         tsdPtr->prevPtr = 0;
  667.         waitingListPtr = tsdPtr;
  668. tsdPtr->onList = 1;
  669. write(triggerPipe, "", 1);
  670.     }
  671.     FD_ZERO( &(tsdPtr->readyMasks.readable) );
  672.     FD_ZERO( &(tsdPtr->readyMasks.writable) );
  673.     FD_ZERO( &(tsdPtr->readyMasks.exceptional) );
  674.     if (!tsdPtr->eventReady) {
  675.         Tcl_ConditionWait(&tsdPtr->waitCV, &notifierMutex, timePtr);
  676.     }
  677.     tsdPtr->eventReady = 0;
  678.     if (waitForFiles && tsdPtr->onList) {
  679. /*
  680.  * Remove the ThreadSpecificData structure of this thread from the
  681.  * waiting list.  Alert the notifier thread to recompute its select
  682.  * masks - skipping this caused a hang when trying to close a pipe
  683.  * which the notifier thread was still doing a select on.
  684.  */
  685.         if (tsdPtr->prevPtr) {
  686.             tsdPtr->prevPtr->nextPtr = tsdPtr->nextPtr;
  687.         } else {
  688.             waitingListPtr = tsdPtr->nextPtr;
  689.         }
  690.         if (tsdPtr->nextPtr) {
  691.             tsdPtr->nextPtr->prevPtr = tsdPtr->prevPtr;
  692.         }
  693.         tsdPtr->nextPtr = tsdPtr->prevPtr = NULL;
  694. tsdPtr->onList = 0;
  695. write(triggerPipe, "", 1);
  696.     }
  697.     
  698. #else
  699.     tsdPtr->readyMasks = tsdPtr->checkMasks;
  700.     numFound = select( tsdPtr->numFdBits,
  701.        &(tsdPtr->readyMasks.readable),
  702.        &(tsdPtr->readyMasks.writable),
  703.        &(tsdPtr->readyMasks.exceptional),
  704.        timeoutPtr );
  705.     /*
  706.      * Some systems don't clear the masks after an error, so
  707.      * we have to do it here.
  708.      */
  709.     if (numFound == -1) {
  710. FD_ZERO( &(tsdPtr->readyMasks.readable ) );
  711. FD_ZERO( &(tsdPtr->readyMasks.writable ) );
  712. FD_ZERO( &(tsdPtr->readyMasks.exceptional ) );
  713.     }
  714. #endif
  715.     /*
  716.      * Queue all detected file events before returning.
  717.      */
  718.     for (filePtr = tsdPtr->firstFileHandlerPtr; (filePtr != NULL);
  719.  filePtr = filePtr->nextPtr) {
  720. mask = 0;
  721. if ( FD_ISSET( filePtr->fd, &(tsdPtr->readyMasks.readable) ) ) {
  722.     mask |= TCL_READABLE;
  723. }
  724. if ( FD_ISSET( filePtr->fd, &(tsdPtr->readyMasks.writable) ) ) {
  725.     mask |= TCL_WRITABLE;
  726. }
  727. if ( FD_ISSET( filePtr->fd, &(tsdPtr->readyMasks.exceptional) ) ) {
  728.     mask |= TCL_EXCEPTION;
  729. }
  730. if (!mask) {
  731.     continue;
  732. }
  733. /*
  734.  * Don't bother to queue an event if the mask was previously
  735.  * non-zero since an event must still be on the queue.
  736.  */
  737. if (filePtr->readyMask == 0) {
  738.     fileEvPtr = (FileHandlerEvent *) ckalloc(
  739. sizeof(FileHandlerEvent));
  740.     fileEvPtr->header.proc = FileHandlerEventProc;
  741.     fileEvPtr->fd = filePtr->fd;
  742.     Tcl_QueueEvent((Tcl_Event *) fileEvPtr, TCL_QUEUE_TAIL);
  743. }
  744. filePtr->readyMask = mask;
  745.     }
  746. #ifdef TCL_THREADS
  747.     Tcl_MutexUnlock(&notifierMutex);
  748. #endif
  749.     return 0;
  750. }
  751. #ifdef TCL_THREADS
  752. /*
  753.  *----------------------------------------------------------------------
  754.  *
  755.  * NotifierThreadProc --
  756.  *
  757.  * This routine is the initial (and only) function executed by the
  758.  * special notifier thread.  Its job is to wait for file descriptors
  759.  * to become readable or writable or to have an exception condition
  760.  * and then to notify other threads who are interested in this
  761.  * information by signalling a condition variable.  Other threads
  762.  * can signal this notifier thread of a change in their interests
  763.  * by writing a single byte to a special pipe that the notifier
  764.  * thread is monitoring.
  765.  *
  766.  * Result:
  767.  * None.  Once started, this routine never exits.  It dies with
  768.  * the overall process.
  769.  *
  770.  * Side effects:
  771.  * The trigger pipe used to signal the notifier thread is created
  772.  * when the notifier thread first starts.
  773.  *
  774.  *----------------------------------------------------------------------
  775.  */
  776. static void
  777. NotifierThreadProc(clientData)
  778.     ClientData clientData; /* Not used. */
  779. {
  780.     ThreadSpecificData *tsdPtr;
  781.     fd_set readableMask;
  782.     fd_set writableMask;
  783.     fd_set exceptionalMask;
  784.     int fds[2];
  785.     int i, status, numFdBits = 0, receivePipe;
  786.     long found;
  787.     struct timeval poll = {0., 0.}, *timePtr;
  788.     char buf[2];
  789.     if (pipe(fds) != 0) {
  790. panic("NotifierThreadProc: could not create trigger pipe.");
  791.     }
  792.     receivePipe = fds[0];
  793. #ifndef USE_FIONBIO
  794.     status = fcntl(receivePipe, F_GETFL);
  795.     status |= O_NONBLOCK;
  796.     if (fcntl(receivePipe, F_SETFL, status) < 0) {
  797. panic("NotifierThreadProc: could not make receive pipe non blocking.");
  798.     }
  799.     status = fcntl(fds[1], F_GETFL);
  800.     status |= O_NONBLOCK;
  801.     if (fcntl(fds[1], F_SETFL, status) < 0) {
  802. panic("NotifierThreadProc: could not make trigger pipe non blocking.");
  803.     }
  804. #else
  805.     if (ioctl(receivePipe, (int) FIONBIO, &status) < 0) {
  806. panic("NotifierThreadProc: could not make receive pipe non blocking.");
  807.     }
  808.     if (ioctl(fds[1], (int) FIONBIO, &status) < 0) {
  809. panic("NotifierThreadProc: could not make trigger pipe non blocking.");
  810.     }
  811. #endif
  812.     /*
  813.      * Install the write end of the pipe into the global variable.
  814.      */
  815.     Tcl_MutexLock(&notifierMutex);
  816.     triggerPipe = fds[1];
  817.     /*
  818.      * Signal any threads that are waiting.
  819.      */
  820.     Tcl_ConditionNotify(&notifierCV);
  821.     Tcl_MutexUnlock(&notifierMutex);
  822.     /*
  823.      * Look for file events and report them to interested threads.
  824.      */
  825.     while (1) {
  826. FD_ZERO( &readableMask );
  827. FD_ZERO( &writableMask );
  828. FD_ZERO( &exceptionalMask );
  829. /*
  830.  * Compute the logical OR of the select masks from all the
  831.  * waiting notifiers.
  832.  */
  833. Tcl_MutexLock(&notifierMutex);
  834. timePtr = NULL;
  835.         for (tsdPtr = waitingListPtr; tsdPtr; tsdPtr = tsdPtr->nextPtr) {
  836.     for ( i = tsdPtr->numFdBits-1; i >= 0; --i ) {
  837. if ( FD_ISSET( i, &(tsdPtr->checkMasks.readable) ) ) {
  838.     FD_SET( i, &readableMask );
  839. }
  840. if ( FD_ISSET( i, &(tsdPtr->checkMasks.writable) ) ) {
  841.     FD_SET( i, &writableMask );
  842. }
  843. if ( FD_ISSET( i, &(tsdPtr->checkMasks.exceptional) ) ) {
  844.     FD_SET( i, &exceptionalMask );
  845. }
  846.     }
  847.     if ( tsdPtr->numFdBits > numFdBits ) {
  848. numFdBits = tsdPtr->numFdBits;
  849.     }
  850.     if (tsdPtr->pollState & POLL_WANT) {
  851. /*
  852.  * Here we make sure we go through select() with the same
  853.  * mask bits that were present when the thread tried to poll.
  854.  */
  855. tsdPtr->pollState |= POLL_DONE;
  856. timePtr = &poll;
  857.     }
  858. }
  859. Tcl_MutexUnlock(&notifierMutex);
  860. /*
  861.  * Set up the select mask to include the receive pipe.
  862.  */
  863. if ( receivePipe >= numFdBits ) {
  864.     numFdBits = receivePipe + 1;
  865. }
  866. FD_SET( receivePipe, &readableMask );
  867. if ( select( numFdBits, &readableMask, &writableMask,
  868.      &exceptionalMask, timePtr) == -1 ) {
  869.     /*
  870.      * Try again immediately on an error.
  871.      */
  872.     continue;
  873.         }
  874. /*
  875.  * Alert any threads that are waiting on a ready file descriptor.
  876.  */
  877. Tcl_MutexLock(&notifierMutex);
  878.         for (tsdPtr = waitingListPtr; tsdPtr; tsdPtr = tsdPtr->nextPtr) {
  879.     found = 0;
  880.     for ( i = tsdPtr->numFdBits-1; i >= 0; --i ) {
  881. if ( FD_ISSET( i, &(tsdPtr->checkMasks.readable) )
  882.      && FD_ISSET( i, &readableMask ) ) {
  883.     FD_SET( i, &(tsdPtr->readyMasks.readable) );
  884.     found = 1;
  885. }
  886. if ( FD_ISSET( i, &(tsdPtr->checkMasks.writable) )
  887.      && FD_ISSET( i, &writableMask ) ) {
  888.     FD_SET( i, &(tsdPtr->readyMasks.writable) );
  889.     found = 1;
  890. }
  891. if ( FD_ISSET( i, &(tsdPtr->checkMasks.exceptional) )
  892.      && FD_ISSET( i, &exceptionalMask ) ) {
  893.     FD_SET( i, &(tsdPtr->readyMasks.exceptional) );
  894.     found = 1;
  895. }
  896.     }
  897.             if (found || (tsdPtr->pollState & POLL_DONE)) {
  898.                 tsdPtr->eventReady = 1;
  899. if (tsdPtr->onList) {
  900.     /*
  901.      * Remove the ThreadSpecificData structure of this
  902.      * thread from the waiting list. This prevents us from
  903.      * continuously spining on select until the other
  904.      * threads runs and services the file event.
  905.      */
  906.     if (tsdPtr->prevPtr) {
  907. tsdPtr->prevPtr->nextPtr = tsdPtr->nextPtr;
  908.     } else {
  909. waitingListPtr = tsdPtr->nextPtr;
  910.     }
  911.     if (tsdPtr->nextPtr) {
  912. tsdPtr->nextPtr->prevPtr = tsdPtr->prevPtr;
  913.     }
  914.     tsdPtr->nextPtr = tsdPtr->prevPtr = NULL;
  915.     tsdPtr->onList = 0;
  916.     tsdPtr->pollState = 0;
  917. }
  918. Tcl_ConditionNotify(&tsdPtr->waitCV);
  919.             }
  920.         }
  921. Tcl_MutexUnlock(&notifierMutex);
  922. /*
  923.  * Consume the next byte from the notifier pipe if the pipe was
  924.  * readable.  Note that there may be multiple bytes pending, but
  925.  * to avoid a race condition we only read one at a time.
  926.  */
  927. if ( FD_ISSET( receivePipe, &readableMask ) ) {
  928.     i = read(receivePipe, buf, 1);
  929.     if ((i == 0) || ((i == 1) && (buf[0] == 'q'))) {
  930. /*
  931.  * Someone closed the write end of the pipe or sent us a
  932.  * Quit message [Bug: 4139] and then closed the write end
  933.  * of the pipe so we need to shut down the notifier thread.
  934.  */
  935. break;
  936.     }
  937. }
  938.     }
  939.     /*
  940.      * Clean up the read end of the pipe and signal any threads waiting on
  941.      * termination of the notifier thread.
  942.      */
  943.     close(receivePipe);
  944.     Tcl_MutexLock(&notifierMutex);
  945.     triggerPipe = -1;
  946.     Tcl_ConditionNotify(&notifierCV);
  947.     Tcl_MutexUnlock(&notifierMutex);
  948.     TclpThreadExit (0);
  949. }
  950. #endif
  951. #endif /* HAVE_COREFOUNDATION */