*** generic/tkEvent.c 19 Jul 2003 01:01:36 -0000 1.18 --- generic/tkEvent.c 21 Dec 2003 22:57:12 -0000 *************** *** 154,159 **** --- 154,182 ---- 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. */ *************** *** 1455,1460 **** --- 1478,1606 ---- } Tcl_QueueEvent(&dispPtr->delayedMotionPtr->header, TCL_QUEUE_TAIL); dispPtr->delayedMotionPtr = NULL; + } + + /* + *--------------------------------------------------------------------------- + * + * 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); } /* *** generic/tkInt.h 13 Oct 2003 03:41:37 -0000 1.60 --- generic/tkInt.h 21 Dec 2003 22:57:15 -0000 *************** *** 1167,1172 **** --- 1167,1177 ---- 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. *** generic/tkMenu.c 3 Dec 2003 16:38:23 -0000 1.23 --- generic/tkMenu.c 21 Dec 2003 22:57:19 -0000 *************** *** 3594,3600 **** /* * Make sure we cleanup on finalize. */ ! Tcl_CreateExitHandler((Tcl_ExitProc *) TkMenuCleanup, NULL); Tcl_MutexUnlock(&menuMutex); } if (!tsdPtr->menusInitialized) { --- 3594,3600 ---- /* * Make sure we cleanup on finalize. */ ! TkCreateExitHandler((Tcl_ExitProc *) TkMenuCleanup, NULL); Tcl_MutexUnlock(&menuMutex); } if (!tsdPtr->menusInitialized) { *** generic/tkWindow.c 18 Jul 2003 13:24:19 -0000 1.60 --- generic/tkWindow.c 21 Dec 2003 22:57:22 -0000 *************** *** 360,366 **** * exits. */ ! Tcl_CreateExitHandler(DeleteWindowsExitProc, (ClientData) NULL); } if ((parent != NULL) && (screenName != NULL) && (screenName[0] == '\0')) { --- 360,366 ---- * exits. */ ! TkCreateExitHandler(DeleteWindowsExitProc, (ClientData) tsdPtr); } if ((parent != NULL) && (screenName != NULL) && (screenName[0] == '\0')) { *************** *** 2705,2716 **** static void DeleteWindowsExitProc(clientData) ! ClientData clientData; /* Not used. */ { TkDisplay *dispPtr, *nextPtr; Tcl_Interp *interp; ! ThreadSpecificData *tsdPtr = (ThreadSpecificData *) ! Tcl_GetThreadData(&dataKey, sizeof(ThreadSpecificData)); /* * Finish destroying any windows that are in a --- 2705,2715 ---- static void DeleteWindowsExitProc(clientData) ! ClientData clientData; /* tsdPtr when handler was created. */ { TkDisplay *dispPtr, *nextPtr; Tcl_Interp *interp; ! ThreadSpecificData *tsdPtr = (ThreadSpecificData *) clientData; /* * Finish destroying any windows that are in a *** mac/tkMacButton.c 10 Oct 2003 20:19:51 -0000 1.19 --- mac/tkMacButton.c 21 Dec 2003 22:57:25 -0000 *************** *** 1272,1278 **** * code it includes will crash the Mac on exit from Tk. oldPixPtr = ((CWindowPeek) windowRef)->port.portPixMap; ! Tcl_CreateExitHandler(ButtonExitProc, (ClientData) NULL); */ } --- 1272,1278 ---- * code it includes will crash the Mac on exit from Tk. oldPixPtr = ((CWindowPeek) windowRef)->port.portPixMap; ! TkCreateExitHandler(ButtonExitProc, (ClientData) NULL); */ } *** unix/tkUnixEvent.c 2 Jul 2003 09:22:45 -0000 1.12 --- unix/tkUnixEvent.c 21 Dec 2003 22:57:28 -0000 *************** *** 81,87 **** if (!tsdPtr->initialized) { tsdPtr->initialized = 1; Tcl_CreateEventSource(DisplaySetupProc, DisplayCheckProc, NULL); ! Tcl_CreateExitHandler(DisplayExitHandler, NULL); } } --- 81,87 ---- if (!tsdPtr->initialized) { tsdPtr->initialized = 1; Tcl_CreateEventSource(DisplaySetupProc, DisplayCheckProc, NULL); ! TkCreateExitHandler(DisplayExitHandler, NULL); } } *** win/tkWin32Dll.c 8 Dec 2002 00:46:51 -0000 1.6 --- win/tkWin32Dll.c 21 Dec 2003 22:57:29 -0000 *************** *** 12,17 **** --- 12,18 ---- */ #include "tkWinInt.h" + #ifndef STATIC_BUILD /* * The following declaration is for the VC++ DLL entry point. *************** *** 61,67 **** * Always TRUE. * * Side effects: ! * None. * *---------------------------------------------------------------------- */ --- 62,70 ---- * Always TRUE. * * Side effects: ! * This might call some sycronization functions, but MSDN ! * documentation states: "Waiting on synchronization objects in ! * DllMain can cause a deadlock." * *---------------------------------------------------------------------- */ *************** *** 77,84 **** * the hInstance to use. */ ! if (reason == DLL_PROCESS_ATTACH) { TkWinSetHINSTANCE(hInstance); } ! return (TRUE); } --- 80,113 ---- * the hInstance to use. */ ! 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; } + #endif /* !STATIC_BUILD */ + *** win/tkWinEmbed.c 13 Dec 2003 01:07:35 -0000 1.8 --- win/tkWinEmbed.c 21 Dec 2003 22:57:31 -0000 *************** *** 208,214 **** */ if (tsdPtr->firstContainerPtr == (Container *) NULL) { ! Tcl_CreateExitHandler(CleanupContainerList, (ClientData) NULL); } /* --- 208,214 ---- */ if (tsdPtr->firstContainerPtr == (Container *) NULL) { ! TkCreateExitHandler(CleanupContainerList, (ClientData) NULL); } /* *************** *** 284,290 **** */ if (tsdPtr->firstContainerPtr == (Container *) NULL) { ! Tcl_CreateExitHandler(CleanupContainerList, (ClientData) NULL); } /* --- 284,290 ---- */ if (tsdPtr->firstContainerPtr == (Container *) NULL) { ! TkCreateExitHandler(CleanupContainerList, (ClientData) NULL); } /* *** win/tkWinMenu.c 16 Dec 2003 03:12:51 -0000 1.25 --- win/tkWinMenu.c 21 Dec 2003 22:57:35 -0000 *************** *** 2990,2996 **** tsdPtr->menuHWND = CreateWindow(MENU_CLASS_NAME, "MenuWindow", WS_POPUP, 0, 0, 10, 10, NULL, NULL, Tk_GetHINSTANCE(), NULL); ! Tcl_CreateExitHandler(MenuExitHandler, (ClientData) NULL); SetDefaults(1); } --- 2990,2996 ---- tsdPtr->menuHWND = CreateWindow(MENU_CLASS_NAME, "MenuWindow", WS_POPUP, 0, 0, 10, 10, NULL, NULL, Tk_GetHINSTANCE(), NULL); ! TkCreateExitHandler(MenuExitHandler, (ClientData) NULL); SetDefaults(1); } *** win/tkWinX.c 13 Dec 2003 01:50:29 -0000 1.26 --- win/tkWinX.c 21 Dec 2003 22:57:37 -0000 *************** *** 277,283 **** /* * Make sure we cleanup on finalize. */ ! Tcl_CreateExitHandler((Tcl_ExitProc *) TkWinXCleanup, (ClientData) hInstance); } --- 277,283 ---- /* * Make sure we cleanup on finalize. */ ! TkCreateExitHandler((Tcl_ExitProc *) TkWinXCleanup, (ClientData) hInstance); } *** win/winMain.c 12 Dec 2003 00:45:33 -0000 1.18 --- win/winMain.c 21 Dec 2003 22:57:39 -0000 *************** *** 174,180 **** * This exit handler will be used to free the * resources allocated in this file. */ ! Tcl_CreateExitHandler(AppInitExitHandler, NULL); /* * Initialize the console only if we are running as an interactive --- 174,180 ---- * This exit handler will be used to free the * resources allocated in this file. */ ! TkCreateExitHandler(AppInitExitHandler, NULL); /* * Initialize the console only if we are running as an interactive