*** 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

