diff options
Diffstat (limited to 'generic/tclAsync.c')
| -rw-r--r-- | generic/tclAsync.c | 222 |
1 files changed, 63 insertions, 159 deletions
diff --git a/generic/tclAsync.c b/generic/tclAsync.c index 9ce2c88..ca18f5e 100644 --- a/generic/tclAsync.c +++ b/generic/tclAsync.c @@ -5,8 +5,8 @@ * 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 © 1993 The Regents of the University of California. - * Copyright © 1994 Sun Microsystems, Inc. + * 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. @@ -25,9 +25,9 @@ typedef struct AsyncHandler { int ready; /* Non-zero means this handler should be * invoked in the next call to * Tcl_AsyncInvoke. */ - struct AsyncHandler *nextPtr, *prevPtr; - /* Next, previous in list of all handlers - * for the process. */ + 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 @@ -38,10 +38,16 @@ typedef struct AsyncHandler { * associated to. */ Tcl_ThreadId originThrdId; /* Origin thread where this token was created * and where it will be yielded. */ - ClientData notifierData; /* Platform notifier data or NULL. */ } 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 @@ -52,29 +58,24 @@ typedef struct ThreadSpecificData { * 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; - -/* Mutex to protect linked-list of AsyncHandlers in the process. */ -TCL_DECLARE_MUTEX(asyncMutex) - -/* List of all existing handlers of the process. */ -static AsyncHandler *firstHandler = NULL; -static AsyncHandler *lastHandler = NULL; /* *---------------------------------------------------------------------- * * TclFinalizeAsync -- * - * Finalizes the thread local data structure for the async + * Finalizes the mutex in the thread local data structure for the async * subsystem. * * Results: * None. * * Side effects: - * Cleans up left-over async handlers for the calling thread. + * Forgets knowledge of the mutex should it have been created. * *---------------------------------------------------------------------- */ @@ -82,40 +83,10 @@ static AsyncHandler *lastHandler = NULL; void TclFinalizeAsync(void) { - AsyncHandler *token, *toDelete = NULL; - Tcl_ThreadId self = Tcl_GetCurrentThread(); - - Tcl_MutexLock(&asyncMutex); - for (token = firstHandler; token != NULL;) { - AsyncHandler *nextToken = token->nextPtr; + ThreadSpecificData *tsdPtr = TCL_TSD_INIT(&dataKey); - if (token->originThrdId == self) { - if (token->prevPtr == NULL) { - firstHandler = token->nextPtr; - if (firstHandler == NULL) { - lastHandler = NULL; - break; - } - } else { - token->prevPtr->nextPtr = token->nextPtr; - if (token == lastHandler) { - lastHandler = token->prevPtr; - } - } - if (token->nextPtr != NULL) { - token->nextPtr->prevPtr = token->prevPtr; - } - token->nextPtr = toDelete; - token->prevPtr = NULL; - toDelete = token; - } - token = nextToken; - } - Tcl_MutexUnlock(&asyncMutex); - while (toDelete != NULL) { - token = toDelete; - toDelete = toDelete->nextPtr; - ckfree(token); + if (tsdPtr->asyncMutex != NULL) { + Tcl_MutexFinalize(&tsdPtr->asyncMutex); } } @@ -147,25 +118,22 @@ Tcl_AsyncCreate( AsyncHandler *asyncPtr; ThreadSpecificData *tsdPtr = TCL_TSD_INIT(&dataKey); - asyncPtr = (AsyncHandler*)ckalloc(sizeof(AsyncHandler)); + asyncPtr = (AsyncHandler *) ckalloc(sizeof(AsyncHandler)); asyncPtr->ready = 0; asyncPtr->nextPtr = NULL; - asyncPtr->prevPtr = NULL; asyncPtr->proc = proc; asyncPtr->clientData = clientData; asyncPtr->originTsd = tsdPtr; asyncPtr->originThrdId = Tcl_GetCurrentThread(); - asyncPtr->notifierData = TclpNotifierData(); - Tcl_MutexLock(&asyncMutex); - if (firstHandler == NULL) { - firstHandler = asyncPtr; + Tcl_MutexLock(&tsdPtr->asyncMutex); + if (tsdPtr->firstHandler == NULL) { + tsdPtr->firstHandler = asyncPtr; } else { - asyncPtr->prevPtr = lastHandler; - lastHandler->nextPtr = asyncPtr; + tsdPtr->lastHandler->nextPtr = asyncPtr; } - lastHandler = asyncPtr; - Tcl_MutexUnlock(&asyncMutex); + tsdPtr->lastHandler = asyncPtr; + Tcl_MutexUnlock(&tsdPtr->asyncMutex); return (Tcl_AsyncHandler) asyncPtr; } @@ -194,86 +162,13 @@ Tcl_AsyncMark( { AsyncHandler *token = (AsyncHandler *) async; - Tcl_MutexLock(&asyncMutex); + Tcl_MutexLock(&token->originTsd->asyncMutex); token->ready = 1; if (!token->originTsd->asyncActive) { token->originTsd->asyncReady = 1; Tcl_ThreadAlert(token->originThrdId); } - Tcl_MutexUnlock(&asyncMutex); - -} - -/* - *---------------------------------------------------------------------- - * - * Tcl_AsyncMarkFromSignal -- - * - * This procedure is similar to Tcl_AsyncMark but must be used - * in POSIX signal contexts. In addition to Tcl_AsyncMark the - * signal number is passed. - * - * Results: - * True, when the handler will be marked, false otherwise. - * - * Side effects: - * The handler gets marked for invocation later. - * - *---------------------------------------------------------------------- - */ - -int -Tcl_AsyncMarkFromSignal( - Tcl_AsyncHandler async, /* Token for handler. */ - int sigNumber) /* Signal number. */ -{ -#if TCL_THREADS - AsyncHandler *token = (AsyncHandler *) async; - - return TclAsyncNotifier(sigNumber, token->originThrdId, - token->notifierData, &token->ready, -1); -#else - (void)sigNumber; - - Tcl_AsyncMark(async); - return 1; -#endif -} - -/* - *---------------------------------------------------------------------- - * - * TclAsyncMarkFromNotifier -- - * - * This procedure is called from the notifier thread and - * invokes Tcl_AsyncMark for specifically marked handlers. - * - * Results: - * None. - * - * Side effects: - * Handlers get marked for invocation later. - * - *---------------------------------------------------------------------- - */ - -void -TclAsyncMarkFromNotifier(void) -{ - AsyncHandler *token; - - Tcl_MutexLock(&asyncMutex); - for (token = firstHandler; token != NULL; - token = token->nextPtr) { - if (token->ready == -1) { - token->ready = 1; - if (!token->originTsd->asyncActive) { - token->originTsd->asyncReady = 1; - Tcl_ThreadAlert(token->originThrdId); - } - } - } - Tcl_MutexUnlock(&asyncMutex); + Tcl_MutexUnlock(&token->originTsd->asyncMutex); } /* @@ -305,12 +200,11 @@ Tcl_AsyncInvoke( { AsyncHandler *asyncPtr; ThreadSpecificData *tsdPtr = TCL_TSD_INIT(&dataKey); - Tcl_ThreadId self = Tcl_GetCurrentThread(); - Tcl_MutexLock(&asyncMutex); + Tcl_MutexLock(&tsdPtr->asyncMutex); if (tsdPtr->asyncReady == 0) { - Tcl_MutexUnlock(&asyncMutex); + Tcl_MutexUnlock(&tsdPtr->asyncMutex); return code; } tsdPtr->asyncReady = 0; @@ -330,11 +224,8 @@ Tcl_AsyncInvoke( */ while (1) { - for (asyncPtr = firstHandler; asyncPtr != NULL; + for (asyncPtr = tsdPtr->firstHandler; asyncPtr != NULL; asyncPtr = asyncPtr->nextPtr) { - if (asyncPtr->originThrdId != self) { - continue; - } if (asyncPtr->ready) { break; } @@ -343,12 +234,12 @@ Tcl_AsyncInvoke( break; } asyncPtr->ready = 0; - Tcl_MutexUnlock(&asyncMutex); - code = asyncPtr->proc(asyncPtr->clientData, interp, code); - Tcl_MutexLock(&asyncMutex); + Tcl_MutexUnlock(&tsdPtr->asyncMutex); + code = (*asyncPtr->proc)(asyncPtr->clientData, interp, code); + Tcl_MutexLock(&tsdPtr->asyncMutex); } tsdPtr->asyncActive = 0; - Tcl_MutexUnlock(&asyncMutex); + Tcl_MutexUnlock(&tsdPtr->asyncMutex); return code; } @@ -369,7 +260,7 @@ Tcl_AsyncInvoke( * 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 + * Consequently, threads should create and delete handlers * themselves. I.e. a handler created by one should not be * deleted by some other thread. * @@ -380,7 +271,9 @@ 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 @@ -390,23 +283,34 @@ Tcl_AsyncDelete( Tcl_Panic("Tcl_AsyncDelete: async handler deleted by the wrong thread"); } - Tcl_MutexLock(&asyncMutex); - if (asyncPtr->prevPtr == NULL) { - firstHandler = asyncPtr->nextPtr; - if (firstHandler == NULL) { - lastHandler = NULL; + /* + * 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; } - } else { - asyncPtr->prevPtr->nextPtr = asyncPtr->nextPtr; - if (asyncPtr == lastHandler) { - lastHandler = asyncPtr->prevPtr; + 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; } } - if (asyncPtr->nextPtr != NULL) { - asyncPtr->nextPtr->prevPtr = asyncPtr->prevPtr; - } - Tcl_MutexUnlock(&asyncMutex); - ckfree(asyncPtr); + Tcl_MutexUnlock(&tsdPtr->asyncMutex); + ckfree((char *) asyncPtr); } /* |
