diff options
Diffstat (limited to 'unix/tclUnixNotfy.c')
-rw-r--r-- | unix/tclUnixNotfy.c | 682 |
1 files changed, 349 insertions, 333 deletions
diff --git a/unix/tclUnixNotfy.c b/unix/tclUnixNotfy.c index 128b080..51f0b1f 100644 --- a/unix/tclUnixNotfy.c +++ b/unix/tclUnixNotfy.c @@ -1,63 +1,65 @@ /* * tclUnixNotify.c -- * - * This file contains the implementation of the select-based - * Unix-specific notifier, which is the lowest-level part of the - * Tcl event loop. This file works together with - * ../generic/tclNotify.c. + * This file contains the implementation of the select()-based + * Unix-specific notifier, which is the lowest-level part of the Tcl + * event loop. This file works together with generic/tclNotify.c. * * Copyright (c) 1995-1997 Sun Microsystems, Inc. * - * See the file "license.terms" for information on usage and redistribution - * of this file, and for a DISCLAIMER OF ALL WARRANTIES. + * See the file "license.terms" for information on usage and redistribution of + * this file, and for a DISCLAIMER OF ALL WARRANTIES. */ #include "tclInt.h" -#include "tclPort.h" -#ifndef HAVE_COREFOUNDATION /* Darwin/Mac OS X CoreFoundation notifier - * is in tclMacOSXNotify.c */ -#include <signal.h> +#ifndef HAVE_COREFOUNDATION /* Darwin/Mac OS X CoreFoundation notifier is + * in tclMacOSXNotify.c */ +#include <signal.h> + +/* + * This code does deep stub magic to allow replacement of the notifier at + * runtime. + */ extern TclStubs tclStubs; extern Tcl_NotifierProcs tclOriginalNotifier; /* - * This structure is used to keep track of the notifier info for a - * a registered file. + * This structure is used to keep track of the notifier info for a registered + * file. */ typedef struct FileHandler { int fd; int mask; /* Mask of desired events: TCL_READABLE, * etc. */ - int readyMask; /* Mask of events that have been seen since the - * last time file handlers were invoked for - * this file. */ - Tcl_FileProc *proc; /* Procedure to call, in the style of + int readyMask; /* Mask of events that have been seen since + * the last time file handlers were invoked + * for this file. */ + Tcl_FileProc *proc; /* Function to call, in the style of * Tcl_CreateFileHandler. */ ClientData clientData; /* Argument to pass to proc. */ struct FileHandler *nextPtr;/* Next in list of all files we care about. */ } FileHandler; /* - * The following structure is what is added to the Tcl event queue when - * file handlers are ready to fire. + * The following structure is what is added to the Tcl event queue when file + * handlers are ready to fire. */ typedef struct FileHandlerEvent { - Tcl_Event header; /* Information that is standard for - * all events. */ - int fd; /* File descriptor that is ready. Used - * to find the FileHandler structure for - * the file (can't point directly to the - * FileHandler structure because it could - * go away while the event is queued). */ + Tcl_Event header; /* Information that is standard for all + * events. */ + int fd; /* File descriptor that is ready. Used to find + * the FileHandler structure for the file + * (can't point directly to the FileHandler + * structure because it could go away while + * the event is queued). */ } FileHandlerEvent; /* - * - * The following structure contains a set of select() masks to track - * readable, writable, and exceptional conditions. + * The following structure contains a set of select() masks to track readable, + * writable, and exceptional conditions. */ typedef struct SelectMasks { @@ -68,49 +70,48 @@ typedef struct SelectMasks { /* * The following static structure contains the state information for the - * select based implementation of the Tcl notifier. One of these structures - * is created for each thread that is using the notifier. + * select based implementation of the Tcl notifier. One of these structures is + * created for each thread that is using the notifier. */ typedef struct ThreadSpecificData { FileHandler *firstFileHandlerPtr; /* Pointer to head of file handler list. */ - - SelectMasks checkMasks; /* This structure is used to build up the masks - * to be used in the next call to select. - * Bits are set in response to calls to - * Tcl_CreateFileHandler. */ + SelectMasks checkMasks; /* This structure is used to build up the + * masks to be used in the next call to + * select. Bits are set in response to calls + * to Tcl_CreateFileHandler. */ SelectMasks readyMasks; /* This array reflects the readable/writable * conditions that were found to exist by the * last call to select. */ - int numFdBits; /* Number of valid bits in checkMasks - * (one more than highest fd for which + int numFdBits; /* Number of valid bits in checkMasks (one + * more than highest fd for which * Tcl_WatchFile has been called). */ #ifdef TCL_THREADS int onList; /* True if it is in this list */ - unsigned int pollState; /* pollState is used to implement a polling + unsigned int pollState; /* pollState is used to implement a polling * handshake between each thread and the * notifier thread. Bits defined below. */ struct ThreadSpecificData *nextPtr, *prevPtr; - /* All threads that are currently waiting on - * an event have their ThreadSpecificData - * structure on a doubly-linked listed formed - * from these pointers. You must hold the - * notifierMutex lock before accessing these - * fields. */ + /* All threads that are currently waiting on + * an event have their ThreadSpecificData + * structure on a doubly-linked listed formed + * from these pointers. You must hold the + * notifierMutex lock before accessing these + * fields. */ #ifdef __CYGWIN__ void *event; /* Any other thread alerts a notifier * that an event is ready to be processed * by sending this event. */ void *hwnd; /* Messaging window. */ #else /* !__CYGWIN__ */ - Tcl_Condition waitCV; /* Any other thread alerts a notifier - * that an event is ready to be processed - * by signaling this condition variable. */ + Tcl_Condition waitCV; /* Any other thread alerts a notifier that an + * event is ready to be processed by signaling + * this condition variable. */ #endif /* __CYGWIN__ */ - int eventReady; /* True if an event is ready to be processed. - * Used as condition flag together with - * waitCV above. */ + int eventReady; /* True if an event is ready to be processed. + * Used as condition flag together with waitCV + * above. */ #endif } ThreadSpecificData; @@ -118,8 +119,8 @@ static Tcl_ThreadDataKey dataKey; #ifdef TCL_THREADS /* - * The following static indicates the number of threads that have - * initialized notifiers. + * The following static indicates the number of threads that have initialized + * notifiers. * * You must hold the notifierMutex lock before accessing this variable. */ @@ -127,9 +128,9 @@ static Tcl_ThreadDataKey dataKey; static int notifierCount = 0; /* - * The following variable points to the head of a doubly-linked list of - * of ThreadSpecificData structures for all threads that are currently - * waiting on an event. + * The following variable points to the head of a doubly-linked list of + * ThreadSpecificData structures for all threads that are currently waiting on + * an event. * * You must hold the notifierMutex lock before accessing this list. */ @@ -137,32 +138,31 @@ static int notifierCount = 0; static ThreadSpecificData *waitingListPtr = NULL; /* - * The notifier thread spends all its time in select() waiting for a - * file descriptor associated with one of the threads on the waitingListPtr - * list to do something interesting. But if the contents of the - * waitingListPtr list ever changes, we need to wake up and restart - * the select() system call. You can wake up the notifier thread by - * writing a single byte to the file descriptor defined below. This - * file descriptor is the input-end of a pipe and the notifier thread is - * listening for data on the output-end of the same pipe. Hence writing - * to this file descriptor will cause the select() system call to return - * and wake up the notifier thread. + * The notifier thread spends all its time in select() waiting for a file + * descriptor associated with one of the threads on the waitingListPtr list to + * do something interesting. But if the contents of the waitingListPtr list + * ever changes, we need to wake up and restart the select() system call. You + * can wake up the notifier thread by writing a single byte to the file + * descriptor defined below. This file descriptor is the input-end of a pipe + * and the notifier thread is listening for data on the output-end of the same + * pipe. Hence writing to this file descriptor will cause the select() system + * call to return and wake up the notifier thread. * - * You must hold the notifierMutex lock before accessing this list. + * You must hold the notifierMutex lock before writing to the pipe. */ static int triggerPipe = -1; /* - * The notifierMutex locks access to all of the global notifier state. + * The notifierMutex locks access to all of the global notifier state. */ TCL_DECLARE_MUTEX(notifierMutex) /* * The notifier thread signals the notifierCV when it has finished - * initializing the triggerPipe and right before the notifier - * thread terminates. + * initializing the triggerPipe and right before the notifier thread + * terminates. */ static Tcl_Condition notifierCV; @@ -170,18 +170,19 @@ static Tcl_Condition notifierCV; /* * The pollState bits * POLL_WANT is set by each thread before it waits on its condition - * variable. It is checked by the notifier before it does - * select. - * POLL_DONE is set by the notifier if it goes into select after - * seeing POLL_WANT. The idea is to ensure it tries a select - * with the same bits the initial thread had set. + * variable. It is checked by the notifier before it does select. + * POLL_DONE is set by the notifier if it goes into select after seeing + * POLL_WANT. The idea is to ensure it tries a select with the + * same bits the initial thread had set. */ + #define POLL_WANT 0x1 #define POLL_DONE 0x2 /* * This is the thread ID of the notifier thread that does select. */ + static Tcl_ThreadId notifierThread; #endif @@ -191,10 +192,9 @@ static Tcl_ThreadId notifierThread; */ #ifdef TCL_THREADS -static void NotifierThreadProc _ANSI_ARGS_((ClientData clientData)); +static void NotifierThreadProc(ClientData clientData); #endif -static int FileHandlerEventProc _ANSI_ARGS_((Tcl_Event *evPtr, - int flags)); +static int FileHandlerEventProc(Tcl_Event *evPtr, int flags); /* *---------------------------------------------------------------------- @@ -204,7 +204,7 @@ static int FileHandlerEventProc _ANSI_ARGS_((Tcl_Event *evPtr, * Initializes the platform specific notifier state. * * Results: - * Returns a handle to the notifier state for this thread.. + * Returns a handle to the notifier state for this thread. * * Side effects: * None. @@ -255,7 +255,7 @@ extern unsigned char __stdcall ResetEvent(void *); #endif ClientData -Tcl_InitNotifier() +Tcl_InitNotifier(void) { ThreadSpecificData *tsdPtr = TCL_TSD_INIT(&dataKey); @@ -269,8 +269,8 @@ Tcl_InitNotifier() Tcl_MutexLock(¬ifierMutex); if (notifierCount == 0) { if (TclpThreadCreate(¬ifierThread, NotifierThreadProc, NULL, - TCL_THREAD_STACK_DEFAULT, TCL_THREAD_JOINABLE) != TCL_OK) { - panic("Tcl_InitNotifier: unable to start notifier thread"); + TCL_THREAD_STACK_DEFAULT, TCL_THREAD_JOINABLE) != TCL_OK) { + Tcl_Panic("Tcl_InitNotifier: unable to start notifier thread"); } } notifierCount++; @@ -293,22 +293,22 @@ Tcl_InitNotifier() * * Tcl_FinalizeNotifier -- * - * This function is called to cleanup the notifier state before - * a thread is terminated. + * This function is called to cleanup the notifier state before a thread + * is terminated. * * Results: * None. * * Side effects: - * May terminate the background notifier thread if this is the - * last notifier instance. + * May terminate the background notifier thread if this is the last + * notifier instance. * *---------------------------------------------------------------------- */ void -Tcl_FinalizeNotifier(clientData) - ClientData clientData; /* Not used. */ +Tcl_FinalizeNotifier( + ClientData clientData) /* Not used. */ { #ifdef TCL_THREADS ThreadSpecificData *tsdPtr = TCL_TSD_INIT(&dataKey); @@ -317,32 +317,36 @@ Tcl_FinalizeNotifier(clientData) notifierCount--; /* - * If this is the last thread to use the notifier, close the notifier - * pipe and wait for the background thread to terminate. + * If this is the last thread to use the notifier, close the notifier pipe + * and wait for the background thread to terminate. */ if (notifierCount == 0) { int result; + if (triggerPipe < 0) { - panic("Tcl_FinalizeNotifier: notifier pipe not initialized"); + Tcl_Panic("Tcl_FinalizeNotifier: notifier pipe not initialized"); } /* - * Send "q" message to the notifier thread so that it will - * terminate. The notifier will return from its call to select() - * and notice that a "q" message has arrived, it will then close - * its side of the pipe and terminate its thread. Note the we can - * not just close the pipe and check for EOF in the notifier - * thread because if a background child process was created with - * exec, select() would not register the EOF on the pipe until the - * child processes had terminated. [Bug: 4139] [Bug: 1222872] + * Send "q" message to the notifier thread so that it will terminate. + * The notifier will return from its call to select() and notice that + * a "q" message has arrived, it will then close its side of the pipe + * and terminate its thread. Note the we can not just close the pipe + * and check for EOF in the notifier thread because if a background + * child process was created with exec, select() would not register + * the EOF on the pipe until the child processes had terminated. [Bug: + * 4139] [Bug: 1222872] */ - write(triggerPipe, "q", 1); + if (write(triggerPipe, "q", 1) != 1) { + Tcl_Panic("Tcl_FinalizeNotifier: unable to write q to triggerPipe"); + } close(triggerPipe); while(triggerPipe >= 0) { Tcl_ConditionWait(¬ifierCV, ¬ifierMutex, NULL); } + result = Tcl_JoinThread(notifierThread, NULL); if (result) { Tcl_Panic("Tcl_FinalizeNotifier: unable to join notifier thread"); @@ -368,25 +372,23 @@ Tcl_FinalizeNotifier(clientData) * * Tcl_AlertNotifier -- * - * Wake up the specified notifier from any thread. This routine - * is called by the platform independent notifier code whenever - * the Tcl_ThreadAlert routine is called. This routine is - * guaranteed not to be called on a given notifier after - * Tcl_FinalizeNotifier is called for that notifier. + * Wake up the specified notifier from any thread. This routine is called + * by the platform independent notifier code whenever the Tcl_ThreadAlert + * routine is called. This routine is guaranteed not to be called on a + * given notifier after Tcl_FinalizeNotifier is called for that notifier. * * Results: * None. * * Side effects: - * Signals the notifier condition variable for the specified - * notifier. + * Signals the notifier condition variable for the specified notifier. * *---------------------------------------------------------------------- */ void -Tcl_AlertNotifier(clientData) - ClientData clientData; +Tcl_AlertNotifier( + ClientData clientData) { #ifdef TCL_THREADS ThreadSpecificData *tsdPtr = (ThreadSpecificData *) clientData; @@ -406,9 +408,9 @@ Tcl_AlertNotifier(clientData) * * Tcl_SetTimer -- * - * This procedure sets the current notifier timer value. This - * interface is not implemented in this notifier because we are - * always running inside of Tcl_DoOneEvent. + * This function sets the current notifier timer value. This interface is + * not implemented in this notifier because we are always running inside + * of Tcl_DoOneEvent. * * Results: * None. @@ -420,13 +422,13 @@ Tcl_AlertNotifier(clientData) */ void -Tcl_SetTimer(timePtr) - Tcl_Time *timePtr; /* Timeout value, may be NULL. */ +Tcl_SetTimer( + Tcl_Time *timePtr) /* Timeout value, may be NULL. */ { /* - * The interval timer doesn't do anything in this implementation, - * because the only event loop is via Tcl_DoOneEvent, which passes - * timeout values to Tcl_WaitForEvent. + * The interval timer doesn't do anything in this implementation, because + * the only event loop is via Tcl_DoOneEvent, which passes timeout values + * to Tcl_WaitForEvent. */ if (tclStubs.tcl_SetTimer != tclOriginalNotifier.setTimerProc) { @@ -451,8 +453,8 @@ Tcl_SetTimer(timePtr) */ void -Tcl_ServiceModeHook(mode) - int mode; /* Either TCL_SERVICE_ALL, or +Tcl_ServiceModeHook( + int mode) /* Either TCL_SERVICE_ALL, or * TCL_SERVICE_NONE. */ { } @@ -462,7 +464,7 @@ Tcl_ServiceModeHook(mode) * * Tcl_CreateFileHandler -- * - * This procedure registers a file handler with the select notifier. + * This function registers a file handler with the select notifier. * * Results: * None. @@ -474,26 +476,27 @@ Tcl_ServiceModeHook(mode) */ void -Tcl_CreateFileHandler(fd, mask, proc, clientData) - int fd; /* Handle of stream to watch. */ - int mask; /* OR'ed combination of TCL_READABLE, - * TCL_WRITABLE, and TCL_EXCEPTION: - * indicates conditions under which - * proc should be called. */ - Tcl_FileProc *proc; /* Procedure to call for each - * selected event. */ - ClientData clientData; /* Arbitrary data to pass to proc. */ +Tcl_CreateFileHandler( + int fd, /* Handle of stream to watch. */ + int mask, /* OR'ed combination of TCL_READABLE, + * TCL_WRITABLE, and TCL_EXCEPTION: indicates + * conditions under which proc should be + * called. */ + Tcl_FileProc *proc, /* Function to call for each selected + * event. */ + ClientData clientData) /* Arbitrary data to pass to proc. */ { ThreadSpecificData *tsdPtr = TCL_TSD_INIT(&dataKey); FileHandler *filePtr; - if (tclStubs.tcl_CreateFileHandler != tclOriginalNotifier.createFileHandlerProc) { + if (tclStubs.tcl_CreateFileHandler != + tclOriginalNotifier.createFileHandlerProc) { tclStubs.tcl_CreateFileHandler(fd, mask, proc, clientData); return; } for (filePtr = tsdPtr->firstFileHandlerPtr; filePtr != NULL; - filePtr = filePtr->nextPtr) { + filePtr = filePtr->nextPtr) { if (filePtr->fd == fd) { break; } @@ -513,20 +516,20 @@ Tcl_CreateFileHandler(fd, mask, proc, clientData) * Update the check masks for this file. */ - if ( mask & TCL_READABLE ) { - FD_SET( fd, &(tsdPtr->checkMasks.readable) ); + if (mask & TCL_READABLE) { + FD_SET(fd, &(tsdPtr->checkMasks.readable)); } else { - FD_CLR( fd, &(tsdPtr->checkMasks.readable) ); + FD_CLR(fd, &(tsdPtr->checkMasks.readable)); } - if ( mask & TCL_WRITABLE ) { - FD_SET( fd, &(tsdPtr->checkMasks.writable) ); + if (mask & TCL_WRITABLE) { + FD_SET(fd, &(tsdPtr->checkMasks.writable)); } else { - FD_CLR( fd, &(tsdPtr->checkMasks.writable) ); + FD_CLR(fd, &(tsdPtr->checkMasks.writable)); } - if ( mask & TCL_EXCEPTION ) { - FD_SET( fd, &(tsdPtr->checkMasks.exceptional) ); + if (mask & TCL_EXCEPTION) { + FD_SET(fd, &(tsdPtr->checkMasks.exceptional)); } else { - FD_CLR( fd, &(tsdPtr->checkMasks.exceptional) ); + FD_CLR(fd, &(tsdPtr->checkMasks.exceptional)); } if (tsdPtr->numFdBits <= fd) { tsdPtr->numFdBits = fd+1; @@ -538,8 +541,7 @@ Tcl_CreateFileHandler(fd, mask, proc, clientData) * * Tcl_DeleteFileHandler -- * - * Cancel a previously-arranged callback arrangement for - * a file. + * Cancel a previously-arranged callback arrangement for a file. * * Results: * None. @@ -551,14 +553,16 @@ Tcl_CreateFileHandler(fd, mask, proc, clientData) */ void -Tcl_DeleteFileHandler(fd) - int fd; /* Stream id for which to remove callback procedure. */ +Tcl_DeleteFileHandler( + int fd) /* Stream id for which to remove callback + * function. */ { FileHandler *filePtr, *prevPtr; int i; ThreadSpecificData *tsdPtr = TCL_TSD_INIT(&dataKey); - if (tclStubs.tcl_DeleteFileHandler != tclOriginalNotifier.deleteFileHandlerProc) { + if (tclStubs.tcl_DeleteFileHandler != + tclOriginalNotifier.deleteFileHandlerProc) { tclStubs.tcl_DeleteFileHandler(fd); return; } @@ -582,13 +586,13 @@ Tcl_DeleteFileHandler(fd) */ if (filePtr->mask & TCL_READABLE) { - FD_CLR( fd, &(tsdPtr->checkMasks.readable) ); + FD_CLR(fd, &(tsdPtr->checkMasks.readable)); } if (filePtr->mask & TCL_WRITABLE) { - FD_CLR( fd, &(tsdPtr->checkMasks.writable) ); + FD_CLR(fd, &(tsdPtr->checkMasks.writable)); } if (filePtr->mask & TCL_EXCEPTION) { - FD_CLR( fd, &(tsdPtr->checkMasks.exceptional) ); + FD_CLR(fd, &(tsdPtr->checkMasks.exceptional)); } /* @@ -596,15 +600,17 @@ Tcl_DeleteFileHandler(fd) */ if (fd+1 == tsdPtr->numFdBits) { - tsdPtr->numFdBits = 0; + int numFdBits = 0; + for (i = fd-1; i >= 0; i--) { - if ( FD_ISSET( i, &(tsdPtr->checkMasks.readable) ) - || FD_ISSET( i, &(tsdPtr->checkMasks.writable) ) - || FD_ISSET( i, &(tsdPtr->checkMasks.exceptional ) ) ) { - tsdPtr->numFdBits = i+1; + if (FD_ISSET(i, &(tsdPtr->checkMasks.readable)) + || FD_ISSET(i, &(tsdPtr->checkMasks.writable)) + || FD_ISSET(i, &(tsdPtr->checkMasks.exceptional))) { + numFdBits = i+1; break; } } + tsdPtr->numFdBits = numFdBits; } /* @@ -624,28 +630,28 @@ Tcl_DeleteFileHandler(fd) * * FileHandlerEventProc -- * - * This procedure is called by Tcl_ServiceEvent when a file event - * reaches the front of the event queue. This procedure is - * responsible for actually handling the event by invoking the - * callback for the file handler. + * This function is called by Tcl_ServiceEvent when a file event reaches + * the front of the event queue. This function is responsible for + * actually handling the event by invoking the callback for the file + * handler. * * Results: - * Returns 1 if the event was handled, meaning it should be removed - * from the queue. Returns 0 if the event was not handled, meaning - * it should stay on the queue. The only time the event isn't - * handled is if the TCL_FILE_EVENTS flag bit isn't set. + * Returns 1 if the event was handled, meaning it should be removed from + * the queue. Returns 0 if the event was not handled, meaning it should + * stay on the queue. The only time the event isn't handled is if the + * TCL_FILE_EVENTS flag bit isn't set. * * Side effects: - * Whatever the file handler's callback procedure does. + * Whatever the file handler's callback function does. * *---------------------------------------------------------------------- */ static int -FileHandlerEventProc(evPtr, flags) - Tcl_Event *evPtr; /* Event to service. */ - int flags; /* Flags that indicate what events to - * handle, such as TCL_FILE_EVENTS. */ +FileHandlerEventProc( + Tcl_Event *evPtr, /* Event to service. */ + int flags) /* Flags that indicate what events to handle, + * such as TCL_FILE_EVENTS. */ { int mask; FileHandler *filePtr; @@ -658,28 +664,28 @@ FileHandlerEventProc(evPtr, flags) /* * Search through the file handlers to find the one whose handle matches - * the event. We do this rather than keeping a pointer to the file - * handler directly in the event, so that the handler can be deleted - * while the event is queued without leaving a dangling pointer. + * the event. We do this rather than keeping a pointer to the file handler + * directly in the event, so that the handler can be deleted while the + * event is queued without leaving a dangling pointer. */ tsdPtr = TCL_TSD_INIT(&dataKey); for (filePtr = tsdPtr->firstFileHandlerPtr; filePtr != NULL; - filePtr = filePtr->nextPtr) { + filePtr = filePtr->nextPtr) { if (filePtr->fd != fileEvPtr->fd) { continue; } /* * The code is tricky for two reasons: - * 1. The file handler's desired events could have changed - * since the time when the event was queued, so AND the - * ready mask with the desired mask. - * 2. The file could have been closed and re-opened since - * the time when the event was queued. This is why the - * ready mask is stored in the file handler rather than - * the queued event: it will be zeroed when a new - * file handler is created for the newly opened file. + * 1. The file handler's desired events could have changed since the + * time when the event was queued, so AND the ready mask with the + * desired mask. + * 2. The file could have been closed and re-opened since the time + * when the event was queued. This is why the ready mask is stored + * in the file handler rather than the queued event: it will be + * zeroed when a new file handler is created for the newly opened + * file. */ mask = filePtr->readyMask & filePtr->mask; @@ -722,13 +728,12 @@ NotifierProc( * * Tcl_WaitForEvent -- * - * This function is called by Tcl_DoOneEvent to wait for new - * events on the message queue. If the block time is 0, then - * Tcl_WaitForEvent just polls without blocking. + * This function is called by Tcl_DoOneEvent to wait for new events on + * the message queue. If the block time is 0, then Tcl_WaitForEvent just + * polls without blocking. * * Results: - * Returns -1 if the select would block forever, otherwise - * returns 0. + * Returns -1 if the select would block forever, otherwise returns 0. * * Side effects: * Queues file events that are detected by the select. @@ -737,56 +742,69 @@ NotifierProc( */ int -Tcl_WaitForEvent(timePtr) - Tcl_Time *timePtr; /* Maximum block time, or NULL. */ +Tcl_WaitForEvent( + Tcl_Time *timePtr) /* Maximum block time, or NULL. */ { FileHandler *filePtr; FileHandlerEvent *fileEvPtr; int mask; + Tcl_Time vTime; #ifdef TCL_THREADS int waitForFiles; # ifdef __CYGWIN__ MSG msg; # endif #else - /* Impl. notes: timeout & timeoutPtr are used if, and only if - * threads are not enabled. They are the arguments for the regular - * select() used when the core is not thread-enabled. */ + /* + * Impl. notes: timeout & timeoutPtr are used if, and only if threads are + * not enabled. They are the arguments for the regular select() used when + * the core is not thread-enabled. + */ struct timeval timeout, *timeoutPtr; int numFound; -#endif +#endif /* TCL_THREADS */ ThreadSpecificData *tsdPtr = TCL_TSD_INIT(&dataKey); if (tclStubs.tcl_WaitForEvent != tclOriginalNotifier.waitForEventProc) { return tclStubs.tcl_WaitForEvent(timePtr); } -#ifndef TCL_THREADS /* - * Set up the timeout structure. Note that if there are no events to - * check for, we return with a negative result rather than blocking - * forever. + * Set up the timeout structure. Note that if there are no events to check + * for, we return with a negative result rather than blocking forever. */ - if (timePtr) { + if (timePtr != NULL) { + /* + * TIP #233 (Virtualized Time). Is virtual time in effect? And do we + * actually have something to scale? If yes to both then we call the + * handler to do this scaling. + */ + + if (timePtr->sec != 0 || timePtr->usec != 0) { + vTime = *timePtr; + (*tclScaleTimeProcPtr) (&vTime, tclTimeClientData); + timePtr = &vTime; + } +#ifndef TCL_THREADS timeout.tv_sec = timePtr->sec; timeout.tv_usec = timePtr->usec; timeoutPtr = &timeout; } else if (tsdPtr->numFdBits == 0) { /* - * If there are no threads, no timeout, and no fds registered, - * then there are no events possible and we must avoid deadlock. - * Note that this is not entirely correct because there might - * be a signal that could interrupt the select call, but we - * don't handle that case if we aren't using threads. + * If there are no threads, no timeout, and no fds registered, then + * there are no events possible and we must avoid deadlock. Note that + * this is not entirely correct because there might be a signal that + * could interrupt the select call, but we don't handle that case if + * we aren't using threads. */ return -1; } else { timeoutPtr = NULL; +#endif /* TCL_THREADS */ } -#endif #ifdef TCL_THREADS /* @@ -796,7 +814,6 @@ Tcl_WaitForEvent(timePtr) Tcl_MutexLock(¬ifierMutex); - waitForFiles = (tsdPtr->numFdBits > 0); if (timePtr != NULL && timePtr->sec == 0 && (timePtr->usec == 0 #if defined(__APPLE__) && defined(__LP64__) /* @@ -810,16 +827,17 @@ Tcl_WaitForEvent(timePtr) )) { /* * Cannot emulate a polling select with a polling condition variable. - * Instead, pretend to wait for files and tell the notifier - * thread what we are doing. The notifier thread makes sure - * it goes through select with its select mask in the same state - * as ours currently is. We block until that happens. + * Instead, pretend to wait for files and tell the notifier thread + * what we are doing. The notifier thread makes sure it goes through + * select with its select mask in the same state as ours currently is. + * We block until that happens. */ waitForFiles = 1; tsdPtr->pollState = POLL_WANT; timePtr = NULL; } else { + waitForFiles = (tsdPtr->numFdBits > 0); tsdPtr->pollState = 0; } @@ -847,27 +865,28 @@ Tcl_WaitForEvent(timePtr) #endif if (waitForFiles) { - /* - * Add the ThreadSpecificData structure of this thread to the list - * of ThreadSpecificData structures of all threads that are waiting - * on file events. - */ - - - tsdPtr->nextPtr = waitingListPtr; - if (waitingListPtr) { - waitingListPtr->prevPtr = tsdPtr; - } - tsdPtr->prevPtr = 0; - waitingListPtr = tsdPtr; + /* + * Add the ThreadSpecificData structure of this thread to the list of + * ThreadSpecificData structures of all threads that are waiting on + * file events. + */ + + tsdPtr->nextPtr = waitingListPtr; + if (waitingListPtr) { + waitingListPtr->prevPtr = tsdPtr; + } + tsdPtr->prevPtr = 0; + waitingListPtr = tsdPtr; tsdPtr->onList = 1; - - write(triggerPipe, "", 1); + + if ((write(triggerPipe, "", 1) == -1) && (errno != EAGAIN)) { + Tcl_Panic("Tcl_WaitForEvent: unable to write to triggerPipe"); + } } - FD_ZERO( &(tsdPtr->readyMasks.readable) ); - FD_ZERO( &(tsdPtr->readyMasks.writable) ); - FD_ZERO( &(tsdPtr->readyMasks.exceptional) ); + FD_ZERO(&(tsdPtr->readyMasks.readable)); + FD_ZERO(&(tsdPtr->readyMasks.writable)); + FD_ZERO(&(tsdPtr->readyMasks.exceptional)); if (!tsdPtr->eventReady) { #ifdef __CYGWIN__ @@ -908,60 +927,59 @@ Tcl_WaitForEvent(timePtr) if (waitForFiles && tsdPtr->onList) { /* * Remove the ThreadSpecificData structure of this thread from the - * waiting list. Alert the notifier thread to recompute its select + * waiting list. Alert the notifier thread to recompute its select * masks - skipping this caused a hang when trying to close a pipe * which the notifier thread was still doing a select on. */ - if (tsdPtr->prevPtr) { - tsdPtr->prevPtr->nextPtr = tsdPtr->nextPtr; - } else { - waitingListPtr = tsdPtr->nextPtr; - } - if (tsdPtr->nextPtr) { - tsdPtr->nextPtr->prevPtr = tsdPtr->prevPtr; - } - tsdPtr->nextPtr = tsdPtr->prevPtr = NULL; + if (tsdPtr->prevPtr) { + tsdPtr->prevPtr->nextPtr = tsdPtr->nextPtr; + } else { + waitingListPtr = tsdPtr->nextPtr; + } + if (tsdPtr->nextPtr) { + tsdPtr->nextPtr->prevPtr = tsdPtr->prevPtr; + } + tsdPtr->nextPtr = tsdPtr->prevPtr = NULL; tsdPtr->onList = 0; - write(triggerPipe, "", 1); + if ((write(triggerPipe, "", 1) == -1) && (errno != EAGAIN)) { + Tcl_Panic("Tcl_WaitForEvent: unable to write to triggerPipe"); + } } - #else tsdPtr->readyMasks = tsdPtr->checkMasks; - numFound = select( tsdPtr->numFdBits, - &(tsdPtr->readyMasks.readable), - &(tsdPtr->readyMasks.writable), - &(tsdPtr->readyMasks.exceptional), - timeoutPtr ); + numFound = select(tsdPtr->numFdBits, &(tsdPtr->readyMasks.readable), + &(tsdPtr->readyMasks.writable), &(tsdPtr->readyMasks.exceptional), + timeoutPtr); /* - * Some systems don't clear the masks after an error, so - * we have to do it here. + * Some systems don't clear the masks after an error, so we have to do it + * here. */ if (numFound == -1) { - FD_ZERO( &(tsdPtr->readyMasks.readable ) ); - FD_ZERO( &(tsdPtr->readyMasks.writable ) ); - FD_ZERO( &(tsdPtr->readyMasks.exceptional ) ); + FD_ZERO(&(tsdPtr->readyMasks.readable)); + FD_ZERO(&(tsdPtr->readyMasks.writable)); + FD_ZERO(&(tsdPtr->readyMasks.exceptional)); } -#endif +#endif /* TCL_THREADS */ /* * Queue all detected file events before returning. */ for (filePtr = tsdPtr->firstFileHandlerPtr; (filePtr != NULL); - filePtr = filePtr->nextPtr) { + filePtr = filePtr->nextPtr) { mask = 0; - if ( FD_ISSET( filePtr->fd, &(tsdPtr->readyMasks.readable) ) ) { + if (FD_ISSET(filePtr->fd, &(tsdPtr->readyMasks.readable))) { mask |= TCL_READABLE; } - if ( FD_ISSET( filePtr->fd, &(tsdPtr->readyMasks.writable) ) ) { + if (FD_ISSET(filePtr->fd, &(tsdPtr->readyMasks.writable))) { mask |= TCL_WRITABLE; } - if ( FD_ISSET( filePtr->fd, &(tsdPtr->readyMasks.exceptional) ) ) { + if (FD_ISSET(filePtr->fd, &(tsdPtr->readyMasks.exceptional))) { mask |= TCL_EXCEPTION; } @@ -970,13 +988,12 @@ Tcl_WaitForEvent(timePtr) } /* - * Don't bother to queue an event if the mask was previously - * non-zero since an event must still be on the queue. + * Don't bother to queue an event if the mask was previously non-zero + * since an event must still be on the queue. */ if (filePtr->readyMask == 0) { - fileEvPtr = (FileHandlerEvent *) ckalloc( - sizeof(FileHandlerEvent)); + fileEvPtr = (FileHandlerEvent *) ckalloc(sizeof(FileHandlerEvent)); fileEvPtr->header.proc = FileHandlerEventProc; fileEvPtr->fd = filePtr->fd; Tcl_QueueEvent((Tcl_Event *) fileEvPtr, TCL_QUEUE_TAIL); @@ -985,7 +1002,7 @@ Tcl_WaitForEvent(timePtr) } #ifdef TCL_THREADS Tcl_MutexUnlock(¬ifierMutex); -#endif +#endif /* TCL_THREADS */ return 0; } @@ -996,64 +1013,56 @@ Tcl_WaitForEvent(timePtr) * NotifierThreadProc -- * * This routine is the initial (and only) function executed by the - * special notifier thread. Its job is to wait for file descriptors - * to become readable or writable or to have an exception condition - * and then to notify other threads who are interested in this - * information by signalling a condition variable. Other threads - * can signal this notifier thread of a change in their interests - * by writing a single byte to a special pipe that the notifier - * thread is monitoring. + * special notifier thread. Its job is to wait for file descriptors to + * become readable or writable or to have an exception condition and then + * to notify other threads who are interested in this information by + * signalling a condition variable. Other threads can signal this + * notifier thread of a change in their interests by writing a single + * byte to a special pipe that the notifier thread is monitoring. * * Result: - * None. Once started, this routine never exits. It dies with - * the overall process. + * None. Once started, this routine never exits. It dies with the overall + * process. * * Side effects: - * The trigger pipe used to signal the notifier thread is created - * when the notifier thread first starts. + * The trigger pipe used to signal the notifier thread is created when + * the notifier thread first starts. * *---------------------------------------------------------------------- */ static void -NotifierThreadProc(clientData) - ClientData clientData; /* Not used. */ +NotifierThreadProc( + ClientData clientData) /* Not used. */ { ThreadSpecificData *tsdPtr; fd_set readableMask; fd_set writableMask; fd_set exceptionalMask; int fds[2]; - int i, status, numFdBits = 0, receivePipe; + int i, numFdBits = 0, receivePipe; long found; struct timeval poll = {0., 0.}, *timePtr; char buf[2]; if (pipe(fds) != 0) { - panic("NotifierThreadProc: could not create trigger pipe."); + Tcl_Panic("NotifierThreadProc: could not create trigger pipe"); } receivePipe = fds[0]; -#ifndef USE_FIONBIO - status = fcntl(receivePipe, F_GETFL); - status |= O_NONBLOCK; - if (fcntl(receivePipe, F_SETFL, status) < 0) { - panic("NotifierThreadProc: could not make receive pipe non blocking."); + if (TclUnixSetBlockingMode(receivePipe, TCL_MODE_NONBLOCKING) < 0) { + Tcl_Panic("NotifierThreadProc: could not make receive pipe non blocking"); } - status = fcntl(fds[1], F_GETFL); - status |= O_NONBLOCK; - if (fcntl(fds[1], F_SETFL, status) < 0) { - panic("NotifierThreadProc: could not make trigger pipe non blocking."); + if (TclUnixSetBlockingMode(fds[1], TCL_MODE_NONBLOCKING) < 0) { + Tcl_Panic("NotifierThreadProc: could not make trigger pipe non blocking"); } -#else - if (ioctl(receivePipe, (int) FIONBIO, &status) < 0) { - panic("NotifierThreadProc: could not make receive pipe non blocking."); + if (fcntl(receivePipe, F_SETFD, FD_CLOEXEC) < 0) { + Tcl_Panic("NotifierThreadProc: could not make receive pipe close-on-exec"); } - if (ioctl(fds[1], (int) FIONBIO, &status) < 0) { - panic("NotifierThreadProc: could not make trigger pipe non blocking."); + if (fcntl(fds[1], F_SETFD, FD_CLOEXEC) < 0) { + Tcl_Panic("NotifierThreadProc: could not make trigger pipe close-on-exec"); } -#endif /* * Install the write end of the pipe into the global variable. @@ -1074,37 +1083,36 @@ NotifierThreadProc(clientData) */ while (1) { - - FD_ZERO( &readableMask ); - FD_ZERO( &writableMask ); - FD_ZERO( &exceptionalMask ); + FD_ZERO(&readableMask); + FD_ZERO(&writableMask); + FD_ZERO(&exceptionalMask); /* - * Compute the logical OR of the select masks from all the - * waiting notifiers. + * Compute the logical OR of the select masks from all the waiting + * notifiers. */ Tcl_MutexLock(¬ifierMutex); timePtr = NULL; - for (tsdPtr = waitingListPtr; tsdPtr; tsdPtr = tsdPtr->nextPtr) { - for ( i = tsdPtr->numFdBits-1; i >= 0; --i ) { - if ( FD_ISSET( i, &(tsdPtr->checkMasks.readable) ) ) { - FD_SET( i, &readableMask ); + for (tsdPtr = waitingListPtr; tsdPtr; tsdPtr = tsdPtr->nextPtr) { + for (i = tsdPtr->numFdBits-1; i >= 0; --i) { + if (FD_ISSET(i, &(tsdPtr->checkMasks.readable))) { + FD_SET(i, &readableMask); } - if ( FD_ISSET( i, &(tsdPtr->checkMasks.writable) ) ) { - FD_SET( i, &writableMask ); + if (FD_ISSET(i, &(tsdPtr->checkMasks.writable))) { + FD_SET(i, &writableMask); } - if ( FD_ISSET( i, &(tsdPtr->checkMasks.exceptional) ) ) { - FD_SET( i, &exceptionalMask ); + if (FD_ISSET(i, &(tsdPtr->checkMasks.exceptional))) { + FD_SET(i, &exceptionalMask); } } - if ( tsdPtr->numFdBits > numFdBits ) { + if (tsdPtr->numFdBits > numFdBits) { numFdBits = tsdPtr->numFdBits; } if (tsdPtr->pollState & POLL_WANT) { /* - * Here we make sure we go through select() with the same - * mask bits that were present when the thread tried to poll. + * Here we make sure we go through select() with the same mask + * bits that were present when the thread tried to poll. */ tsdPtr->pollState |= POLL_DONE; @@ -1117,54 +1125,54 @@ NotifierThreadProc(clientData) * Set up the select mask to include the receive pipe. */ - if ( receivePipe >= numFdBits ) { + if (receivePipe >= numFdBits) { numFdBits = receivePipe + 1; } - FD_SET( receivePipe, &readableMask ); + FD_SET(receivePipe, &readableMask); - if ( select( numFdBits, &readableMask, &writableMask, - &exceptionalMask, timePtr) == -1 ) { + if (select(numFdBits, &readableMask, &writableMask, &exceptionalMask, + timePtr) == -1) { /* * Try again immediately on an error. */ continue; - } + } /* * Alert any threads that are waiting on a ready file descriptor. */ Tcl_MutexLock(¬ifierMutex); - for (tsdPtr = waitingListPtr; tsdPtr; tsdPtr = tsdPtr->nextPtr) { + for (tsdPtr = waitingListPtr; tsdPtr; tsdPtr = tsdPtr->nextPtr) { found = 0; - for ( i = tsdPtr->numFdBits-1; i >= 0; --i ) { - if ( FD_ISSET( i, &(tsdPtr->checkMasks.readable) ) - && FD_ISSET( i, &readableMask ) ) { - FD_SET( i, &(tsdPtr->readyMasks.readable) ); + for (i = tsdPtr->numFdBits-1; i >= 0; --i) { + if (FD_ISSET(i, &(tsdPtr->checkMasks.readable)) + && FD_ISSET(i, &readableMask)) { + FD_SET(i, &(tsdPtr->readyMasks.readable)); found = 1; } - if ( FD_ISSET( i, &(tsdPtr->checkMasks.writable) ) - && FD_ISSET( i, &writableMask ) ) { - FD_SET( i, &(tsdPtr->readyMasks.writable) ); + if (FD_ISSET(i, &(tsdPtr->checkMasks.writable)) + && FD_ISSET(i, &writableMask)) { + FD_SET(i, &(tsdPtr->readyMasks.writable)); found = 1; } - if ( FD_ISSET( i, &(tsdPtr->checkMasks.exceptional) ) - && FD_ISSET( i, &exceptionalMask ) ) { - FD_SET( i, &(tsdPtr->readyMasks.exceptional) ); + if (FD_ISSET(i, &(tsdPtr->checkMasks.exceptional)) + && FD_ISSET(i, &exceptionalMask)) { + FD_SET(i, &(tsdPtr->readyMasks.exceptional)); found = 1; } } - if (found || (tsdPtr->pollState & POLL_DONE)) { - tsdPtr->eventReady = 1; + if (found || (tsdPtr->pollState & POLL_DONE)) { + tsdPtr->eventReady = 1; if (tsdPtr->onList) { /* - * Remove the ThreadSpecificData structure of this - * thread from the waiting list. This prevents us from - * continuously spining on select until the other - * threads runs and services the file event. + * Remove the ThreadSpecificData structure of this thread + * from the waiting list. This prevents us from + * continuously spining on select until the other threads + * runs and services the file event. */ if (tsdPtr->prevPtr) { @@ -1182,26 +1190,26 @@ NotifierThreadProc(clientData) #ifdef __CYGWIN__ PostMessageW(tsdPtr->hwnd, 1024, 0, 0); #else /* __CYGWIN__ */ - Tcl_ConditionNotify(&tsdPtr->waitCV); + Tcl_ConditionNotify(&tsdPtr->waitCV); #endif /* __CYGWIN__ */ - } - } + } + } Tcl_MutexUnlock(¬ifierMutex); /* * Consume the next byte from the notifier pipe if the pipe was - * readable. Note that there may be multiple bytes pending, but - * to avoid a race condition we only read one at a time. + * readable. Note that there may be multiple bytes pending, but to + * avoid a race condition we only read one at a time. */ - if ( FD_ISSET( receivePipe, &readableMask ) ) { + if (FD_ISSET(receivePipe, &readableMask)) { i = read(receivePipe, buf, 1); if ((i == 0) || ((i == 1) && (buf[0] == 'q'))) { /* - * Someone closed the write end of the pipe or sent us a - * Quit message [Bug: 4139] and then closed the write end - * of the pipe so we need to shut down the notifier thread. + * Someone closed the write end of the pipe or sent us a Quit + * message [Bug: 4139] and then closed the write end of the + * pipe so we need to shut down the notifier thread. */ break; @@ -1222,6 +1230,14 @@ NotifierThreadProc(clientData) TclpThreadExit (0); } -#endif +#endif /* TCL_THREADS */ #endif /* HAVE_COREFOUNDATION */ + +/* + * Local Variables: + * mode: c + * c-basic-offset: 4 + * fill-column: 78 + * End: + */ |