From 46d3c7a201e8862c6e18d23809f5245cc2356ab9 Mon Sep 17 00:00:00 2001 From: davygrvy Date: Sun, 21 Dec 2003 23:50:13 +0000 Subject: * generic/tkEvent.c: Added three new functions: TkCreateExitHandler, * generic/tkInt.h: TkDeleteExitHandler, and TkFinalize. This adds * generic/tkMenu.c: an insertion point so Tk's exit handlers can be * generic/tkWindow.c: called on their own from tk85.dll's DllMain * mac/tkMacButton.c: for DLL_PROCESS_DETACH. These are private to * unix/tkUnixEvent.c: the binary and not exported. It is possible * win/tkWin32Dll.c: the Windows OS can unload Tk _prior_ to Tcl * win/tkWinEmbed.c: under some conditions such as ExitProcess(). * win/tkWinMenu.c: This avoids a dangling pointer problem when Tcl * win/tkWinX.c: does Tcl_Finalize after Tk has been unloaded. * win/winMain.c: DllMain's DLL_PROCESS_DETACH now protected with SEH as DeleteWindowsExitProc is causing an exception of its own under some teardown conditions. AT&T assembly syntax has not been added for MinGW yet. [Tcl Patch 858493] FossilOrigin-Name: e12249878fe2fa70e532f6629a899a96c165e3e3 --- ChangeLog | 18 +++++++ generic/tkEvent.c | 148 ++++++++++++++++++++++++++++++++++++++++++++++++++++- generic/tkInt.h | 7 ++- generic/tkMenu.c | 4 +- generic/tkWindow.c | 9 ++-- mac/tkMacButton.c | 4 +- unix/tkUnixEvent.c | 4 +- win/tkWin32Dll.c | 37 ++++++++++++-- win/tkWinEmbed.c | 6 +-- win/tkWinMenu.c | 4 +- win/tkWinX.c | 4 +- win/winMain.c | 4 +- 12 files changed, 223 insertions(+), 26 deletions(-) diff --git a/ChangeLog b/ChangeLog index f6d3e39..d25184a 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,21 @@ +2003-12-21 David Gravereaux + + * generic/tkEvent.c: Added three new functions: TkCreateExitHandler, + * generic/tkInt.h: TkDeleteExitHandler, and TkFinalize. This adds + * generic/tkMenu.c: an insertion point so Tk's exit handlers can be + * generic/tkWindow.c: called on their own from tk85.dll's DllMain + * mac/tkMacButton.c: for DLL_PROCESS_DETACH. These are private to + * unix/tkUnixEvent.c: the binary and not exported. It is possible + * win/tkWin32Dll.c: the Windows OS can unload Tk _prior_ to Tcl + * win/tkWinEmbed.c: under some conditions such as ExitProcess(). + * win/tkWinMenu.c: This avoids a dangling pointer problem when Tcl + * win/tkWinX.c: does Tcl_Finalize after Tk has been unloaded. + * win/winMain.c: DllMain's DLL_PROCESS_DETACH now protected with + SEH as DeleteWindowsExitProc is causing an + exception of its own under some teardown + conditions. AT&T assembly syntax has not been + added for MinGW yet. [Tcl Patch 858493] + 2003-12-20 Joe English * library/bgerror.tcl: Truncate displayed error message if it's too long (fixes: #231251) diff --git a/generic/tkEvent.c b/generic/tkEvent.c index f95bf9e..f6bf9d4 100644 --- a/generic/tkEvent.c +++ b/generic/tkEvent.c @@ -11,7 +11,7 @@ * See the file "license.terms" for information on usage and redistribution * of this file, and for a DISCLAIMER OF ALL WARRANTIES. * - * RCS: @(#) $Id: tkEvent.c,v 1.18 2003/07/19 01:01:36 hobbs Exp $ + * RCS: @(#) $Id: tkEvent.c,v 1.19 2003/12/21 23:50:13 davygrvy Exp $ */ #include "tkPort.h" @@ -154,6 +154,29 @@ typedef struct ThreadSpecificData { static Tcl_ThreadDataKey dataKey; /* + * For each exit handler created with a call to TkCreateExitHandler + * there is a structure of the following type: + */ + +typedef struct ExitHandler { + Tcl_ExitProc *proc; /* Procedure to call when process exits. */ + ClientData clientData; /* One word of information to pass to proc. */ + struct ExitHandler *nextPtr;/* Next in list of all exit handlers for + * this application, or NULL for end of list. */ +} ExitHandler; + +/* + * There is both per-process and per-thread exit handlers. + * The first list is controlled by a mutex. The other is in + * thread local storage. + */ + +static ExitHandler *firstExitPtr = NULL; + /* First in list of all exit handlers for + * application. */ +TCL_DECLARE_MUTEX(exitMutex) + +/* * Prototypes for procedures that are only referenced locally within * this file. */ @@ -1458,6 +1481,129 @@ DelayedMotionProc(clientData) } /* + *--------------------------------------------------------------------------- + * + * TkCreateExitHandler -- + * + * Same as Tcl_CreateExitHandler, but private to Tk. + * + * Results: + * None. + * + * Side effects. + * Sets a handler with Tcl_CreateExitHandler if this is the first call. + * + *--------------------------------------------------------------------------- + */ + +void +TkCreateExitHandler (proc, clientData) + Tcl_ExitProc *proc; /* Procedure to invoke. */ + ClientData clientData; /* Arbitrary value to pass to proc. */ +{ + ExitHandler *exitPtr; + + exitPtr = (ExitHandler *) ckalloc(sizeof(ExitHandler)); + exitPtr->proc = proc; + exitPtr->clientData = clientData; + Tcl_MutexLock(&exitMutex); + if (firstExitPtr == NULL) { + Tcl_CreateExitHandler(TkFinalize, NULL); + } + exitPtr->nextPtr = firstExitPtr; + firstExitPtr = exitPtr; + Tcl_MutexUnlock(&exitMutex); +} + +/* + *--------------------------------------------------------------------------- + * + * TkDeleteExitHandler -- + * + * Same as Tcl_DeleteExitHandler, but private to Tk. + * + * Results: + * None. + * + * Side effects. + * None. + * + *--------------------------------------------------------------------------- + */ + +void +TkDeleteExitHandler (proc, clientData) + Tcl_ExitProc *proc; /* Procedure that was previously registered. */ + ClientData clientData; /* Arbitrary value to pass to proc. */ +{ + ExitHandler *exitPtr, *prevPtr; + + Tcl_MutexLock(&exitMutex); + for (prevPtr = NULL, exitPtr = firstExitPtr; exitPtr != NULL; + prevPtr = exitPtr, exitPtr = exitPtr->nextPtr) { + if ((exitPtr->proc == proc) + && (exitPtr->clientData == clientData)) { + if (prevPtr == NULL) { + firstExitPtr = exitPtr->nextPtr; + } else { + prevPtr->nextPtr = exitPtr->nextPtr; + } + ckfree((char *) exitPtr); + break; + } + } + Tcl_MutexUnlock(&exitMutex); + return; +} + +/* + *--------------------------------------------------------------------------- + * + * TkFinalize -- + * + * Runs our private exit handlers and removes itself from Tcl. This is + * benificial should we want to protect from dangling pointers should + * the Tk shared library be unloaded prior to Tcl which can happen on + * windows should the process be forcefully exiting from an exception + * handler. + * + * Results: + * None. + * + * Side effects. + * None. + * + *--------------------------------------------------------------------------- + */ + +void +TkFinalize (clientData) + ClientData clientData; /* Arbitrary value to pass to proc. */ +{ + ExitHandler *exitPtr; + + Tcl_DeleteExitHandler(TkFinalize, NULL); + + Tcl_MutexLock(&exitMutex); + for (exitPtr = firstExitPtr; exitPtr != NULL; exitPtr = firstExitPtr) { + /* + * Be careful to remove the handler from the list before + * invoking its callback. This protects us against + * double-freeing if the callback should call + * TkDeleteExitHandler on itself. + */ + + firstExitPtr = exitPtr->nextPtr; + Tcl_MutexUnlock(&exitMutex); + (*exitPtr->proc)(exitPtr->clientData); + ckfree((char *) exitPtr); + Tcl_MutexLock(&exitMutex); + } + firstExitPtr = NULL; + Tcl_MutexUnlock(&exitMutex); +} + +/* *-------------------------------------------------------------- * * Tk_MainLoop -- diff --git a/generic/tkInt.h b/generic/tkInt.h index a90f2d6..635229a 100644 --- a/generic/tkInt.h +++ b/generic/tkInt.h @@ -11,7 +11,7 @@ * See the file "license.terms" for information on usage and redistribution * of this file, and for a DISCLAIMER OF ALL WARRANTIES. * - * RCS: $Id: tkInt.h,v 1.60 2003/10/13 03:41:37 hobbs Exp $ + * RCS: $Id: tkInt.h,v 1.61 2003/12/21 23:50:13 davygrvy Exp $ */ #ifndef _TKINT @@ -1167,6 +1167,11 @@ EXTERN char * TkTilePrintProc _ANSI_ARGS_(( Tcl_FreeProc **freeProcPtr)); EXTERN XEvent * TkpGetBindingXEvent _ANSI_ARGS_(( Tcl_Interp *interp)); +EXTERN void TkCreateExitHandler _ANSI_ARGS_((Tcl_ExitProc *proc, + ClientData clientData)); +EXTERN void TkDeleteExitHandler _ANSI_ARGS_((Tcl_ExitProc *proc, + ClientData clientData)); +EXTERN Tcl_ExitProc TkFinalize; /* * Unsupported commands. diff --git a/generic/tkMenu.c b/generic/tkMenu.c index 9590b59..868268a 100644 --- a/generic/tkMenu.c +++ b/generic/tkMenu.c @@ -12,7 +12,7 @@ * See the file "license.terms" for information on usage and redistribution * of this file, and for a DISCLAIMER OF ALL WARRANTIES. * - * RCS: @(#) $Id: tkMenu.c,v 1.23 2003/12/03 16:38:23 dkf Exp $ + * RCS: @(#) $Id: tkMenu.c,v 1.24 2003/12/21 23:50:13 davygrvy Exp $ */ /* @@ -3594,7 +3594,7 @@ TkMenuInit() /* * Make sure we cleanup on finalize. */ - Tcl_CreateExitHandler((Tcl_ExitProc *) TkMenuCleanup, NULL); + TkCreateExitHandler((Tcl_ExitProc *) TkMenuCleanup, NULL); Tcl_MutexUnlock(&menuMutex); } if (!tsdPtr->menusInitialized) { diff --git a/generic/tkWindow.c b/generic/tkWindow.c index fde6736..d14f125 100644 --- a/generic/tkWindow.c +++ b/generic/tkWindow.c @@ -12,7 +12,7 @@ * See the file "license.terms" for information on usage and redistribution * of this file, and for a DISCLAIMER OF ALL WARRANTIES. * - * RCS: @(#) $Id: tkWindow.c,v 1.60 2003/07/18 13:24:19 vincentdarley Exp $ + * RCS: @(#) $Id: tkWindow.c,v 1.61 2003/12/21 23:50:13 davygrvy Exp $ */ #include "tkPort.h" @@ -360,7 +360,7 @@ CreateTopLevelWindow(interp, parent, name, screenName, flags) * exits. */ - Tcl_CreateExitHandler(DeleteWindowsExitProc, (ClientData) NULL); + TkCreateExitHandler(DeleteWindowsExitProc, (ClientData) tsdPtr); } if ((parent != NULL) && (screenName != NULL) && (screenName[0] == '\0')) { @@ -2705,12 +2705,11 @@ Tk_GetNumMainWindows() static void DeleteWindowsExitProc(clientData) - ClientData clientData; /* Not used. */ + ClientData clientData; /* tsdPtr when handler was created. */ { TkDisplay *dispPtr, *nextPtr; Tcl_Interp *interp; - ThreadSpecificData *tsdPtr = (ThreadSpecificData *) - Tcl_GetThreadData(&dataKey, sizeof(ThreadSpecificData)); + ThreadSpecificData *tsdPtr = (ThreadSpecificData *) clientData; /* * Finish destroying any windows that are in a diff --git a/mac/tkMacButton.c b/mac/tkMacButton.c index 4bc0fdf..a055609 100644 --- a/mac/tkMacButton.c +++ b/mac/tkMacButton.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: tkMacButton.c,v 1.19 2003/10/10 20:19:51 hobbs Exp $ + * RCS: @(#) $Id: tkMacButton.c,v 1.20 2003/12/21 23:50:13 davygrvy Exp $ */ #include "tkButton.h" @@ -1272,7 +1272,7 @@ InitSampleControls() * code it includes will crash the Mac on exit from Tk. oldPixPtr = ((CWindowPeek) windowRef)->port.portPixMap; - Tcl_CreateExitHandler(ButtonExitProc, (ClientData) NULL); + TkCreateExitHandler(ButtonExitProc, (ClientData) NULL); */ } diff --git a/unix/tkUnixEvent.c b/unix/tkUnixEvent.c index de250a1..b4c1654 100644 --- a/unix/tkUnixEvent.c +++ b/unix/tkUnixEvent.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: tkUnixEvent.c,v 1.12 2003/07/02 09:22:45 mdejong Exp $ + * RCS: @(#) $Id: tkUnixEvent.c,v 1.13 2003/12/21 23:50:13 davygrvy Exp $ */ #include "tkInt.h" @@ -81,7 +81,7 @@ TkCreateXEventSource() if (!tsdPtr->initialized) { tsdPtr->initialized = 1; Tcl_CreateEventSource(DisplaySetupProc, DisplayCheckProc, NULL); - Tcl_CreateExitHandler(DisplayExitHandler, NULL); + TkCreateExitHandler(DisplayExitHandler, NULL); } } diff --git a/win/tkWin32Dll.c b/win/tkWin32Dll.c index 4570a5a..f5aa7ed 100644 --- a/win/tkWin32Dll.c +++ b/win/tkWin32Dll.c @@ -8,10 +8,11 @@ * See the file "license.terms" for information on usage and redistribution * of this file, and for a DISCLAIMER OF ALL WARRANTIES. * - * RCS: @(#) $Id: tkWin32Dll.c,v 1.6 2002/12/08 00:46:51 hobbs Exp $ + * RCS: @(#) $Id: tkWin32Dll.c,v 1.7 2003/12/21 23:50:13 davygrvy Exp $ */ #include "tkWinInt.h" +#ifndef STATIC_BUILD /* * The following declaration is for the VC++ DLL entry point. @@ -61,7 +62,9 @@ DllEntryPoint(hInst, reason, reserved) * Always TRUE. * * Side effects: - * None. + * This might call some sycronization functions, but MSDN + * documentation states: "Waiting on synchronization objects in + * DllMain can cause a deadlock." * *---------------------------------------------------------------------- */ @@ -77,8 +80,34 @@ DllMain(hInstance, reason, reserved) * the hInstance to use. */ - if (reason == DLL_PROCESS_ATTACH) { + switch (reason) { + case DLL_PROCESS_ATTACH: + DisableThreadLibraryCalls(hInstance); TkWinSetHINSTANCE(hInstance); + break; + + case DLL_PROCESS_DETACH: + /* + * Protect the call to TkFinalize in an SEH block. We can't + * be guarenteed Tk is always being unloaded from a stable + * condition. + */ + + __try { + /* + * Run and remove our exit handlers, if they haven't already + * been run. Just in case we are being unloaded prior to + * Tcl (it can happen), we won't leave any dangling pointers + * hanging around for when Tcl gets unloaded later. + */ + + TkFinalize(NULL); + } __except (EXCEPTION_EXECUTE_HANDLER) { + /* empty handler body */ + } + break; } - return (TRUE); + return TRUE; } +#endif /* !STATIC_BUILD */ + diff --git a/win/tkWinEmbed.c b/win/tkWinEmbed.c index 19d4506..62ffbb3 100644 --- a/win/tkWinEmbed.c +++ b/win/tkWinEmbed.c @@ -11,7 +11,7 @@ * See the file "license.terms" for information on usage and redistribution * of this file, and for a DISCLAIMER OF ALL WARRANTIES. * - * RCS: @(#) $Id: tkWinEmbed.c,v 1.8 2003/12/13 01:07:35 davygrvy Exp $ + * RCS: @(#) $Id: tkWinEmbed.c,v 1.9 2003/12/21 23:50:13 davygrvy Exp $ */ #include "tkWinInt.h" @@ -208,7 +208,7 @@ TkpUseWindow(interp, tkwin, string) */ if (tsdPtr->firstContainerPtr == (Container *) NULL) { - Tcl_CreateExitHandler(CleanupContainerList, (ClientData) NULL); + TkCreateExitHandler(CleanupContainerList, (ClientData) NULL); } /* @@ -284,7 +284,7 @@ TkpMakeContainer(tkwin) */ if (tsdPtr->firstContainerPtr == (Container *) NULL) { - Tcl_CreateExitHandler(CleanupContainerList, (ClientData) NULL); + TkCreateExitHandler(CleanupContainerList, (ClientData) NULL); } /* diff --git a/win/tkWinMenu.c b/win/tkWinMenu.c index bf8c2d4..f366cf7 100644 --- a/win/tkWinMenu.c +++ b/win/tkWinMenu.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: tkWinMenu.c,v 1.25 2003/12/16 03:12:51 davygrvy Exp $ + * RCS: @(#) $Id: tkWinMenu.c,v 1.26 2003/12/21 23:50:13 davygrvy Exp $ */ #define OEMRESOURCE @@ -2990,7 +2990,7 @@ TkpMenuInit() tsdPtr->menuHWND = CreateWindow(MENU_CLASS_NAME, "MenuWindow", WS_POPUP, 0, 0, 10, 10, NULL, NULL, Tk_GetHINSTANCE(), NULL); - Tcl_CreateExitHandler(MenuExitHandler, (ClientData) NULL); + TkCreateExitHandler(MenuExitHandler, (ClientData) NULL); SetDefaults(1); } diff --git a/win/tkWinX.c b/win/tkWinX.c index 6a1e292..8a4fe05 100644 --- a/win/tkWinX.c +++ b/win/tkWinX.c @@ -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: tkWinX.c,v 1.26 2003/12/13 01:50:29 davygrvy Exp $ + * RCS: @(#) $Id: tkWinX.c,v 1.27 2003/12/21 23:50:13 davygrvy Exp $ */ #include "tkWinInt.h" @@ -277,7 +277,7 @@ TkWinXInit(hInstance) /* * Make sure we cleanup on finalize. */ - Tcl_CreateExitHandler((Tcl_ExitProc *) TkWinXCleanup, + TkCreateExitHandler((Tcl_ExitProc *) TkWinXCleanup, (ClientData) hInstance); } diff --git a/win/winMain.c b/win/winMain.c index ba203c4..84fe4f9 100644 --- a/win/winMain.c +++ b/win/winMain.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: winMain.c,v 1.18 2003/12/12 00:45:33 davygrvy Exp $ + * RCS: @(#) $Id: winMain.c,v 1.19 2003/12/21 23:50:13 davygrvy Exp $ */ #include @@ -174,7 +174,7 @@ Tcl_AppInit(interp) * This exit handler will be used to free the * resources allocated in this file. */ - Tcl_CreateExitHandler(AppInitExitHandler, NULL); + TkCreateExitHandler(AppInitExitHandler, NULL); /* * Initialize the console only if we are running as an interactive -- cgit v0.12