From bd23cfcf8912fb898bef69b10cd34e3673d0af83 Mon Sep 17 00:00:00 2001
From: davygrvy <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]

---
 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 <davygrvy@pobox.com>
+
+	* 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  <jenglish@users.sourceforge.net>
 	* 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 <tk.h>
@@ -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