tclUnixNotfy.cGo 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(¬ifierMutex); 00223 if (notifierCount == 0) { 00224 if (TclpThreadCreate(¬ifierThread, 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(¬ifierCV, ¬ifierMutex, NULL); 00237 } 00238 00239 Tcl_MutexUnlock(¬ifierMutex); 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(¬ifierMutex); 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(¬ifierCV, ¬ifierMutex, 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(¬ifierMutex); 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(¬ifierMutex); 00343 tsdPtr->eventReady = 1; 00344 Tcl_ConditionNotify(&tsdPtr->waitCV); 00345 Tcl_MutexUnlock(¬ifierMutex); 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(¬ifierMutex); 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, ¬ifierMutex, 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(¬ifierMutex); 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(¬ifierMutex); 00946 triggerPipe = fds[1]; 00947 00948 /* 00949 * Signal any threads that are waiting. 00950 */ 00951 00952 Tcl_ConditionNotify(¬ifierCV); 00953 Tcl_MutexUnlock(¬ifierMutex); 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(¬ifierMutex); 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(¬ifierMutex); 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(¬ifierMutex); 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(¬ifierMutex); 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(¬ifierMutex); 01097 triggerPipe = -1; 01098 Tcl_ConditionNotify(¬ifierCV); 01099 Tcl_MutexUnlock(¬ifierMutex); 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 1.5.1 |