From 54614f84cbcd150e2663bd4d30fe843b5e6da767 Mon Sep 17 00:00:00 2001 From: davidg Date: Wed, 26 Jul 2000 01:27:58 +0000 Subject: Thread-safe rewrite for the Tcl_Async* commands. --- generic/tclAsync.c | 136 ++++++++++++++++++++++++++++++++--------------------- mac/tclMacPort.h | 3 +- unix/tclUnixPort.h | 3 +- win/tclWinInit.c | 46 +----------------- 4 files changed, 85 insertions(+), 103 deletions(-) diff --git a/generic/tclAsync.c b/generic/tclAsync.c index fc80385..8d6757f 100644 --- a/generic/tclAsync.c +++ b/generic/tclAsync.c @@ -12,12 +12,15 @@ * See the file "license.terms" for information on usage and redistribution * of this file, and for a DISCLAIMER OF ALL WARRANTIES. * - * RCS: @(#) $Id: tclAsync.c,v 1.4 1999/04/16 00:46:42 stanton Exp $ + * RCS: @(#) $Id: tclAsync.c,v 1.5 2000/07/26 01:28:35 davidg Exp $ */ #include "tclInt.h" #include "tclPort.h" +/* Forward declaration */ +struct ThreadSpecificData; + /* * One of the following structures exists for each asynchronous * handler: @@ -33,34 +36,49 @@ typedef struct AsyncHandler { * 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; -/* - * The variables below maintain a list of all existing handlers. - */ -static AsyncHandler *firstHandler; /* First handler defined for process, - * or NULL if none. */ -static AsyncHandler *lastHandler; /* Last handler or NULL. */ +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. */ -TCL_DECLARE_MUTEX(asyncMutex) /* Process-wide async handler lock */ + /* + * The variable below 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. + */ -/* - * The variable below 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 asyncReady; -static int asyncReady = 0; + /* + * The variable below indicates whether Tcl_AsyncInvoke is currently + * working. If so then we won't set asyncReady again until + * Tcl_AsyncInvoke returns. + */ -/* - * The variable below indicates whether Tcl_AsyncInvoke is currently - * working. If so then we won't set asyncReady again until - * Tcl_AsyncInvoke returns. - */ + int asyncActive; + +#ifdef TCL_THREADS + Tcl_Mutex asyncMutex; /* Thread-specific AsyncHandler linked-list lock */ +#endif + +} ThreadSpecificData; +static Tcl_ThreadDataKey dataKey; -static int asyncActive = 0; /* *---------------------------------------------------------------------- @@ -88,20 +106,24 @@ Tcl_AsyncCreate(proc, clientData) ClientData clientData; /* Argument to pass to handler. */ { AsyncHandler *asyncPtr; + ThreadSpecificData *tsdPtr = TCL_TSD_INIT(&dataKey); asyncPtr = (AsyncHandler *) ckalloc(sizeof(AsyncHandler)); asyncPtr->ready = 0; asyncPtr->nextPtr = NULL; asyncPtr->proc = proc; asyncPtr->clientData = clientData; - Tcl_MutexLock(&asyncMutex); - if (firstHandler == NULL) { - firstHandler = asyncPtr; + asyncPtr->originTsd = tsdPtr; + asyncPtr->originThrdId = Tcl_GetCurrentThread(); + + Tcl_MutexLock(&tsdPtr->asyncMutex); + if (tsdPtr->firstHandler == NULL) { + tsdPtr->firstHandler = asyncPtr; } else { - lastHandler->nextPtr = asyncPtr; + tsdPtr->lastHandler->nextPtr = asyncPtr; } - lastHandler = asyncPtr; - Tcl_MutexUnlock(&asyncMutex); + tsdPtr->lastHandler = asyncPtr; + Tcl_MutexUnlock(&tsdPtr->asyncMutex); return (Tcl_AsyncHandler) asyncPtr; } @@ -128,13 +150,15 @@ void Tcl_AsyncMark(async) Tcl_AsyncHandler async; /* Token for handler. */ { - Tcl_MutexLock(&asyncMutex); - ((AsyncHandler *) async)->ready = 1; - if (!asyncActive) { - asyncReady = 1; - TclpAsyncMark(async); + 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(&asyncMutex); + Tcl_MutexUnlock(&token->originTsd->asyncMutex); } /* @@ -167,14 +191,16 @@ Tcl_AsyncInvoke(interp, code) * just completed. */ { AsyncHandler *asyncPtr; - Tcl_MutexLock(&asyncMutex); + ThreadSpecificData *tsdPtr = TCL_TSD_INIT(&dataKey); + + Tcl_MutexLock(&tsdPtr->asyncMutex); - if (asyncReady == 0) { - Tcl_MutexUnlock(&asyncMutex); + if (tsdPtr->asyncReady == 0) { + Tcl_MutexUnlock(&tsdPtr->asyncMutex); return code; } - asyncReady = 0; - asyncActive = 1; + tsdPtr->asyncReady = 0; + tsdPtr->asyncActive = 1; if (interp == NULL) { code = 0; } @@ -191,7 +217,7 @@ Tcl_AsyncInvoke(interp, code) */ while (1) { - for (asyncPtr = firstHandler; asyncPtr != NULL; + for (asyncPtr = tsdPtr->firstHandler; asyncPtr != NULL; asyncPtr = asyncPtr->nextPtr) { if (asyncPtr->ready) { break; @@ -201,12 +227,12 @@ Tcl_AsyncInvoke(interp, code) break; } asyncPtr->ready = 0; - Tcl_MutexUnlock(&asyncMutex); + Tcl_MutexUnlock(&tsdPtr->asyncMutex); code = (*asyncPtr->proc)(asyncPtr->clientData, interp, code); - Tcl_MutexLock(&asyncMutex); + Tcl_MutexLock(&tsdPtr->asyncMutex); } - asyncActive = 0; - Tcl_MutexUnlock(&asyncMutex); + tsdPtr->asyncActive = 0; + Tcl_MutexUnlock(&tsdPtr->asyncMutex); return code; } @@ -231,26 +257,27 @@ void Tcl_AsyncDelete(async) Tcl_AsyncHandler async; /* Token for handler to delete. */ { + ThreadSpecificData *tsdPtr = TCL_TSD_INIT(&dataKey); AsyncHandler *asyncPtr = (AsyncHandler *) async; AsyncHandler *prevPtr; - Tcl_MutexLock(&asyncMutex); - if (firstHandler == asyncPtr) { - firstHandler = asyncPtr->nextPtr; - if (firstHandler == NULL) { - lastHandler = NULL; + Tcl_MutexLock(&tsdPtr->asyncMutex); + if (tsdPtr->firstHandler == asyncPtr) { + tsdPtr->firstHandler = asyncPtr->nextPtr; + if (tsdPtr->firstHandler == NULL) { + tsdPtr->lastHandler = NULL; } } else { - prevPtr = firstHandler; + prevPtr = tsdPtr->firstHandler; while (prevPtr->nextPtr != asyncPtr) { prevPtr = prevPtr->nextPtr; } prevPtr->nextPtr = asyncPtr->nextPtr; - if (lastHandler == asyncPtr) { - lastHandler = prevPtr; + if (tsdPtr->lastHandler == asyncPtr) { + tsdPtr->lastHandler = prevPtr; } } - Tcl_MutexUnlock(&asyncMutex); + Tcl_MutexUnlock(&tsdPtr->asyncMutex); ckfree((char *) asyncPtr); } @@ -261,7 +288,7 @@ Tcl_AsyncDelete(async) * * This procedure can be used to tell whether Tcl_AsyncInvoke * needs to be called. This procedure is the external interface - * for checking the internal asyncReady variable. + * for checking the thread-specific asyncReady variable. * * Results: * The return value is 1 whenever a handler is ready and is 0 @@ -276,5 +303,6 @@ Tcl_AsyncDelete(async) int Tcl_AsyncReady() { - return asyncReady; + ThreadSpecificData *tsdPtr = TCL_TSD_INIT(&dataKey); + return tsdPtr->asyncReady; } diff --git a/mac/tclMacPort.h b/mac/tclMacPort.h index c69063e..18b0f2d 100644 --- a/mac/tclMacPort.h +++ b/mac/tclMacPort.h @@ -10,7 +10,7 @@ * See the file "license.terms" for information on usage and redistribution * of this file, and for a DISCLAIMER OF ALL WARRANTIES. * - * RCS: @(#) $Id: tclMacPort.h,v 1.10 1999/12/08 03:50:04 hobbs Exp $ + * RCS: @(#) $Id: tclMacPort.h,v 1.11 2000/07/26 01:28:24 davidg Exp $ */ @@ -224,7 +224,6 @@ extern char **environ; * address platform-specific issues. */ -#define TclpAsyncMark(async) #define TclpGetPid(pid) ((unsigned long) (pid)) #define TclSetSystemEnv(a,b) #define tzset() diff --git a/unix/tclUnixPort.h b/unix/tclUnixPort.h index cb3c777..d5b683c 100644 --- a/unix/tclUnixPort.h +++ b/unix/tclUnixPort.h @@ -19,7 +19,7 @@ * See the file "license.terms" for information on usage and redistribution * of this file, and for a DISCLAIMER OF ALL WARRANTIES. * - * RCS: @(#) $Id: tclUnixPort.h,v 1.15 2000/04/21 04:04:12 hobbs Exp $ + * RCS: @(#) $Id: tclUnixPort.h,v 1.16 2000/07/26 01:28:11 davidg Exp $ */ #ifndef _TCLUNIXPORT @@ -457,7 +457,6 @@ extern double strtod(); * address platform-specific issues. */ -#define TclpAsyncMark(async) #define TclpGetPid(pid) ((unsigned long) (pid)) #define TclpReleaseFile(file) /* Nothing. */ diff --git a/win/tclWinInit.c b/win/tclWinInit.c index 7576763..1f6d76f 100644 --- a/win/tclWinInit.c +++ b/win/tclWinInit.c @@ -7,7 +7,7 @@ * Copyright (c) 1998-1999 by Scriptics Corporation. * All rights reserved. * - * RCS: @(#) $Id: tclWinInit.c,v 1.23 2000/06/13 20:30:23 ericm Exp $ + * RCS: @(#) $Id: tclWinInit.c,v 1.24 2000/07/26 01:27:58 davidg Exp $ */ #include "tclWinInt.h" @@ -73,12 +73,6 @@ static char* processors[NUMPROCESSORS] = { }; /* - * Thread id used for asynchronous notification from signal handlers. - */ - -static DWORD mainThreadId; - -/* * The Init script (common to Windows and Unix platforms) is * defined in tkInitScript.h */ @@ -128,16 +122,6 @@ TclpInitPlatform() SetErrorMode(SetErrorMode(0) | SEM_FAILCRITICALERRORS); - /* - * Save the id of the first thread to intialize the Tcl library. This - * thread will be used to handle notifications from async event - * procedures. This is not strictly correct. A better solution involves - * using a designated "main" notifier that is kept up to date as threads - * come and go. - */ - - mainThreadId = GetCurrentThreadId(); - #ifdef STATIC_BUILD /* * If we are in a statically linked executable, then we need to @@ -814,31 +798,3 @@ Tcl_SourceRCFile(interp) Tcl_DStringFree(&temp); } } - -/* - *---------------------------------------------------------------------- - * - * TclpAsyncMark -- - * - * Wake up the main thread from a signal handler. - * - * Results: - * None. - * - * Side effects: - * Sends a message to the main thread. - * - *---------------------------------------------------------------------- - */ - -void -TclpAsyncMark(async) - Tcl_AsyncHandler async; /* Token for handler. */ -{ - /* - * Need a way to kick the Windows event loop and tell it to go look at - * asynchronous events. - */ - - PostThreadMessage(mainThreadId, WM_USER, 0, 0); -} -- cgit v0.12