diff options
author | jan.nijtmans <nijtmans@users.sourceforge.net> | 2021-08-17 16:16:29 (GMT) |
---|---|---|
committer | jan.nijtmans <nijtmans@users.sourceforge.net> | 2021-08-17 16:16:29 (GMT) |
commit | 5f83d31eceac5d0a48f75293f02725a59168beaf (patch) | |
tree | 088ba5712739eee4f6fd1ff277de958420af7021 /generic/tclAsync.c | |
parent | 58502bda0b655dcb2b8e619c1cce4ba99f294aad (diff) | |
download | tcl-5f83d31eceac5d0a48f75293f02725a59168beaf.zip tcl-5f83d31eceac5d0a48f75293f02725a59168beaf.tar.gz tcl-5f83d31eceac5d0a48f75293f02725a59168beaf.tar.bz2 |
tip#511 proposed implementation
Diffstat (limited to 'generic/tclAsync.c')
-rw-r--r-- | generic/tclAsync.c | 208 |
1 files changed, 151 insertions, 57 deletions
diff --git a/generic/tclAsync.c b/generic/tclAsync.c index 3a09304..bce984d 100644 --- a/generic/tclAsync.c +++ b/generic/tclAsync.c @@ -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; - /* Next in list of all handlers for the - * process. */ + struct AsyncHandler *nextPtr, *prevPtr; + /* Next, previous 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,16 +38,10 @@ 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 @@ -58,24 +52,29 @@ 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 mutex in the thread local data structure for the async + * Finalizes the thread local data structure for the async * subsystem. * * Results: * None. * * Side effects: - * Forgets knowledge of the mutex should it have been created. + * Cleans up left-over async handlers for the calling thread. * *---------------------------------------------------------------------- */ @@ -83,10 +82,40 @@ static Tcl_ThreadDataKey dataKey; void TclFinalizeAsync(void) { - ThreadSpecificData *tsdPtr = TCL_TSD_INIT(&dataKey); + AsyncHandler *token, *toDelete = NULL; + Tcl_ThreadId self = Tcl_GetCurrentThread(); + + Tcl_MutexLock(&asyncMutex); + for (token = firstHandler; token != NULL;) { + AsyncHandler *nextToken = token->nextPtr; - if (tsdPtr->asyncMutex != NULL) { - Tcl_MutexFinalize(&tsdPtr->asyncMutex); + 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); } } @@ -121,19 +150,22 @@ Tcl_AsyncCreate( 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(&tsdPtr->asyncMutex); - if (tsdPtr->firstHandler == NULL) { - tsdPtr->firstHandler = asyncPtr; + Tcl_MutexLock(&asyncMutex); + if (firstHandler == NULL) { + firstHandler = asyncPtr; } else { - tsdPtr->lastHandler->nextPtr = asyncPtr; + asyncPtr->prevPtr = lastHandler; + lastHandler->nextPtr = asyncPtr; } - tsdPtr->lastHandler = asyncPtr; - Tcl_MutexUnlock(&tsdPtr->asyncMutex); + lastHandler = asyncPtr; + Tcl_MutexUnlock(&asyncMutex); return (Tcl_AsyncHandler) asyncPtr; } @@ -162,13 +194,84 @@ Tcl_AsyncMark( { AsyncHandler *token = (AsyncHandler *) async; - Tcl_MutexLock(&token->originTsd->asyncMutex); + Tcl_MutexLock(&asyncMutex); token->ready = 1; if (!token->originTsd->asyncActive) { token->originTsd->asyncReady = 1; Tcl_ThreadAlert(token->originThrdId); } - Tcl_MutexUnlock(&token->originTsd->asyncMutex); + 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. */ +{ +#ifdef TCL_THREADS + AsyncHandler *token = (AsyncHandler *) async; + + return TclAsyncNotifier(sigNumber, token->originThrdId, + token->notifierData, &token->ready, -1); +#else + 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); } /* @@ -200,11 +303,12 @@ Tcl_AsyncInvoke( { AsyncHandler *asyncPtr; ThreadSpecificData *tsdPtr = TCL_TSD_INIT(&dataKey); + Tcl_ThreadId self = Tcl_GetCurrentThread(); - Tcl_MutexLock(&tsdPtr->asyncMutex); + Tcl_MutexLock(&asyncMutex); if (tsdPtr->asyncReady == 0) { - Tcl_MutexUnlock(&tsdPtr->asyncMutex); + Tcl_MutexUnlock(&asyncMutex); return code; } tsdPtr->asyncReady = 0; @@ -224,8 +328,11 @@ Tcl_AsyncInvoke( */ while (1) { - for (asyncPtr = tsdPtr->firstHandler; asyncPtr != NULL; + for (asyncPtr = firstHandler; asyncPtr != NULL; asyncPtr = asyncPtr->nextPtr) { + if (asyncPtr->originThrdId != self) { + continue; + } if (asyncPtr->ready) { break; } @@ -234,12 +341,12 @@ Tcl_AsyncInvoke( break; } asyncPtr->ready = 0; - Tcl_MutexUnlock(&tsdPtr->asyncMutex); + Tcl_MutexUnlock(&asyncMutex); code = asyncPtr->proc(asyncPtr->clientData, interp, code); - Tcl_MutexLock(&tsdPtr->asyncMutex); + Tcl_MutexLock(&asyncMutex); } tsdPtr->asyncActive = 0; - Tcl_MutexUnlock(&tsdPtr->asyncMutex); + Tcl_MutexUnlock(&asyncMutex); return code; } @@ -271,9 +378,7 @@ 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 @@ -283,33 +388,22 @@ Tcl_AsyncDelete( 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"); + Tcl_MutexLock(&asyncMutex); + if (asyncPtr->prevPtr == NULL) { + firstHandler = asyncPtr->nextPtr; + if (firstHandler == NULL) { + lastHandler = NULL; } - if (asyncPtr == tsdPtr->firstHandler) { - tsdPtr->firstHandler = asyncPtr->nextPtr; - } else { - prevPtr->nextPtr = asyncPtr->nextPtr; - } - if (asyncPtr == tsdPtr->lastHandler) { - tsdPtr->lastHandler = prevPtr; + } else { + asyncPtr->prevPtr->nextPtr = asyncPtr->nextPtr; + if (asyncPtr == lastHandler) { + lastHandler = asyncPtr->prevPtr; } } - Tcl_MutexUnlock(&tsdPtr->asyncMutex); + if (asyncPtr->nextPtr != NULL) { + asyncPtr->nextPtr->prevPtr = asyncPtr->prevPtr; + } + Tcl_MutexUnlock(&asyncMutex); ckfree(asyncPtr); } |