tclUnixNotfy.c

Go to the documentation of this file.
00001 /*
00002  * tclUnixNotify.c --
00003  *
00004  *      This file contains the implementation of the select()-based
00005  *      Unix-specific notifier, which is the lowest-level part of the Tcl
00006  *      event loop. This file works together with generic/tclNotify.c.
00007  *
00008  * Copyright (c) 1995-1997 Sun Microsystems, Inc.
00009  *
00010  * See the file "license.terms" for information on usage and redistribution of
00011  * this file, and for a DISCLAIMER OF ALL WARRANTIES.
00012  *
00013  * RCS: @(#) $Id: tclUnixNotfy.c,v 1.32 2006/08/21 01:08:03 das Exp $
00014  */
00015 
00016 #include "tclInt.h"
00017 #ifndef HAVE_COREFOUNDATION     /* Darwin/Mac OS X CoreFoundation notifier is
00018                                  * in tclMacOSXNotify.c */
00019 #include <signal.h>
00020 
00021 /*
00022  * This code does deep stub magic to allow replacement of the notifier at
00023  * runtime.
00024  */
00025 
00026 extern TclStubs tclStubs;
00027 extern Tcl_NotifierProcs tclOriginalNotifier;
00028 
00029 /*
00030  * This structure is used to keep track of the notifier info for a registered
00031  * file.
00032  */
00033 
00034 typedef struct FileHandler {
00035     int fd;
00036     int mask;                   /* Mask of desired events: TCL_READABLE,
00037                                  * etc. */
00038     int readyMask;              /* Mask of events that have been seen since
00039                                  * the last time file handlers were invoked
00040                                  * for this file. */
00041     Tcl_FileProc *proc;         /* Function to call, in the style of
00042                                  * Tcl_CreateFileHandler. */
00043     ClientData clientData;      /* Argument to pass to proc. */
00044     struct FileHandler *nextPtr;/* Next in list of all files we care about. */
00045 } FileHandler;
00046 
00047 /*
00048  * The following structure is what is added to the Tcl event queue when file
00049  * handlers are ready to fire.
00050  */
00051 
00052 typedef struct FileHandlerEvent {
00053     Tcl_Event header;           /* Information that is standard for all
00054                                  * events. */
00055     int fd;                     /* File descriptor that is ready. Used to find
00056                                  * the FileHandler structure for the file
00057                                  * (can't point directly to the FileHandler
00058                                  * structure because it could go away while
00059                                  * the event is queued). */
00060 } FileHandlerEvent;
00061 
00062 /*
00063  * The following structure contains a set of select() masks to track readable,
00064  * writable, and exceptional conditions.
00065  */
00066 
00067 typedef struct SelectMasks {
00068     fd_set readable;
00069     fd_set writable;
00070     fd_set exceptional;
00071 } SelectMasks;
00072 
00073 /*
00074  * The following static structure contains the state information for the
00075  * select based implementation of the Tcl notifier. One of these structures is
00076  * created for each thread that is using the notifier.
00077  */
00078 
00079 typedef struct ThreadSpecificData {
00080     FileHandler *firstFileHandlerPtr;
00081                                 /* Pointer to head of file handler list. */
00082     SelectMasks checkMasks;     /* This structure is used to build up the
00083                                  * masks to be used in the next call to
00084                                  * select. Bits are set in response to calls
00085                                  * to Tcl_CreateFileHandler. */
00086     SelectMasks readyMasks;     /* This array reflects the readable/writable
00087                                  * conditions that were found to exist by the
00088                                  * last call to select. */
00089     int numFdBits;              /* Number of valid bits in checkMasks (one
00090                                  * more than highest fd for which
00091                                  * Tcl_WatchFile has been called). */
00092 #ifdef TCL_THREADS
00093     int onList;                 /* True if it is in this list */
00094     unsigned int pollState;     /* pollState is used to implement a polling
00095                                  * handshake between each thread and the
00096                                  * notifier thread. Bits defined below. */
00097     struct ThreadSpecificData *nextPtr, *prevPtr;
00098                                 /* All threads that are currently waiting on
00099                                  * an event have their ThreadSpecificData
00100                                  * structure on a doubly-linked listed formed
00101                                  * from these pointers. You must hold the
00102                                  * notifierMutex lock before accessing these
00103                                  * fields. */
00104     Tcl_Condition waitCV;       /* Any other thread alerts a notifier that an
00105                                  * event is ready to be processed by signaling
00106                                  * this condition variable. */
00107     int eventReady;             /* True if an event is ready to be processed.
00108                                  * Used as condition flag together with waitCV
00109                                  * above. */
00110 #endif
00111 } ThreadSpecificData;
00112 
00113 static Tcl_ThreadDataKey dataKey;
00114 
00115 #ifdef TCL_THREADS
00116 /*
00117  * The following static indicates the number of threads that have initialized
00118  * notifiers.
00119  *
00120  * You must hold the notifierMutex lock before accessing this variable.
00121  */
00122 
00123 static int notifierCount = 0;
00124 
00125 /*
00126  * The following variable points to the head of a doubly-linked list of
00127  * ThreadSpecificData structures for all threads that are currently waiting on
00128  * an event.
00129  *
00130  * You must hold the notifierMutex lock before accessing this list.
00131  */
00132 
00133 static ThreadSpecificData *waitingListPtr = NULL;
00134 
00135 /*
00136  * The notifier thread spends all its time in select() waiting for a file
00137  * descriptor associated with one of the threads on the waitingListPtr list to
00138  * do something interesting. But if the contents of the waitingListPtr list
00139  * ever changes, we need to wake up and restart the select() system call. You
00140  * can wake up the notifier thread by writing a single byte to the file
00141  * descriptor defined below. This file descriptor is the input-end of a pipe
00142  * and the notifier thread is listening for data on the output-end of the same
00143  * pipe. Hence writing to this file descriptor will cause the select() system
00144  * call to return and wake up the notifier thread.
00145  *
00146  * You must hold the notifierMutex lock before accessing this list.
00147  */
00148 
00149 static int triggerPipe = -1;
00150 
00151 /*
00152  * The notifierMutex locks access to all of the global notifier state.
00153  */
00154 
00155 TCL_DECLARE_MUTEX(notifierMutex)
00156 
00157 /*
00158  * The notifier thread signals the notifierCV when it has finished
00159  * initializing the triggerPipe and right before the notifier thread
00160  * terminates.
00161  */
00162 
00163 static Tcl_Condition notifierCV;
00164 
00165 /*
00166  * The pollState bits
00167  *      POLL_WANT is set by each thread before it waits on its condition
00168  *              variable. It is checked by the notifier before it does select.
00169  *      POLL_DONE is set by the notifier if it goes into select after seeing
00170  *              POLL_WANT. The idea is to ensure it tries a select with the
00171  *              same bits the initial thread had set.
00172  */
00173 
00174 #define POLL_WANT       0x1
00175 #define POLL_DONE       0x2
00176 
00177 /*
00178  * This is the thread ID of the notifier thread that does select.
00179  */
00180 
00181 static Tcl_ThreadId notifierThread;
00182 
00183 #endif
00184 
00185 /*
00186  * Static routines defined in this file.
00187  */
00188 
00189 #ifdef TCL_THREADS
00190 static void     NotifierThreadProc(ClientData clientData);
00191 #endif
00192 static int      FileHandlerEventProc(Tcl_Event *evPtr, int flags);
00193 
00194 /*
00195  *----------------------------------------------------------------------
00196  *
00197  * Tcl_InitNotifier --
00198  *
00199  *      Initializes the platform specific notifier state.
00200  *
00201  * Results:
00202  *      Returns a handle to the notifier state for this thread..
00203  *
00204  * Side effects:
00205  *      None.
00206  *
00207  *----------------------------------------------------------------------
00208  */
00209 
00210 ClientData
00211 Tcl_InitNotifier(void)
00212 {
00213     ThreadSpecificData *tsdPtr = TCL_TSD_INIT(&dataKey);
00214 
00215 #ifdef TCL_THREADS
00216     tsdPtr->eventReady = 0;
00217 
00218     /*
00219      * Start the Notifier thread if necessary.
00220      */
00221 
00222     Tcl_MutexLock(&notifierMutex);
00223     if (notifierCount == 0) {
00224         if (TclpThreadCreate(&notifierThread, NotifierThreadProc, NULL,
00225                 TCL_THREAD_STACK_DEFAULT, TCL_THREAD_JOINABLE) != TCL_OK) {
00226             Tcl_Panic("Tcl_InitNotifier: unable to start notifier thread");
00227         }
00228     }
00229     notifierCount++;
00230 
00231     /*
00232      * Wait for the notifier pipe to be created.
00233      */
00234 
00235     while (triggerPipe < 0) {
00236         Tcl_ConditionWait(&notifierCV, &notifierMutex, NULL);
00237     }
00238 
00239     Tcl_MutexUnlock(&notifierMutex);
00240 #endif
00241     return (ClientData) tsdPtr;
00242 }
00243 
00244 /*
00245  *----------------------------------------------------------------------
00246  *
00247  * Tcl_FinalizeNotifier --
00248  *
00249  *      This function is called to cleanup the notifier state before a thread
00250  *      is terminated.
00251  *
00252  * Results:
00253  *      None.
00254  *
00255  * Side effects:
00256  *      May terminate the background notifier thread if this is the last
00257  *      notifier instance.
00258  *
00259  *----------------------------------------------------------------------
00260  */
00261 
00262 void
00263 Tcl_FinalizeNotifier(
00264     ClientData clientData)              /* Not used. */
00265 {
00266 #ifdef TCL_THREADS
00267     ThreadSpecificData *tsdPtr = TCL_TSD_INIT(&dataKey);
00268 
00269     Tcl_MutexLock(&notifierMutex);
00270     notifierCount--;
00271 
00272     /*
00273      * If this is the last thread to use the notifier, close the notifier pipe
00274      * and wait for the background thread to terminate.
00275      */
00276 
00277     if (notifierCount == 0) {
00278         int result;
00279 
00280         if (triggerPipe < 0) {
00281             Tcl_Panic("Tcl_FinalizeNotifier: notifier pipe not initialized");
00282         }
00283 
00284         /*
00285          * Send "q" message to the notifier thread so that it will terminate.
00286          * The notifier will return from its call to select() and notice that
00287          * a "q" message has arrived, it will then close its side of the pipe
00288          * and terminate its thread. Note the we can not just close the pipe
00289          * and check for EOF in the notifier thread because if a background
00290          * child process was created with exec, select() would not register
00291          * the EOF on the pipe until the child processes had terminated. [Bug:
00292          * 4139] [Bug: 1222872]
00293          */
00294 
00295         write(triggerPipe, "q", 1);
00296         close(triggerPipe);
00297         while(triggerPipe >= 0) {
00298             Tcl_ConditionWait(&notifierCV, &notifierMutex, NULL);
00299         }
00300 
00301         result = Tcl_JoinThread(notifierThread, NULL);
00302         if (result) {
00303             Tcl_Panic("Tcl_FinalizeNotifier: unable to join notifier thread");
00304         }
00305     }
00306 
00307     /*
00308      * Clean up any synchronization objects in the thread local storage.
00309      */
00310 
00311     Tcl_ConditionFinalize(&(tsdPtr->waitCV));
00312 
00313     Tcl_MutexUnlock(&notifierMutex);
00314 #endif
00315 }
00316 
00317 /*
00318  *----------------------------------------------------------------------
00319  *
00320  * Tcl_AlertNotifier --
00321  *
00322  *      Wake up the specified notifier from any thread. This routine is called
00323  *      by the platform independent notifier code whenever the Tcl_ThreadAlert
00324  *      routine is called. This routine is guaranteed not to be called on a
00325  *      given notifier after Tcl_FinalizeNotifier is called for that notifier.
00326  *
00327  * Results:
00328  *      None.
00329  *
00330  * Side effects:
00331  *      Signals the notifier condition variable for the specified notifier.
00332  *
00333  *----------------------------------------------------------------------
00334  */
00335 
00336 void
00337 Tcl_AlertNotifier(
00338     ClientData clientData)
00339 {
00340 #ifdef TCL_THREADS
00341     ThreadSpecificData *tsdPtr = (ThreadSpecificData *) clientData;
00342     Tcl_MutexLock(&notifierMutex);
00343     tsdPtr->eventReady = 1;
00344     Tcl_ConditionNotify(&tsdPtr->waitCV);
00345     Tcl_MutexUnlock(&notifierMutex);
00346 #endif
00347 }
00348 
00349 /*
00350  *----------------------------------------------------------------------
00351  *
00352  * Tcl_SetTimer --
00353  *
00354  *      This function sets the current notifier timer value. This interface is
00355  *      not implemented in this notifier because we are always running inside
00356  *      of Tcl_DoOneEvent.
00357  *
00358  * Results:
00359  *      None.
00360  *
00361  * Side effects:
00362  *      None.
00363  *
00364  *----------------------------------------------------------------------
00365  */
00366 
00367 void
00368 Tcl_SetTimer(
00369     Tcl_Time *timePtr)          /* Timeout value, may be NULL. */
00370 {
00371     /*
00372      * The interval timer doesn't do anything in this implementation, because
00373      * the only event loop is via Tcl_DoOneEvent, which passes timeout values
00374      * to Tcl_WaitForEvent.
00375      */
00376 
00377     if (tclStubs.tcl_SetTimer != tclOriginalNotifier.setTimerProc) {
00378         tclStubs.tcl_SetTimer(timePtr);
00379     }
00380 }
00381 
00382 /*
00383  *----------------------------------------------------------------------
00384  *
00385  * Tcl_ServiceModeHook --
00386  *
00387  *      This function is invoked whenever the service mode changes.
00388  *
00389  * Results:
00390  *      None.
00391  *
00392  * Side effects:
00393  *      None.
00394  *
00395  *----------------------------------------------------------------------
00396  */
00397 
00398 void
00399 Tcl_ServiceModeHook(
00400     int mode)                   /* Either TCL_SERVICE_ALL, or
00401                                  * TCL_SERVICE_NONE. */
00402 {
00403 }
00404 
00405 /*
00406  *----------------------------------------------------------------------
00407  *
00408  * Tcl_CreateFileHandler --
00409  *
00410  *      This function registers a file handler with the select notifier.
00411  *
00412  * Results:
00413  *      None.
00414  *
00415  * Side effects:
00416  *      Creates a new file handler structure.
00417  *
00418  *----------------------------------------------------------------------
00419  */
00420 
00421 void
00422 Tcl_CreateFileHandler(
00423     int fd,                     /* Handle of stream to watch. */
00424     int mask,                   /* OR'ed combination of TCL_READABLE,
00425                                  * TCL_WRITABLE, and TCL_EXCEPTION: indicates
00426                                  * conditions under which proc should be
00427                                  * called. */
00428     Tcl_FileProc *proc,         /* Function to call for each selected
00429                                  * event. */
00430     ClientData clientData)      /* Arbitrary data to pass to proc. */
00431 {
00432     ThreadSpecificData *tsdPtr = TCL_TSD_INIT(&dataKey);
00433     FileHandler *filePtr;
00434 
00435     if (tclStubs.tcl_CreateFileHandler !=
00436             tclOriginalNotifier.createFileHandlerProc) {
00437         tclStubs.tcl_CreateFileHandler(fd, mask, proc, clientData);
00438         return;
00439     }
00440 
00441     for (filePtr = tsdPtr->firstFileHandlerPtr; filePtr != NULL;
00442             filePtr = filePtr->nextPtr) {
00443         if (filePtr->fd == fd) {
00444             break;
00445         }
00446     }
00447     if (filePtr == NULL) {
00448         filePtr = (FileHandler*) ckalloc(sizeof(FileHandler));
00449         filePtr->fd = fd;
00450         filePtr->readyMask = 0;
00451         filePtr->nextPtr = tsdPtr->firstFileHandlerPtr;
00452         tsdPtr->firstFileHandlerPtr = filePtr;
00453     }
00454     filePtr->proc = proc;
00455     filePtr->clientData = clientData;
00456     filePtr->mask = mask;
00457 
00458     /*
00459      * Update the check masks for this file.
00460      */
00461 
00462     if (mask & TCL_READABLE) {
00463         FD_SET(fd, &(tsdPtr->checkMasks.readable));
00464     } else {
00465         FD_CLR(fd, &(tsdPtr->checkMasks.readable));
00466     }
00467     if (mask & TCL_WRITABLE) {
00468         FD_SET(fd, &(tsdPtr->checkMasks.writable));
00469     } else {
00470         FD_CLR(fd, &(tsdPtr->checkMasks.writable));
00471     }
00472     if (mask & TCL_EXCEPTION) {
00473         FD_SET(fd, &(tsdPtr->checkMasks.exceptional));
00474     } else {
00475         FD_CLR(fd, &(tsdPtr->checkMasks.exceptional));
00476     }
00477     if (tsdPtr->numFdBits <= fd) {
00478         tsdPtr->numFdBits = fd+1;
00479     }
00480 }
00481 
00482 /*
00483  *----------------------------------------------------------------------
00484  *
00485  * Tcl_DeleteFileHandler --
00486  *
00487  *      Cancel a previously-arranged callback arrangement for a file.
00488  *
00489  * Results:
00490  *      None.
00491  *
00492  * Side effects:
00493  *      If a callback was previously registered on file, remove it.
00494  *
00495  *----------------------------------------------------------------------
00496  */
00497 
00498 void
00499 Tcl_DeleteFileHandler(
00500     int fd)                     /* Stream id for which to remove callback
00501                                  * function. */
00502 {
00503     FileHandler *filePtr, *prevPtr;
00504     int i;
00505     ThreadSpecificData *tsdPtr = TCL_TSD_INIT(&dataKey);
00506 
00507     if (tclStubs.tcl_DeleteFileHandler !=
00508             tclOriginalNotifier.deleteFileHandlerProc) {
00509         tclStubs.tcl_DeleteFileHandler(fd);
00510         return;
00511     }
00512 
00513     /*
00514      * Find the entry for the given file (and return if there isn't one).
00515      */
00516 
00517     for (prevPtr = NULL, filePtr = tsdPtr->firstFileHandlerPtr; ;
00518          prevPtr = filePtr, filePtr = filePtr->nextPtr) {
00519         if (filePtr == NULL) {
00520             return;
00521         }
00522         if (filePtr->fd == fd) {
00523             break;
00524         }
00525     }
00526 
00527     /*
00528      * Update the check masks for this file.
00529      */
00530 
00531     if (filePtr->mask & TCL_READABLE) {
00532         FD_CLR(fd, &(tsdPtr->checkMasks.readable));
00533     }
00534     if (filePtr->mask & TCL_WRITABLE) {
00535         FD_CLR(fd, &(tsdPtr->checkMasks.writable));
00536     }
00537     if (filePtr->mask & TCL_EXCEPTION) {
00538         FD_CLR(fd, &(tsdPtr->checkMasks.exceptional));
00539     }
00540 
00541     /*
00542      * Find current max fd.
00543      */
00544 
00545     if (fd+1 == tsdPtr->numFdBits) {
00546         tsdPtr->numFdBits = 0;
00547         for (i = fd-1; i >= 0; i--) {
00548             if (FD_ISSET(i, &(tsdPtr->checkMasks.readable))
00549                     || FD_ISSET(i, &(tsdPtr->checkMasks.writable))
00550                     || FD_ISSET(i, &(tsdPtr->checkMasks.exceptional))) {
00551                 tsdPtr->numFdBits = i+1;
00552                 break;
00553             }
00554         }
00555     }
00556 
00557     /*
00558      * Clean up information in the callback record.
00559      */
00560 
00561     if (prevPtr == NULL) {
00562         tsdPtr->firstFileHandlerPtr = filePtr->nextPtr;
00563     } else {
00564         prevPtr->nextPtr = filePtr->nextPtr;
00565     }
00566     ckfree((char *) filePtr);
00567 }
00568 
00569 /*
00570  *----------------------------------------------------------------------
00571  *
00572  * FileHandlerEventProc --
00573  *
00574  *      This function is called by Tcl_ServiceEvent when a file event reaches
00575  *      the front of the event queue. This function is responsible for
00576  *      actually handling the event by invoking the callback for the file
00577  *      handler.
00578  *
00579  * Results:
00580  *      Returns 1 if the event was handled, meaning it should be removed from
00581  *      the queue. Returns 0 if the event was not handled, meaning it should
00582  *      stay on the queue. The only time the event isn't handled is if the
00583  *      TCL_FILE_EVENTS flag bit isn't set.
00584  *
00585  * Side effects:
00586  *      Whatever the file handler's callback function does.
00587  *
00588  *----------------------------------------------------------------------
00589  */
00590 
00591 static int
00592 FileHandlerEventProc(
00593     Tcl_Event *evPtr,           /* Event to service. */
00594     int flags)                  /* Flags that indicate what events to handle,
00595                                  * such as TCL_FILE_EVENTS. */
00596 {
00597     int mask;
00598     FileHandler *filePtr;
00599     FileHandlerEvent *fileEvPtr = (FileHandlerEvent *) evPtr;
00600     ThreadSpecificData *tsdPtr;
00601 
00602     if (!(flags & TCL_FILE_EVENTS)) {
00603         return 0;
00604     }
00605 
00606     /*
00607      * Search through the file handlers to find the one whose handle matches
00608      * the event. We do this rather than keeping a pointer to the file handler
00609      * directly in the event, so that the handler can be deleted while the
00610      * event is queued without leaving a dangling pointer.
00611      */
00612 
00613     tsdPtr = TCL_TSD_INIT(&dataKey);
00614     for (filePtr = tsdPtr->firstFileHandlerPtr; filePtr != NULL;
00615             filePtr = filePtr->nextPtr) {
00616         if (filePtr->fd != fileEvPtr->fd) {
00617             continue;
00618         }
00619 
00620         /*
00621          * The code is tricky for two reasons:
00622          * 1. The file handler's desired events could have changed since the
00623          *    time when the event was queued, so AND the ready mask with the
00624          *    desired mask.
00625          * 2. The file could have been closed and re-opened since the time
00626          *    when the event was queued. This is why the ready mask is stored
00627          *    in the file handler rather than the queued event: it will be
00628          *    zeroed when a new file handler is created for the newly opened
00629          *    file.
00630          */
00631 
00632         mask = filePtr->readyMask & filePtr->mask;
00633         filePtr->readyMask = 0;
00634         if (mask != 0) {
00635             (*filePtr->proc)(filePtr->clientData, mask);
00636         }
00637         break;
00638     }
00639     return 1;
00640 }
00641 
00642 /*
00643  *----------------------------------------------------------------------
00644  *
00645  * Tcl_WaitForEvent --
00646  *
00647  *      This function is called by Tcl_DoOneEvent to wait for new events on
00648  *      the message queue. If the block time is 0, then Tcl_WaitForEvent just
00649  *      polls without blocking.
00650  *
00651  * Results:
00652  *      Returns -1 if the select would block forever, otherwise returns 0.
00653  *
00654  * Side effects:
00655  *      Queues file events that are detected by the select.
00656  *
00657  *----------------------------------------------------------------------
00658  */
00659 
00660 int
00661 Tcl_WaitForEvent(
00662     Tcl_Time *timePtr)          /* Maximum block time, or NULL. */
00663 {
00664     FileHandler *filePtr;
00665     FileHandlerEvent *fileEvPtr;
00666     int mask;
00667     Tcl_Time myTime;
00668 #ifdef TCL_THREADS
00669     int waitForFiles;
00670     Tcl_Time *myTimePtr;
00671 #else
00672     /*
00673      * Impl. notes: timeout & timeoutPtr are used if, and only if threads are
00674      * not enabled. They are the arguments for the regular select() used when
00675      * the core is not thread-enabled.
00676      */
00677 
00678     struct timeval timeout, *timeoutPtr;
00679     int numFound;
00680 #endif /* TCL_THREADS */
00681     ThreadSpecificData *tsdPtr = TCL_TSD_INIT(&dataKey);
00682 
00683     if (tclStubs.tcl_WaitForEvent != tclOriginalNotifier.waitForEventProc) {
00684         return tclStubs.tcl_WaitForEvent(timePtr);
00685     }
00686 
00687     /*
00688      * Set up the timeout structure. Note that if there are no events to check
00689      * for, we return with a negative result rather than blocking forever.
00690      */
00691 
00692     if (timePtr != NULL) {
00693         /*
00694          * TIP #233 (Virtualized Time). Is virtual time in effect? And do we
00695          * actually have something to scale? If yes to both then we call the
00696          * handler to do this scaling.
00697          */
00698 
00699         myTime.sec  = timePtr->sec;
00700         myTime.usec = timePtr->usec;
00701 
00702         if (myTime.sec != 0 || myTime.usec != 0) {
00703             (*tclScaleTimeProcPtr) (&myTime, tclTimeClientData);
00704         }
00705 
00706 #ifdef TCL_THREADS
00707         myTimePtr = &myTime;
00708 #else
00709         timeout.tv_sec = myTime.sec;
00710         timeout.tv_usec = myTime.usec;
00711         timeoutPtr = &timeout;
00712 #endif /* TCL_THREADS */
00713 
00714 #ifndef TCL_THREADS
00715     } else if (tsdPtr->numFdBits == 0) {
00716         /*
00717          * If there are no threads, no timeout, and no fds registered, then
00718          * there are no events possible and we must avoid deadlock. Note that
00719          * this is not entirely correct because there might be a signal that
00720          * could interrupt the select call, but we don't handle that case if
00721          * we aren't using threads.
00722          */
00723 
00724         return -1;
00725 #endif /* !TCL_THREADS */
00726     } else {
00727 #ifdef TCL_THREADS
00728         myTimePtr = NULL;
00729 #else
00730         timeoutPtr = NULL;
00731 #endif /* TCL_THREADS */
00732     }
00733 
00734 #ifdef TCL_THREADS
00735     /*
00736      * Place this thread on the list of interested threads, signal the
00737      * notifier thread, and wait for a response or a timeout.
00738      */
00739 
00740     Tcl_MutexLock(&notifierMutex);
00741 
00742     waitForFiles = (tsdPtr->numFdBits > 0);
00743     if (myTimePtr != NULL && myTimePtr->sec == 0 && (myTimePtr->usec == 0
00744 #if defined(__APPLE__) && defined(__LP64__)
00745             /*
00746              * On 64-bit Darwin, pthread_cond_timedwait() appears to have a bug
00747              * that causes it to wait forever when passed an absolute time which
00748              * has already been exceeded by the system time; as a workaround,
00749              * when given a very brief timeout, just do a poll. [Bug 1457797]
00750              */
00751             || myTimePtr->usec < 10
00752 #endif
00753             )) {
00754         /*
00755          * Cannot emulate a polling select with a polling condition variable.
00756          * Instead, pretend to wait for files and tell the notifier thread
00757          * what we are doing. The notifier thread makes sure it goes through
00758          * select with its select mask in the same state as ours currently is.
00759          * We block until that happens.
00760          */
00761 
00762         waitForFiles = 1;
00763         tsdPtr->pollState = POLL_WANT;
00764         myTimePtr = NULL;
00765     } else {
00766         tsdPtr->pollState = 0;
00767     }
00768 
00769     if (waitForFiles) {
00770         /*
00771          * Add the ThreadSpecificData structure of this thread to the list of
00772          * ThreadSpecificData structures of all threads that are waiting on
00773          * file events.
00774          */
00775 
00776         tsdPtr->nextPtr = waitingListPtr;
00777         if (waitingListPtr) {
00778             waitingListPtr->prevPtr = tsdPtr;
00779         }
00780         tsdPtr->prevPtr = 0;
00781         waitingListPtr = tsdPtr;
00782         tsdPtr->onList = 1;
00783 
00784         write(triggerPipe, "", 1);
00785     }
00786 
00787     FD_ZERO(&(tsdPtr->readyMasks.readable));
00788     FD_ZERO(&(tsdPtr->readyMasks.writable));
00789     FD_ZERO(&(tsdPtr->readyMasks.exceptional));
00790 
00791     if (!tsdPtr->eventReady) {
00792         Tcl_ConditionWait(&tsdPtr->waitCV, &notifierMutex, myTimePtr);
00793     }
00794     tsdPtr->eventReady = 0;
00795 
00796     if (waitForFiles && tsdPtr->onList) {
00797         /*
00798          * Remove the ThreadSpecificData structure of this thread from the
00799          * waiting list. Alert the notifier thread to recompute its select
00800          * masks - skipping this caused a hang when trying to close a pipe
00801          * which the notifier thread was still doing a select on.
00802          */
00803 
00804         if (tsdPtr->prevPtr) {
00805             tsdPtr->prevPtr->nextPtr = tsdPtr->nextPtr;
00806         } else {
00807             waitingListPtr = tsdPtr->nextPtr;
00808         }
00809         if (tsdPtr->nextPtr) {
00810             tsdPtr->nextPtr->prevPtr = tsdPtr->prevPtr;
00811         }
00812         tsdPtr->nextPtr = tsdPtr->prevPtr = NULL;
00813         tsdPtr->onList = 0;
00814         write(triggerPipe, "", 1);
00815     }
00816 
00817 #else
00818     tsdPtr->readyMasks = tsdPtr->checkMasks;
00819     numFound = select(tsdPtr->numFdBits, &(tsdPtr->readyMasks.readable),
00820             &(tsdPtr->readyMasks.writable), &(tsdPtr->readyMasks.exceptional),
00821             timeoutPtr);
00822 
00823     /*
00824      * Some systems don't clear the masks after an error, so we have to do it
00825      * here.
00826      */
00827 
00828     if (numFound == -1) {
00829         FD_ZERO(&(tsdPtr->readyMasks.readable));
00830         FD_ZERO(&(tsdPtr->readyMasks.writable));
00831         FD_ZERO(&(tsdPtr->readyMasks.exceptional));
00832     }
00833 #endif /* TCL_THREADS */
00834 
00835     /*
00836      * Queue all detected file events before returning.
00837      */
00838 
00839     for (filePtr = tsdPtr->firstFileHandlerPtr; (filePtr != NULL);
00840             filePtr = filePtr->nextPtr) {
00841 
00842         mask = 0;
00843         if (FD_ISSET(filePtr->fd, &(tsdPtr->readyMasks.readable))) {
00844             mask |= TCL_READABLE;
00845         }
00846         if (FD_ISSET(filePtr->fd, &(tsdPtr->readyMasks.writable))) {
00847             mask |= TCL_WRITABLE;
00848         }
00849         if (FD_ISSET(filePtr->fd, &(tsdPtr->readyMasks.exceptional))) {
00850             mask |= TCL_EXCEPTION;
00851         }
00852 
00853         if (!mask) {
00854             continue;
00855         }
00856 
00857         /*
00858          * Don't bother to queue an event if the mask was previously non-zero
00859          * since an event must still be on the queue.
00860          */
00861 
00862         if (filePtr->readyMask == 0) {
00863             fileEvPtr = (FileHandlerEvent *) ckalloc(sizeof(FileHandlerEvent));
00864             fileEvPtr->header.proc = FileHandlerEventProc;
00865             fileEvPtr->fd = filePtr->fd;
00866             Tcl_QueueEvent((Tcl_Event *) fileEvPtr, TCL_QUEUE_TAIL);
00867         }
00868         filePtr->readyMask = mask;
00869     }
00870 #ifdef TCL_THREADS
00871     Tcl_MutexUnlock(&notifierMutex);
00872 #endif /* TCL_THREADS */
00873     return 0;
00874 }
00875 
00876 #ifdef TCL_THREADS
00877 /*
00878  *----------------------------------------------------------------------
00879  *
00880  * NotifierThreadProc --
00881  *
00882  *      This routine is the initial (and only) function executed by the
00883  *      special notifier thread. Its job is to wait for file descriptors to
00884  *      become readable or writable or to have an exception condition and then
00885  *      to notify other threads who are interested in this information by
00886  *      signalling a condition variable. Other threads can signal this
00887  *      notifier thread of a change in their interests by writing a single
00888  *      byte to a special pipe that the notifier thread is monitoring.
00889  *
00890  * Result:
00891  *      None. Once started, this routine never exits. It dies with the overall
00892  *      process.
00893  *
00894  * Side effects:
00895  *      The trigger pipe used to signal the notifier thread is created when
00896  *      the notifier thread first starts.
00897  *
00898  *----------------------------------------------------------------------
00899  */
00900 
00901 static void
00902 NotifierThreadProc(
00903     ClientData clientData)      /* Not used. */
00904 {
00905     ThreadSpecificData *tsdPtr;
00906     fd_set readableMask;
00907     fd_set writableMask;
00908     fd_set exceptionalMask;
00909     int fds[2];
00910     int i, status, numFdBits = 0, receivePipe;
00911     long found;
00912     struct timeval poll = {0., 0.}, *timePtr;
00913     char buf[2];
00914 
00915     if (pipe(fds) != 0) {
00916         Tcl_Panic("NotifierThreadProc: could not create trigger pipe");
00917     }
00918 
00919     receivePipe = fds[0];
00920 
00921 #ifndef USE_FIONBIO
00922     status = fcntl(receivePipe, F_GETFL);
00923     status |= O_NONBLOCK;
00924     if (fcntl(receivePipe, F_SETFL, status) < 0) {
00925         Tcl_Panic("NotifierThreadProc: could not make receive pipe non blocking");
00926     }
00927     status = fcntl(fds[1], F_GETFL);
00928     status |= O_NONBLOCK;
00929     if (fcntl(fds[1], F_SETFL, status) < 0) {
00930         Tcl_Panic("NotifierThreadProc: could not make trigger pipe non blocking");
00931     }
00932 #else
00933     if (ioctl(receivePipe, (int) FIONBIO, &status) < 0) {
00934         Tcl_Panic("NotifierThreadProc: could not make receive pipe non blocking");
00935     }
00936     if (ioctl(fds[1], (int) FIONBIO, &status) < 0) {
00937         Tcl_Panic("NotifierThreadProc: could not make trigger pipe non blocking");
00938     }
00939 #endif /* FIONBIO */
00940 
00941     /*
00942      * Install the write end of the pipe into the global variable.
00943      */
00944 
00945     Tcl_MutexLock(&notifierMutex);
00946     triggerPipe = fds[1];
00947 
00948     /*
00949      * Signal any threads that are waiting.
00950      */
00951 
00952     Tcl_ConditionNotify(&notifierCV);
00953     Tcl_MutexUnlock(&notifierMutex);
00954 
00955     /*
00956      * Look for file events and report them to interested threads.
00957      */
00958 
00959     while (1) {
00960         FD_ZERO(&readableMask);
00961         FD_ZERO(&writableMask);
00962         FD_ZERO(&exceptionalMask);
00963 
00964         /*
00965          * Compute the logical OR of the select masks from all the waiting
00966          * notifiers.
00967          */
00968 
00969         Tcl_MutexLock(&notifierMutex);
00970         timePtr = NULL;
00971         for (tsdPtr = waitingListPtr; tsdPtr; tsdPtr = tsdPtr->nextPtr) {
00972             for (i = tsdPtr->numFdBits-1; i >= 0; --i) {
00973                 if (FD_ISSET(i, &(tsdPtr->checkMasks.readable))) {
00974                     FD_SET(i, &readableMask);
00975                 }
00976                 if (FD_ISSET(i, &(tsdPtr->checkMasks.writable))) {
00977                     FD_SET(i, &writableMask);
00978                 }
00979                 if (FD_ISSET(i, &(tsdPtr->checkMasks.exceptional))) {
00980                     FD_SET(i, &exceptionalMask);
00981                 }
00982             }
00983             if (tsdPtr->numFdBits > numFdBits) {
00984                 numFdBits = tsdPtr->numFdBits;
00985             }
00986             if (tsdPtr->pollState & POLL_WANT) {
00987                 /*
00988                  * Here we make sure we go through select() with the same mask
00989                  * bits that were present when the thread tried to poll.
00990                  */
00991 
00992                 tsdPtr->pollState |= POLL_DONE;
00993                 timePtr = &poll;
00994             }
00995         }
00996         Tcl_MutexUnlock(&notifierMutex);
00997 
00998         /*
00999          * Set up the select mask to include the receive pipe.
01000          */
01001 
01002         if (receivePipe >= numFdBits) {
01003             numFdBits = receivePipe + 1;
01004         }
01005         FD_SET(receivePipe, &readableMask);
01006 
01007         if (select(numFdBits, &readableMask, &writableMask, &exceptionalMask,
01008                 timePtr) == -1) {
01009             /*
01010              * Try again immediately on an error.
01011              */
01012 
01013             continue;
01014         }
01015 
01016         /*
01017          * Alert any threads that are waiting on a ready file descriptor.
01018          */
01019 
01020         Tcl_MutexLock(&notifierMutex);
01021         for (tsdPtr = waitingListPtr; tsdPtr; tsdPtr = tsdPtr->nextPtr) {
01022             found = 0;
01023 
01024             for (i = tsdPtr->numFdBits-1; i >= 0; --i) {
01025                 if (FD_ISSET(i, &(tsdPtr->checkMasks.readable))
01026                         && FD_ISSET(i, &readableMask)) {
01027                     FD_SET(i, &(tsdPtr->readyMasks.readable));
01028                     found = 1;
01029                 }
01030                 if (FD_ISSET(i, &(tsdPtr->checkMasks.writable))
01031                         && FD_ISSET(i, &writableMask)) {
01032                     FD_SET(i, &(tsdPtr->readyMasks.writable));
01033                     found = 1;
01034                 }
01035                 if (FD_ISSET(i, &(tsdPtr->checkMasks.exceptional))
01036                         && FD_ISSET(i, &exceptionalMask)) {
01037                     FD_SET(i, &(tsdPtr->readyMasks.exceptional));
01038                     found = 1;
01039                 }
01040             }
01041 
01042             if (found || (tsdPtr->pollState & POLL_DONE)) {
01043                 tsdPtr->eventReady = 1;
01044                 if (tsdPtr->onList) {
01045                     /*
01046                      * Remove the ThreadSpecificData structure of this thread
01047                      * from the waiting list. This prevents us from
01048                      * continuously spining on select until the other threads
01049                      * runs and services the file event.
01050                      */
01051 
01052                     if (tsdPtr->prevPtr) {
01053                         tsdPtr->prevPtr->nextPtr = tsdPtr->nextPtr;
01054                     } else {
01055                         waitingListPtr = tsdPtr->nextPtr;
01056                     }
01057                     if (tsdPtr->nextPtr) {
01058                         tsdPtr->nextPtr->prevPtr = tsdPtr->prevPtr;
01059                     }
01060                     tsdPtr->nextPtr = tsdPtr->prevPtr = NULL;
01061                     tsdPtr->onList = 0;
01062                     tsdPtr->pollState = 0;
01063                 }
01064                 Tcl_ConditionNotify(&tsdPtr->waitCV);
01065             }
01066         }
01067         Tcl_MutexUnlock(&notifierMutex);
01068 
01069         /*
01070          * Consume the next byte from the notifier pipe if the pipe was
01071          * readable. Note that there may be multiple bytes pending, but to
01072          * avoid a race condition we only read one at a time.
01073          */
01074 
01075         if (FD_ISSET(receivePipe, &readableMask)) {
01076             i = read(receivePipe, buf, 1);
01077 
01078             if ((i == 0) || ((i == 1) && (buf[0] == 'q'))) {
01079                 /*
01080                  * Someone closed the write end of the pipe or sent us a Quit
01081                  * message [Bug: 4139] and then closed the write end of the
01082                  * pipe so we need to shut down the notifier thread.
01083                  */
01084 
01085                 break;
01086             }
01087         }
01088     }
01089 
01090     /*
01091      * Clean up the read end of the pipe and signal any threads waiting on
01092      * termination of the notifier thread.
01093      */
01094 
01095     close(receivePipe);
01096     Tcl_MutexLock(&notifierMutex);
01097     triggerPipe = -1;
01098     Tcl_ConditionNotify(&notifierCV);
01099     Tcl_MutexUnlock(&notifierMutex);
01100 
01101     TclpThreadExit (0);
01102 }
01103 #endif /* TCL_THREADS */
01104 
01105 #endif /* HAVE_COREFOUNDATION */
01106 
01107 /*
01108  * Local Variables:
01109  * mode: c
01110  * c-basic-offset: 4
01111  * fill-column: 78
01112  * End:
01113  */



Generated on Wed Mar 12 12:18:26 2008 by  doxygen 1.5.1