diff options
Diffstat (limited to 'tcl8.6/generic/tclAsync.c')
-rw-r--r-- | tcl8.6/generic/tclAsync.c | 355 |
1 files changed, 355 insertions, 0 deletions
diff --git a/tcl8.6/generic/tclAsync.c b/tcl8.6/generic/tclAsync.c new file mode 100644 index 0000000..14804e4 --- /dev/null +++ b/tcl8.6/generic/tclAsync.c @@ -0,0 +1,355 @@ +/* + * tclAsync.c -- + * + * This file provides low-level support needed to invoke signal handlers + * in a safe way. The code here doesn't actually handle signals, though. + * This code is based on proposals made by Mark Diekhans and Don Libes. + * + * Copyright (c) 1993 The Regents of the University of California. + * Copyright (c) 1994 Sun Microsystems, Inc. + * + * See the file "license.terms" for information on usage and redistribution of + * this file, and for a DISCLAIMER OF ALL WARRANTIES. + */ + +#include "tclInt.h" + +/* Forward declaration */ +struct ThreadSpecificData; + +/* + * One of the following structures exists for each asynchronous handler: + */ + +typedef struct AsyncHandler { + int ready; /* Non-zero means this handler should be + * invoked in the next call to + * Tcl_AsyncInvoke. */ + struct AsyncHandler *nextPtr; + /* Next in list of all handlers for the + * process. */ + Tcl_AsyncProc *proc; /* Procedure to call when handler is + * invoked. */ + ClientData clientData; /* Value to pass to handler when it is + * invoked. */ + struct ThreadSpecificData *originTsd; + /* Used in Tcl_AsyncMark to modify thread- + * specific data from outside the thread it is + * associated to. */ + Tcl_ThreadId originThrdId; /* Origin thread where this token was created + * and where it will be yielded. */ +} AsyncHandler; + +typedef struct ThreadSpecificData { + /* + * The variables below maintain a list of all existing handlers specific + * to the calling thread. + */ + AsyncHandler *firstHandler; /* First handler defined for process, or NULL + * if none. */ + AsyncHandler *lastHandler; /* Last handler or NULL. */ + int asyncReady; /* This is set to 1 whenever a handler becomes + * ready and it is cleared to zero whenever + * Tcl_AsyncInvoke is called. It can be + * checked elsewhere in the application by + * calling Tcl_AsyncReady to see if + * Tcl_AsyncInvoke should be invoked. */ + int asyncActive; /* Indicates whether Tcl_AsyncInvoke is + * currently working. If so then we won't set + * asyncReady again until Tcl_AsyncInvoke + * returns. */ + Tcl_Mutex asyncMutex; /* Thread-specific AsyncHandler linked-list + * lock */ +} ThreadSpecificData; +static Tcl_ThreadDataKey dataKey; + +/* + *---------------------------------------------------------------------- + * + * TclFinalizeAsync -- + * + * Finalizes the mutex in the thread local data structure for the async + * subsystem. + * + * Results: + * None. + * + * Side effects: + * Forgets knowledge of the mutex should it have been created. + * + *---------------------------------------------------------------------- + */ + +void +TclFinalizeAsync(void) +{ + ThreadSpecificData *tsdPtr = TCL_TSD_INIT(&dataKey); + + if (tsdPtr->asyncMutex != NULL) { + Tcl_MutexFinalize(&tsdPtr->asyncMutex); + } +} + +/* + *---------------------------------------------------------------------- + * + * Tcl_AsyncCreate -- + * + * This procedure creates the data structures for an asynchronous + * handler, so that no memory has to be allocated when the handler is + * activated. + * + * Results: + * The return value is a token for the handler, which can be used to + * activate it later on. + * + * Side effects: + * Information about the handler is recorded. + * + *---------------------------------------------------------------------- + */ + +Tcl_AsyncHandler +Tcl_AsyncCreate( + Tcl_AsyncProc *proc, /* Procedure to call when handler is + * invoked. */ + ClientData clientData) /* Argument to pass to handler. */ +{ + AsyncHandler *asyncPtr; + ThreadSpecificData *tsdPtr = TCL_TSD_INIT(&dataKey); + + asyncPtr = ckalloc(sizeof(AsyncHandler)); + asyncPtr->ready = 0; + asyncPtr->nextPtr = NULL; + asyncPtr->proc = proc; + asyncPtr->clientData = clientData; + asyncPtr->originTsd = tsdPtr; + asyncPtr->originThrdId = Tcl_GetCurrentThread(); + + Tcl_MutexLock(&tsdPtr->asyncMutex); + if (tsdPtr->firstHandler == NULL) { + tsdPtr->firstHandler = asyncPtr; + } else { + tsdPtr->lastHandler->nextPtr = asyncPtr; + } + tsdPtr->lastHandler = asyncPtr; + Tcl_MutexUnlock(&tsdPtr->asyncMutex); + return (Tcl_AsyncHandler) asyncPtr; +} + +/* + *---------------------------------------------------------------------- + * + * Tcl_AsyncMark -- + * + * This procedure is called to request that an asynchronous handler be + * invoked as soon as possible. It's typically called from an interrupt + * handler, where it isn't safe to do anything that depends on or + * modifies application state. + * + * Results: + * None. + * + * Side effects: + * The handler gets marked for invocation later. + * + *---------------------------------------------------------------------- + */ + +void +Tcl_AsyncMark( + Tcl_AsyncHandler async) /* Token for handler. */ +{ + AsyncHandler *token = (AsyncHandler *) async; + + Tcl_MutexLock(&token->originTsd->asyncMutex); + token->ready = 1; + if (!token->originTsd->asyncActive) { + token->originTsd->asyncReady = 1; + Tcl_ThreadAlert(token->originThrdId); + } + Tcl_MutexUnlock(&token->originTsd->asyncMutex); +} + +/* + *---------------------------------------------------------------------- + * + * Tcl_AsyncInvoke -- + * + * This procedure is called at a "safe" time at background level to + * invoke any active asynchronous handlers. + * + * Results: + * The return value is a normal Tcl result, which is intended to replace + * the code argument as the current completion code for interp. + * + * Side effects: + * Depends on the handlers that are active. + * + *---------------------------------------------------------------------- + */ + +int +Tcl_AsyncInvoke( + Tcl_Interp *interp, /* If invoked from Tcl_Eval just after + * completing a command, points to + * interpreter. Otherwise it is NULL. */ + int code) /* If interp is non-NULL, this gives + * completion code from command that just + * completed. */ +{ + AsyncHandler *asyncPtr; + ThreadSpecificData *tsdPtr = TCL_TSD_INIT(&dataKey); + + Tcl_MutexLock(&tsdPtr->asyncMutex); + + if (tsdPtr->asyncReady == 0) { + Tcl_MutexUnlock(&tsdPtr->asyncMutex); + return code; + } + tsdPtr->asyncReady = 0; + tsdPtr->asyncActive = 1; + if (interp == NULL) { + code = 0; + } + + /* + * Make one or more passes over the list of handlers, invoking at most one + * handler in each pass. After invoking a handler, go back to the start of + * the list again so that (a) if a new higher-priority handler gets marked + * while executing a lower priority handler, we execute the higher- + * priority handler next, and (b) if a handler gets deleted during the + * execution of a handler, then the list structure may change so it isn't + * safe to continue down the list anyway. + */ + + while (1) { + for (asyncPtr = tsdPtr->firstHandler; asyncPtr != NULL; + asyncPtr = asyncPtr->nextPtr) { + if (asyncPtr->ready) { + break; + } + } + if (asyncPtr == NULL) { + break; + } + asyncPtr->ready = 0; + Tcl_MutexUnlock(&tsdPtr->asyncMutex); + code = asyncPtr->proc(asyncPtr->clientData, interp, code); + Tcl_MutexLock(&tsdPtr->asyncMutex); + } + tsdPtr->asyncActive = 0; + Tcl_MutexUnlock(&tsdPtr->asyncMutex); + return code; +} + +/* + *---------------------------------------------------------------------- + * + * Tcl_AsyncDelete -- + * + * Frees up all the state for an asynchronous handler. The handler should + * never be used again. + * + * Results: + * None. + * + * Side effects: + * The state associated with the handler is deleted. + * + * Failure to locate the handler in current thread private list + * of async handlers will result in panic; exception: the list + * is already empty (potential trouble?). + * Consequently, threads should create and delete handlers + * themselves. I.e. a handler created by one should not be + * deleted by some other thread. + * + *---------------------------------------------------------------------- + */ + +void +Tcl_AsyncDelete( + Tcl_AsyncHandler async) /* Token for handler to delete. */ +{ + ThreadSpecificData *tsdPtr = TCL_TSD_INIT(&dataKey); + AsyncHandler *asyncPtr = (AsyncHandler *) async; + AsyncHandler *prevPtr, *thisPtr; + + /* + * Assure early handling of the constraint + */ + + if (asyncPtr->originThrdId != Tcl_GetCurrentThread()) { + Tcl_Panic("Tcl_AsyncDelete: async handler deleted by the wrong thread"); + } + + /* + * If we come to this point when TSD's for the current + * thread have already been garbage-collected, we are + * in the _serious_ trouble. OTOH, we tolerate calling + * with already cleaned-up handler list (should we?). + */ + + Tcl_MutexLock(&tsdPtr->asyncMutex); + if (tsdPtr->firstHandler != NULL) { + prevPtr = thisPtr = tsdPtr->firstHandler; + while (thisPtr != NULL && thisPtr != asyncPtr) { + prevPtr = thisPtr; + thisPtr = thisPtr->nextPtr; + } + if (thisPtr == NULL) { + Tcl_Panic("Tcl_AsyncDelete: cannot find async handler"); + } + if (asyncPtr == tsdPtr->firstHandler) { + tsdPtr->firstHandler = asyncPtr->nextPtr; + } else { + prevPtr->nextPtr = asyncPtr->nextPtr; + } + if (asyncPtr == tsdPtr->lastHandler) { + tsdPtr->lastHandler = prevPtr; + } + } + Tcl_MutexUnlock(&tsdPtr->asyncMutex); + ckfree(asyncPtr); +} + +/* + *---------------------------------------------------------------------- + * + * Tcl_AsyncReady -- + * + * This procedure can be used to tell whether Tcl_AsyncInvoke needs to be + * called. This procedure is the external interface for checking the + * thread-specific asyncReady variable. + * + * Results: + * The return value is 1 whenever a handler is ready and is 0 when no + * handlers are ready. + * + * Side effects: + * None. + * + *---------------------------------------------------------------------- + */ + +int +Tcl_AsyncReady(void) +{ + ThreadSpecificData *tsdPtr = TCL_TSD_INIT(&dataKey); + return tsdPtr->asyncReady; +} + +int * +TclGetAsyncReadyPtr(void) +{ + ThreadSpecificData *tsdPtr = TCL_TSD_INIT(&dataKey); + return &(tsdPtr->asyncReady); +} + +/* + * Local Variables: + * mode: c + * c-basic-offset: 4 + * fill-column: 78 + * End: + */ |