summaryrefslogtreecommitdiffstats
path: root/generic/tclAsync.c
diff options
context:
space:
mode:
Diffstat (limited to 'generic/tclAsync.c')
-rw-r--r--generic/tclAsync.c222
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);
}
/*