📄 tclunixnotfy.c
字号:
/* * 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. * * 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. * * RCS: @(#) $Id: tclUnixNotfy.c,v 1.11 2002/08/31 06:09:46 das Exp $ */#include "tclInt.h"#include "tclPort.h"#include <signal.h> extern TclStubs tclStubs;/* * This structure is used to keep track of the notifier info for a * 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 * 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. */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). */} FileHandlerEvent;/* * 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. */typedef struct ThreadSpecificData { FileHandler *firstFileHandlerPtr; /* Pointer to head of file handler list. */ fd_mask checkMasks[3*MASK_SIZE]; /* This array 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. */ fd_mask readyMasks[3*MASK_SIZE]; /* 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 * 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 * 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. */ Tcl_Condition waitCV; /* Any other thread alerts a notifier * that an event is ready to be processed * by signaling this condition variable. */ int eventReady; /* True if an event is ready to be processed. * Used as condition flag together with * waitCV above. */#endif} ThreadSpecificData;static Tcl_ThreadDataKey dataKey;#ifdef TCL_THREADS/* * The following static indicates the number of threads that have * initialized notifiers. * * You must hold the notifierMutex lock before accessing this variable. */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. * * You must hold the notifierMutex lock before accessing this list. */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. * * You must hold the notifierMutex lock before accessing this list. */static int triggerPipe = -1;/* * 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. */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. */#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/* * Static routines defined in this file. */#ifdef TCL_THREADSstatic void NotifierThreadProc _ANSI_ARGS_((ClientData clientData));#endifstatic int FileHandlerEventProc _ANSI_ARGS_((Tcl_Event *evPtr, int flags));/* *---------------------------------------------------------------------- * * Tcl_InitNotifier -- * * Initializes the platform specific notifier state. * * Results: * Returns a handle to the notifier state for this thread.. * * Side effects: * None. * *---------------------------------------------------------------------- */ClientDataTcl_InitNotifier(){ ThreadSpecificData *tsdPtr = TCL_TSD_INIT(&dataKey);#ifdef TCL_THREADS tsdPtr->eventReady = 0; /* * Start the Notifier thread if necessary. */ Tcl_MutexLock(¬ifierMutex); if (notifierCount == 0) { if (Tcl_CreateThread(¬ifierThread, NotifierThreadProc, NULL, TCL_THREAD_STACK_DEFAULT, TCL_THREAD_NOFLAGS) != TCL_OK) { panic("Tcl_InitNotifier: unable to start notifier thread"); } } notifierCount++; /* * Wait for the notifier pipe to be created. */ while (triggerPipe < 0) { Tcl_ConditionWait(¬ifierCV, ¬ifierMutex, NULL); } Tcl_MutexUnlock(¬ifierMutex);#endif return (ClientData) tsdPtr;}/* *---------------------------------------------------------------------- * * Tcl_FinalizeNotifier -- * * 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. * *---------------------------------------------------------------------- */voidTcl_FinalizeNotifier(clientData) ClientData clientData; /* Not used. */{#ifdef TCL_THREADS ThreadSpecificData *tsdPtr = TCL_TSD_INIT(&dataKey); Tcl_MutexLock(¬ifierMutex); notifierCount--; /* * 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) { if (triggerPipe < 0) { 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] */ write(triggerPipe, "q", 1); close(triggerPipe); Tcl_ConditionWait(¬ifierCV, ¬ifierMutex, NULL); } /* * Clean up any synchronization objects in the thread local storage. */ Tcl_ConditionFinalize(&(tsdPtr->waitCV)); Tcl_MutexUnlock(¬ifierMutex);#endif}/* *---------------------------------------------------------------------- * * 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. * * Results: * None. * * Side effects: * Signals the notifier condition variable for the specified * notifier. * *---------------------------------------------------------------------- */voidTcl_AlertNotifier(clientData) ClientData clientData;{#ifdef TCL_THREADS ThreadSpecificData *tsdPtr = (ThreadSpecificData *) clientData; Tcl_MutexLock(¬ifierMutex); tsdPtr->eventReady = 1; Tcl_ConditionNotify(&tsdPtr->waitCV); Tcl_MutexUnlock(¬ifierMutex);#endif}/* *---------------------------------------------------------------------- * * 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. * * Results: * None. * * Side effects: * None. * *---------------------------------------------------------------------- */voidTcl_SetTimer(timePtr) 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. */ if (tclStubs.tcl_SetTimer != Tcl_SetTimer) { tclStubs.tcl_SetTimer(timePtr); }}/* *---------------------------------------------------------------------- * * Tcl_ServiceModeHook -- * * This function is invoked whenever the service mode changes. * * Results: * None. * * Side effects: * None. * *---------------------------------------------------------------------- */voidTcl_ServiceModeHook(mode) int mode; /* Either TCL_SERVICE_ALL, or * TCL_SERVICE_NONE. */{}/* *---------------------------------------------------------------------- * * Tcl_CreateFileHandler -- * * This procedure registers a file handler with the select notifier. * * Results: * None. * * Side effects: * Creates a new file handler structure. * *---------------------------------------------------------------------- */voidTcl_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. */{ ThreadSpecificData *tsdPtr = TCL_TSD_INIT(&dataKey); FileHandler *filePtr; int index, bit; if (tclStubs.tcl_CreateFileHandler != Tcl_CreateFileHandler) { tclStubs.tcl_CreateFileHandler(fd, mask, proc, clientData); return; } for (filePtr = tsdPtr->firstFileHandlerPtr; filePtr != NULL; filePtr = filePtr->nextPtr) { if (filePtr->fd == fd) { break; } } if (filePtr == NULL) { filePtr = (FileHandler*) ckalloc(sizeof(FileHandler)); filePtr->fd = fd; filePtr->readyMask = 0; filePtr->nextPtr = tsdPtr->firstFileHandlerPtr; tsdPtr->firstFileHandlerPtr = filePtr; } filePtr->proc = proc; filePtr->clientData = clientData; filePtr->mask = mask; /* * Update the check masks for this file. */ index = fd/(NBBY*sizeof(fd_mask)); bit = 1 << (fd%(NBBY*sizeof(fd_mask))); if (mask & TCL_READABLE) { tsdPtr->checkMasks[index] |= bit; } else { tsdPtr->checkMasks[index] &= ~bit; } if (mask & TCL_WRITABLE) { (tsdPtr->checkMasks+MASK_SIZE)[index] |= bit; } else { (tsdPtr->checkMasks+MASK_SIZE)[index] &= ~bit; } if (mask & TCL_EXCEPTION) { (tsdPtr->checkMasks+2*(MASK_SIZE))[index] |= bit; } else { (tsdPtr->checkMasks+2*(MASK_SIZE))[index] &= ~bit; } if (tsdPtr->numFdBits <= fd) { tsdPtr->numFdBits = fd+1; }}/* *---------------------------------------------------------------------- * * Tcl_DeleteFileHandler -- * * Cancel a previously-arranged callback arrangement for * a file. * * Results: * None. * * Side effects: * If a callback was previously registered on file, remove it. * *---------------------------------------------------------------------- */voidTcl_DeleteFileHandler(fd) int fd; /* Stream id for which to remove callback procedure. */{ FileHandler *filePtr, *prevPtr; int index, bit, i; unsigned long flags; ThreadSpecificData *tsdPtr = TCL_TSD_INIT(&dataKey); if (tclStubs.tcl_DeleteFileHandler != Tcl_DeleteFileHandler) { tclStubs.tcl_DeleteFileHandler(fd); return; } /* * Find the entry for the given file (and return if there isn't one). */ for (prevPtr = NULL, filePtr = tsdPtr->firstFileHandlerPtr; ; prevPtr = filePtr, filePtr = filePtr->nextPtr) { if (filePtr == NULL) { return; } if (filePtr->fd == fd) { break; } } /* * Update the check masks for this file. */ index = fd/(NBBY*sizeof(fd_mask)); bit = 1 << (fd%(NBBY*sizeof(fd_mask))); if (filePtr->mask & TCL_READABLE) { tsdPtr->checkMasks[index] &= ~bit; }
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -