From 3b86fed833e653cc528194dd085f1d9729b91bed Mon Sep 17 00:00:00 2001 From: davygrvy Date: Sat, 1 Sep 2001 22:53:44 +0000 Subject: -=[ Tcl_Async* API merge from HEAD ]=- * generic/tclInt.decls: Removed TclpAsyncMark permanently and left the hole not to upset the positions. * generic/tclDecls.h: * generic/tclIntDecls.h: * generic/tclIntPlatDecls.h: * generic/tclPlatDecls.h: * generic/tclStubInit.c: regen'd tables. * generic/tclAsync.c: Brought source to match -r1.6 on the HEAD branch. * unix/tclUnixPort.h: * mac/tclMacPort.h: removed #defines for TclpAsyncMark. * win/tclWinInit.c: Removed the TclpAsyncMark function and the thread ID saving that was going on in TclpInitPlatform(). [the old hack] * win/tclWinThread.c: TclpFinalizeCondition() and TclpFinalizeMutex() are now properly returning the old CriticalSection handle to the system. Tcl_CreateThread() is now decrementing its handle reference, so the system will recover resources when the thread closes (doh!). These changes are all already up on the HEAD. * doc/Async.3: matches HEAD. FossilOrigin-Name: 11201d88beb6033b27cee711d5fc97f252715813 --- doc/Async.3 | 9 ++- generic/tclAsync.c | 157 +++++++++++++++++++++++++++++++--------------- generic/tclInt.decls | 10 +-- generic/tclIntPlatDecls.h | 12 ++-- generic/tclStubInit.c | 4 +- mac/tclMacPort.h | 3 +- unix/tclUnixPort.h | 3 +- win/tclWinInit.c | 46 +------------- win/tclWinThrd.c | 18 ++++-- 9 files changed, 141 insertions(+), 121 deletions(-) diff --git a/doc/Async.3 b/doc/Async.3 index 81c5cd1..e0051f3 100644 --- a/doc/Async.3 +++ b/doc/Async.3 @@ -5,7 +5,7 @@ '\" See the file "license.terms" for information on usage and redistribution '\" of this file, and for a DISCLAIMER OF ALL WARRANTIES. '\" -'\" RCS: @(#) $Id: Async.3,v 1.4 2000/04/14 23:01:48 hobbs Exp $ +'\" RCS: @(#) $Id: Async.3,v 1.4.2.1 2001/09/01 22:53:45 davygrvy Exp $ '\" .so man.macros .TH Tcl_AsyncCreate 3 7.0 Tcl "Tcl Library Procedures" @@ -60,6 +60,13 @@ The only safe approach is to set a flag indicating that the event occurred, then handle the event later when the world has returned to a clean state, such as after the current Tcl command completes. .PP +\fBTcl_AsyncCreate\fR, \fBTcl_AsyncDelete\fR, and \fBTcl_AsyncReady\fR +are thread sensitive. They access and/or set a thread-specific data +structure in the event of an --enable-thread built core. The token +created by Tcl_AsyncCreate contains the needed thread information it +was called from so that calling Tcl_AsyncMark(token) will only yield +the origin thread into the AsyncProc. +.PP \fBTcl_AsyncCreate\fR creates an asynchronous handler and returns a token for it. The asynchronous handler must be created before diff --git a/generic/tclAsync.c b/generic/tclAsync.c index fc80385..d0ac056 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.4.12.1 2001/09/01 22:53:45 davygrvy Exp $ */ #include "tclInt.h" #include "tclPort.h" +/* Forward declaration */ +struct ThreadSpecificData; + /* * One of the following structures exists for each asynchronous * handler: @@ -33,34 +36,74 @@ 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; + + /* + * The variable below indicates whether Tcl_AsyncInvoke is currently + * working. If so then we won't set asyncReady again until + * Tcl_AsyncInvoke returns. + */ -static int asyncReady = 0; + int asyncActive; + Tcl_Mutex asyncMutex; /* Thread-specific AsyncHandler linked-list lock */ + +} ThreadSpecificData; +static Tcl_ThreadDataKey dataKey; + + /* - * The variable below indicates whether Tcl_AsyncInvoke is currently - * working. If so then we won't set asyncReady again until - * Tcl_AsyncInvoke returns. + *---------------------------------------------------------------------- + * + * TclFinalizeAsync -- + * + * Finalizes the mutex in the thread local data structure for the + * async subsystem. + * + * Results: + * None. + * + * Side effects: + * Forgets knowledge of the mutex should it have been created. + * + *---------------------------------------------------------------------- */ -static int asyncActive = 0; +void +TclFinalizeAsync() +{ + ThreadSpecificData *tsdPtr = TCL_TSD_INIT(&dataKey); + + if (tsdPtr->asyncMutex != NULL) { + Tcl_MutexFinalize(&tsdPtr->asyncMutex); + } +} /* *---------------------------------------------------------------------- @@ -88,20 +131,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 +175,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 +216,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 +242,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 +252,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 +282,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 +313,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 +328,6 @@ Tcl_AsyncDelete(async) int Tcl_AsyncReady() { - return asyncReady; + ThreadSpecificData *tsdPtr = TCL_TSD_INIT(&dataKey); + return tsdPtr->asyncReady; } diff --git a/generic/tclInt.decls b/generic/tclInt.decls index 215a058..3b945ba 100644 --- a/generic/tclInt.decls +++ b/generic/tclInt.decls @@ -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: tclInt.decls,v 1.20.2.2 2001/04/04 21:22:18 hobbs Exp $ +# RCS: @(#) $Id: tclInt.decls,v 1.20.2.3 2001/09/01 22:53:45 davygrvy Exp $ library tcl @@ -803,9 +803,11 @@ declare 19 win { declare 20 win { void TclWinAddProcess(HANDLE hProcess, DWORD id) } -declare 21 win { - void TclpAsyncMark(Tcl_AsyncHandler async) -} +# Removed permanently from 8.3.4 to match 8.4a2's removal +# +# declare 21 win { +# void TclpAsyncMark(Tcl_AsyncHandler async) +# } # Added in 8.1: declare 22 win { diff --git a/generic/tclIntPlatDecls.h b/generic/tclIntPlatDecls.h index 5462b90..9a55e15 100644 --- a/generic/tclIntPlatDecls.h +++ b/generic/tclIntPlatDecls.h @@ -9,7 +9,7 @@ * Copyright (c) 1998-1999 by Scriptics Corporation. * All rights reserved. * - * RCS: @(#) $Id: tclIntPlatDecls.h,v 1.8.2.2 2001/08/28 00:12:45 hobbs Exp $ + * RCS: @(#) $Id: tclIntPlatDecls.h,v 1.8.2.3 2001/09/01 22:53:45 davygrvy Exp $ */ #ifndef _TCLINTPLATDECLS @@ -115,8 +115,7 @@ EXTERN TclFile TclpOpenFile _ANSI_ARGS_((CONST char * fname, /* 20 */ EXTERN void TclWinAddProcess _ANSI_ARGS_((HANDLE hProcess, DWORD id)); -/* 21 */ -EXTERN void TclpAsyncMark _ANSI_ARGS_((Tcl_AsyncHandler async)); +/* Slot 21 is reserved */ /* 22 */ EXTERN TclFile TclpCreateTempFile _ANSI_ARGS_(( CONST char * contents)); @@ -236,7 +235,7 @@ typedef struct TclIntPlatStubs { TclFile (*tclpMakeFile) _ANSI_ARGS_((Tcl_Channel channel, int direction)); /* 18 */ TclFile (*tclpOpenFile) _ANSI_ARGS_((CONST char * fname, int mode)); /* 19 */ void (*tclWinAddProcess) _ANSI_ARGS_((HANDLE hProcess, DWORD id)); /* 20 */ - void (*tclpAsyncMark) _ANSI_ARGS_((Tcl_AsyncHandler async)); /* 21 */ + void *reserved21; TclFile (*tclpCreateTempFile) _ANSI_ARGS_((CONST char * contents)); /* 22 */ char * (*tclpGetTZName) _ANSI_ARGS_((int isdst)); /* 23 */ char * (*tclWinNoBackslash) _ANSI_ARGS_((char * path)); /* 24 */ @@ -399,10 +398,7 @@ extern TclIntPlatStubs *tclIntPlatStubsPtr; #define TclWinAddProcess \ (tclIntPlatStubsPtr->tclWinAddProcess) /* 20 */ #endif -#ifndef TclpAsyncMark -#define TclpAsyncMark \ - (tclIntPlatStubsPtr->tclpAsyncMark) /* 21 */ -#endif +/* Slot 21 is reserved */ #ifndef TclpCreateTempFile #define TclpCreateTempFile \ (tclIntPlatStubsPtr->tclpCreateTempFile) /* 22 */ diff --git a/generic/tclStubInit.c b/generic/tclStubInit.c index 2ca7c1d..1f0b58f 100644 --- a/generic/tclStubInit.c +++ b/generic/tclStubInit.c @@ -8,7 +8,7 @@ * See the file "license.terms" for information on usage and redistribution * of this file, and for a DISCLAIMER OF ALL WARRANTIES. * - * RCS: @(#) $Id: tclStubInit.c,v 1.35.2.3 2001/08/28 00:12:45 hobbs Exp $ + * RCS: @(#) $Id: tclStubInit.c,v 1.35.2.4 2001/09/01 22:53:45 davygrvy Exp $ */ #include "tclInt.h" @@ -277,7 +277,7 @@ TclIntPlatStubs tclIntPlatStubs = { TclpMakeFile, /* 18 */ TclpOpenFile, /* 19 */ TclWinAddProcess, /* 20 */ - TclpAsyncMark, /* 21 */ + NULL, /* 21 */ TclpCreateTempFile, /* 22 */ TclpGetTZName, /* 23 */ TclWinNoBackslash, /* 24 */ diff --git a/mac/tclMacPort.h b/mac/tclMacPort.h index 22451a8..72fc683 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.2.1 2001/04/04 21:22:19 hobbs Exp $ + * RCS: @(#) $Id: tclMacPort.h,v 1.10.2.2 2001/09/01 22:53:45 davygrvy Exp $ */ @@ -231,7 +231,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..731357e 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.15.2.1 2001/09/01 22:53:45 davygrvy 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 8328bb9..16fb280 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.22.2.2 2001/08/24 16:19:10 dgp Exp $ + * RCS: @(#) $Id: tclWinInit.c,v 1.22.2.3 2001/09/01 22:53:45 davygrvy 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 */ @@ -129,16 +123,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 @@ -827,31 +811,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); -} diff --git a/win/tclWinThrd.c b/win/tclWinThrd.c index 8fe2596..f76adf2 100644 --- a/win/tclWinThrd.c +++ b/win/tclWinThrd.c @@ -9,7 +9,7 @@ * See the file "license.terms" for information on usage and redistribution * of this file, and for a DISCLAIMER OF ALL WARRANTIES. * - * RCS: @(#) $Id: tclWinThrd.c,v 1.8 2000/04/20 01:30:20 hobbs Exp $ + * RCS: @(#) $Id: tclWinThrd.c,v 1.8.2.1 2001/09/01 22:53:45 davygrvy Exp $ */ #include "tclWinInt.h" @@ -123,13 +123,19 @@ Tcl_CreateThread(idPtr, proc, clientData, stackSize, flags) int flags; /* Flags controlling behaviour of * the new thread */ { - unsigned long code; + HANDLE tHandle; - code = _beginthreadex(NULL, stackSize, proc, clientData, 0, - (unsigned *)idPtr); - if (code == 0) { + tHandle = (HANDLE) _beginthreadex(NULL, (unsigned) stackSize, proc, + clientData, 0, (unsigned *)idPtr); + + if (tHandle == NULL) { return TCL_ERROR; } else { + /* + * The only purpose of this is to decrement the reference count so the + * OS resources will be reaquired when the thread closes. + */ + CloseHandle(tHandle); return TCL_OK; } } @@ -428,6 +434,7 @@ TclpFinalizeMutex(mutexPtr) { CRITICAL_SECTION *csPtr = *(CRITICAL_SECTION **)mutexPtr; if (csPtr != NULL) { + DeleteCriticalSection(csPtr); ckfree((char *)csPtr); *mutexPtr = NULL; } @@ -896,6 +903,7 @@ TclpFinalizeCondition(condPtr) */ if (winCondPtr != NULL) { + DeleteCriticalSection(&winCondPtr->condLock); ckfree((char *)winCondPtr); *condPtr = NULL; } -- cgit v0.12