sl@0: /* sl@0: * tclAsync.c -- sl@0: * sl@0: * This file provides low-level support needed to invoke signal sl@0: * handlers in a safe way. The code here doesn't actually handle sl@0: * signals, though. This code is based on proposals made by sl@0: * Mark Diekhans and Don Libes. sl@0: * sl@0: * Copyright (c) 1993 The Regents of the University of California. sl@0: * Copyright (c) 1994 Sun Microsystems, Inc. sl@0: * Portions Copyright (c) 2007-2008 Nokia Corporation and/or its subsidiaries. All rights reserved. sl@0: * sl@0: * See the file "license.terms" for information on usage and redistribution sl@0: * of this file, and for a DISCLAIMER OF ALL WARRANTIES. sl@0: * sl@0: * RCS: @(#) $Id: tclAsync.c,v 1.6.12.1 2006/07/11 13:18:10 vasiljevic Exp $ sl@0: */ sl@0: sl@0: #include "tclInt.h" sl@0: #include "tclPort.h" sl@0: #if defined(__SYMBIAN32__) && defined(__WINSCW__) sl@0: #include "tclSymbianGlobals.h" sl@0: #define dataKey getdataKey(1) sl@0: #endif sl@0: sl@0: /* Forward declaration */ sl@0: struct ThreadSpecificData; sl@0: sl@0: /* sl@0: * One of the following structures exists for each asynchronous sl@0: * handler: sl@0: */ sl@0: sl@0: typedef struct AsyncHandler { sl@0: int ready; /* Non-zero means this handler should sl@0: * be invoked in the next call to sl@0: * Tcl_AsyncInvoke. */ sl@0: struct AsyncHandler *nextPtr; /* Next in list of all handlers for sl@0: * the process. */ sl@0: Tcl_AsyncProc *proc; /* Procedure to call when handler sl@0: * is invoked. */ sl@0: ClientData clientData; /* Value to pass to handler when it sl@0: * is invoked. */ sl@0: struct ThreadSpecificData *originTsd; sl@0: /* Used in Tcl_AsyncMark to modify thread- sl@0: * specific data from outside the thread sl@0: * it is associated to. */ sl@0: Tcl_ThreadId originThrdId; /* Origin thread where this token was sl@0: * created and where it will be sl@0: * yielded. */ sl@0: } AsyncHandler; sl@0: sl@0: sl@0: typedef struct ThreadSpecificData { sl@0: /* sl@0: * The variables below maintain a list of all existing handlers sl@0: * specific to the calling thread. sl@0: */ sl@0: AsyncHandler *firstHandler; /* First handler defined for process, sl@0: * or NULL if none. */ sl@0: AsyncHandler *lastHandler; /* Last handler or NULL. */ sl@0: sl@0: /* sl@0: * The variable below is set to 1 whenever a handler becomes ready and sl@0: * it is cleared to zero whenever Tcl_AsyncInvoke is called. It can be sl@0: * checked elsewhere in the application by calling Tcl_AsyncReady to see sl@0: * if Tcl_AsyncInvoke should be invoked. sl@0: */ sl@0: sl@0: int asyncReady; sl@0: sl@0: /* sl@0: * The variable below indicates whether Tcl_AsyncInvoke is currently sl@0: * working. If so then we won't set asyncReady again until sl@0: * Tcl_AsyncInvoke returns. sl@0: */ sl@0: sl@0: int asyncActive; sl@0: sl@0: Tcl_Mutex asyncMutex; /* Thread-specific AsyncHandler linked-list lock */ sl@0: sl@0: } ThreadSpecificData; sl@0: #if !defined(__SYMBIAN32__) || !defined(__WINSCW__) sl@0: static Tcl_ThreadDataKey dataKey; sl@0: #endif sl@0: sl@0: sl@0: /* sl@0: *---------------------------------------------------------------------- sl@0: * sl@0: * TclFinalizeAsync -- sl@0: * sl@0: * Finalizes the mutex in the thread local data structure for the sl@0: * async subsystem. sl@0: * sl@0: * Results: sl@0: * None. sl@0: * sl@0: * Side effects: sl@0: * Forgets knowledge of the mutex should it have been created. sl@0: * sl@0: *---------------------------------------------------------------------- sl@0: */ sl@0: sl@0: void sl@0: TclFinalizeAsync() sl@0: { sl@0: ThreadSpecificData *tsdPtr = TCL_TSD_INIT(&dataKey); sl@0: sl@0: if (tsdPtr->asyncMutex != NULL) { sl@0: Tcl_MutexFinalize(&tsdPtr->asyncMutex); sl@0: } sl@0: } sl@0: sl@0: /* sl@0: *---------------------------------------------------------------------- sl@0: * sl@0: * Tcl_AsyncCreate -- sl@0: * sl@0: * This procedure creates the data structures for an asynchronous sl@0: * handler, so that no memory has to be allocated when the handler sl@0: * is activated. sl@0: * sl@0: * Results: sl@0: * The return value is a token for the handler, which can be used sl@0: * to activate it later on. sl@0: * sl@0: * Side effects: sl@0: * Information about the handler is recorded. sl@0: * sl@0: *---------------------------------------------------------------------- sl@0: */ sl@0: sl@0: EXPORT_C Tcl_AsyncHandler sl@0: Tcl_AsyncCreate(proc, clientData) sl@0: Tcl_AsyncProc *proc; /* Procedure to call when handler sl@0: * is invoked. */ sl@0: ClientData clientData; /* Argument to pass to handler. */ sl@0: { sl@0: AsyncHandler *asyncPtr; sl@0: ThreadSpecificData *tsdPtr = TCL_TSD_INIT(&dataKey); sl@0: sl@0: asyncPtr = (AsyncHandler *) ckalloc(sizeof(AsyncHandler)); sl@0: asyncPtr->ready = 0; sl@0: asyncPtr->nextPtr = NULL; sl@0: asyncPtr->proc = proc; sl@0: asyncPtr->clientData = clientData; sl@0: asyncPtr->originTsd = tsdPtr; sl@0: asyncPtr->originThrdId = Tcl_GetCurrentThread(); sl@0: sl@0: Tcl_MutexLock(&tsdPtr->asyncMutex); sl@0: if (tsdPtr->firstHandler == NULL) { sl@0: tsdPtr->firstHandler = asyncPtr; sl@0: } else { sl@0: tsdPtr->lastHandler->nextPtr = asyncPtr; sl@0: } sl@0: tsdPtr->lastHandler = asyncPtr; sl@0: Tcl_MutexUnlock(&tsdPtr->asyncMutex); sl@0: return (Tcl_AsyncHandler) asyncPtr; sl@0: } sl@0: sl@0: /* sl@0: *---------------------------------------------------------------------- sl@0: * sl@0: * Tcl_AsyncMark -- sl@0: * sl@0: * This procedure is called to request that an asynchronous handler sl@0: * be invoked as soon as possible. It's typically called from sl@0: * an interrupt handler, where it isn't safe to do anything that sl@0: * depends on or modifies application state. sl@0: * sl@0: * Results: sl@0: * None. sl@0: * sl@0: * Side effects: sl@0: * The handler gets marked for invocation later. sl@0: * sl@0: *---------------------------------------------------------------------- sl@0: */ sl@0: sl@0: EXPORT_C void sl@0: Tcl_AsyncMark(async) sl@0: Tcl_AsyncHandler async; /* Token for handler. */ sl@0: { sl@0: AsyncHandler *token = (AsyncHandler *) async; sl@0: sl@0: Tcl_MutexLock(&token->originTsd->asyncMutex); sl@0: token->ready = 1; sl@0: if (!token->originTsd->asyncActive) { sl@0: token->originTsd->asyncReady = 1; sl@0: Tcl_ThreadAlert(token->originThrdId); sl@0: } sl@0: Tcl_MutexUnlock(&token->originTsd->asyncMutex); sl@0: } sl@0: sl@0: /* sl@0: *---------------------------------------------------------------------- sl@0: * sl@0: * Tcl_AsyncInvoke -- sl@0: * sl@0: * This procedure is called at a "safe" time at background level sl@0: * to invoke any active asynchronous handlers. sl@0: * sl@0: * Results: sl@0: * The return value is a normal Tcl result, which is intended to sl@0: * replace the code argument as the current completion code for sl@0: * interp. sl@0: * sl@0: * Side effects: sl@0: * Depends on the handlers that are active. sl@0: * sl@0: *---------------------------------------------------------------------- sl@0: */ sl@0: sl@0: EXPORT_C int sl@0: Tcl_AsyncInvoke(interp, code) sl@0: Tcl_Interp *interp; /* If invoked from Tcl_Eval just after sl@0: * completing a command, points to sl@0: * interpreter. Otherwise it is sl@0: * NULL. */ sl@0: int code; /* If interp is non-NULL, this gives sl@0: * completion code from command that sl@0: * just completed. */ sl@0: { sl@0: AsyncHandler *asyncPtr; sl@0: ThreadSpecificData *tsdPtr = TCL_TSD_INIT(&dataKey); sl@0: sl@0: Tcl_MutexLock(&tsdPtr->asyncMutex); sl@0: sl@0: if (tsdPtr->asyncReady == 0) { sl@0: Tcl_MutexUnlock(&tsdPtr->asyncMutex); sl@0: return code; sl@0: } sl@0: tsdPtr->asyncReady = 0; sl@0: tsdPtr->asyncActive = 1; sl@0: if (interp == NULL) { sl@0: code = 0; sl@0: } sl@0: sl@0: /* sl@0: * Make one or more passes over the list of handlers, invoking sl@0: * at most one handler in each pass. After invoking a handler, sl@0: * go back to the start of the list again so that (a) if a new sl@0: * higher-priority handler gets marked while executing a lower sl@0: * priority handler, we execute the higher-priority handler sl@0: * next, and (b) if a handler gets deleted during the execution sl@0: * of a handler, then the list structure may change so it isn't sl@0: * safe to continue down the list anyway. sl@0: */ sl@0: sl@0: while (1) { sl@0: for (asyncPtr = tsdPtr->firstHandler; asyncPtr != NULL; sl@0: asyncPtr = asyncPtr->nextPtr) { sl@0: if (asyncPtr->ready) { sl@0: break; sl@0: } sl@0: } sl@0: if (asyncPtr == NULL) { sl@0: break; sl@0: } sl@0: asyncPtr->ready = 0; sl@0: Tcl_MutexUnlock(&tsdPtr->asyncMutex); sl@0: code = (*asyncPtr->proc)(asyncPtr->clientData, interp, code); sl@0: Tcl_MutexLock(&tsdPtr->asyncMutex); sl@0: } sl@0: tsdPtr->asyncActive = 0; sl@0: Tcl_MutexUnlock(&tsdPtr->asyncMutex); sl@0: return code; sl@0: } sl@0: sl@0: /* sl@0: *---------------------------------------------------------------------- sl@0: * sl@0: * Tcl_AsyncDelete -- sl@0: * sl@0: * Frees up all the state for an asynchronous handler. The handler sl@0: * should never be used again. sl@0: * sl@0: * Results: sl@0: * None. sl@0: * sl@0: * Side effects: sl@0: * The state associated with the handler is deleted. sl@0: * sl@0: *---------------------------------------------------------------------- sl@0: */ sl@0: sl@0: EXPORT_C void sl@0: Tcl_AsyncDelete(async) sl@0: Tcl_AsyncHandler async; /* Token for handler to delete. */ sl@0: { sl@0: ThreadSpecificData *tsdPtr = TCL_TSD_INIT(&dataKey); sl@0: AsyncHandler *asyncPtr = (AsyncHandler *) async; sl@0: AsyncHandler *prevPtr; sl@0: sl@0: /* sl@0: * Conservatively check the existence of the linked list of sl@0: * registered handlers, as we may come at this point even sl@0: * when the TSD's for the current thread have been already sl@0: * garbage-collected. sl@0: */ sl@0: sl@0: Tcl_MutexLock(&tsdPtr->asyncMutex); sl@0: if (tsdPtr->firstHandler != NULL ) { sl@0: if (tsdPtr->firstHandler == asyncPtr) { sl@0: tsdPtr->firstHandler = asyncPtr->nextPtr; sl@0: if (tsdPtr->firstHandler == NULL) { sl@0: tsdPtr->lastHandler = NULL; sl@0: } sl@0: } else { sl@0: prevPtr = tsdPtr->firstHandler; sl@0: while (prevPtr->nextPtr != asyncPtr) { sl@0: prevPtr = prevPtr->nextPtr; sl@0: } sl@0: prevPtr->nextPtr = asyncPtr->nextPtr; sl@0: if (tsdPtr->lastHandler == asyncPtr) { sl@0: tsdPtr->lastHandler = prevPtr; sl@0: } sl@0: } sl@0: } sl@0: Tcl_MutexUnlock(&tsdPtr->asyncMutex); sl@0: ckfree((char *) asyncPtr); sl@0: } sl@0: sl@0: /* sl@0: *---------------------------------------------------------------------- sl@0: * sl@0: * Tcl_AsyncReady -- sl@0: * sl@0: * This procedure can be used to tell whether Tcl_AsyncInvoke sl@0: * needs to be called. This procedure is the external interface sl@0: * for checking the thread-specific asyncReady variable. sl@0: * sl@0: * Results: sl@0: * The return value is 1 whenever a handler is ready and is 0 sl@0: * when no handlers are ready. sl@0: * sl@0: * Side effects: sl@0: * None. sl@0: * sl@0: *---------------------------------------------------------------------- sl@0: */ sl@0: sl@0: EXPORT_C int sl@0: Tcl_AsyncReady() sl@0: { sl@0: ThreadSpecificData *tsdPtr = TCL_TSD_INIT(&dataKey); sl@0: return tsdPtr->asyncReady; sl@0: }