]> granicus.if.org Git - python/commitdiff
Fixed issue #5122: Synchronize tk load failure check to prevent a
authorGuilherme Polo <ggpolo@gmail.com>
Mon, 9 Feb 2009 20:50:27 +0000 (20:50 +0000)
committerGuilherme Polo <ggpolo@gmail.com>
Mon, 9 Feb 2009 20:50:27 +0000 (20:50 +0000)
potential deadlock.

Misc/NEWS
Modules/_tkinter.c
Modules/tkappinit.c
Modules/tkinter.h [new file with mode: 0644]

index 824b0d45a81c556bd15bc6c9c110bf56f6104ba2..c51315a999c6b02b31684f9b517a280785b6fe9c 100644 (file)
--- a/Misc/NEWS
+++ b/Misc/NEWS
@@ -152,6 +152,9 @@ Core and Builtins
 Library
 -------
 
+- Issue #5122: Synchronize tk load failure check to prevent a potential
+  deadlock.
+
 - Issue #4890: Handle empty text search pattern in Tkinter.Text.search.
 
 - Issue #5170: Fixed Unicode output bug in logging and added test case.
index 5b1de8c7a30d2aecdd2920f648c389624f7f89db..62bcff41dcefe07c8b5e3857a195b1ee1fbf5e97 100644 (file)
@@ -33,6 +33,8 @@ Copyright (C) 1994 Steen Lumholt.
 #include <windows.h>
 #endif
 
+#include "tkinter.h"
+
 /* Allow using this code in Python 2.[12] */
 #ifndef PyDoc_STRVAR
 #define PyDoc_STRVAR(name,str) static char name[] = str
@@ -74,9 +76,7 @@ Copyright (C) 1994 Steen Lumholt.
 #define CONST
 #endif
 
-#define TKMAJORMINOR (TK_MAJOR_VERSION*1000 + TK_MINOR_VERSION)
-
-#if TKMAJORMINOR < 8002
+#if TK_VERSION_HEX < 0x08020002
 #error "Tk older than 8.2 not supported"
 #endif
 
@@ -280,6 +280,9 @@ static PyObject *excInCmd;
 static PyObject *valInCmd;
 static PyObject *trbInCmd;
 
+#ifdef TKINTER_PROTECT_LOADTK
+static int tk_load_failed;
+#endif
 
 \f
 static PyObject *
@@ -555,21 +558,35 @@ SplitObj(PyObject *arg)
 int
 Tcl_AppInit(Tcl_Interp *interp)
 {
-       Tk_Window main;
        const char * _tkinter_skip_tk_init;
 
        if (Tcl_Init(interp) == TCL_ERROR) {
                PySys_WriteStderr("Tcl_Init error: %s\n", Tcl_GetStringResult(interp));
                return TCL_ERROR;
        }
-       _tkinter_skip_tk_init = Tcl_GetVar(interp, "_tkinter_skip_tk_init", TCL_GLOBAL_ONLY);
-       if (_tkinter_skip_tk_init == NULL || strcmp(_tkinter_skip_tk_init, "1") != 0) {
-               main = Tk_MainWindow(interp);
-               if (Tk_Init(interp) == TCL_ERROR) {
-                       PySys_WriteStderr("Tk_Init error: %s\n", Tcl_GetStringResult(interp));
-                       return TCL_ERROR;
-               }
+
+       _tkinter_skip_tk_init = Tcl_GetVar(interp,
+                       "_tkinter_skip_tk_init", TCL_GLOBAL_ONLY);
+       if (_tkinter_skip_tk_init != NULL &&
+                       strcmp(_tkinter_skip_tk_init, "1") == 0) {
+               return TCL_OK;
+       }
+
+#ifdef TKINTER_PROTECT_LOADTK
+       if (tk_load_failed) {
+               PySys_WriteStderr("Tk_Init error: %s\n", TKINTER_LOADTK_ERRMSG);
+               return TCL_ERROR;
+       }
+#endif
+
+       if (Tk_Init(interp) == TCL_ERROR) {
+#ifdef TKINTER_PROTECT_LOADTK
+               tk_load_failed = 1;
+#endif
+               PySys_WriteStderr("Tk_Init error: %s\n", Tcl_GetStringResult(interp));
+               return TCL_ERROR;
        }
+
        return TCL_OK;
 }
 #endif /* !WITH_APPINIT */
@@ -652,8 +669,15 @@ Tkapp_New(char *screenName, char *baseName, char *className,
        ckfree(argv0);
 
        if (! wantTk) {
-           Tcl_SetVar(v->interp, "_tkinter_skip_tk_init", "1", TCL_GLOBAL_ONLY);
+               Tcl_SetVar(v->interp,
+                               "_tkinter_skip_tk_init", "1", TCL_GLOBAL_ONLY);
+       }
+#ifdef TKINTER_PROTECT_LOADTK
+       else if (tk_load_failed) {
+               Tcl_SetVar(v->interp,
+                               "_tkinter_tk_failed", "1", TCL_GLOBAL_ONLY);
        }
+#endif
 
        /* some initial arguments need to be in argv */
        if (sync || use) {
@@ -688,6 +712,18 @@ Tkapp_New(char *screenName, char *baseName, char *className,
 
        if (Tcl_AppInit(v->interp) != TCL_OK) {
                PyObject *result = Tkinter_Error((PyObject *)v);
+#ifdef TKINTER_PROTECT_LOADTK
+               if (wantTk) {
+                       const char *_tkinter_tk_failed;
+                       _tkinter_tk_failed = Tcl_GetVar(v->interp,
+                                       "_tkinter_tk_failed", TCL_GLOBAL_ONLY);
+
+                       if ( _tkinter_tk_failed != NULL &&
+                                       strcmp(_tkinter_tk_failed, "1") == 0) {
+                               tk_load_failed = 1;
+                       }
+               }
+#endif
                Py_DECREF((PyObject *)v);
                return (TkappObject *)result;
        }
@@ -2669,23 +2705,22 @@ Tkapp_InterpAddr(PyObject *self, PyObject *args)
 static PyObject        *
 Tkapp_TkInit(PyObject *self, PyObject *args)
 {
-       static int has_failed;
        Tcl_Interp *interp = Tkapp_Interp(self);
-       Tk_Window main_window;
        const char * _tk_exists = NULL;
        int err;
-       main_window = Tk_MainWindow(interp);
-
-       /* In all current versions of Tk (including 8.4.13), Tk_Init
-          deadlocks on the second call when the first call failed.
-          To avoid the deadlock, we just refuse the second call through
-          a static variable. */
-       if (has_failed) {
-               PyErr_SetString(Tkinter_TclError, 
-                               "Calling Tk_Init again after a previous call failed might deadlock");
+
+#ifdef TKINTER_PROTECT_LOADTK
+       /* Up to Tk 8.4.13, Tk_Init deadlocks on the second call when the
+        * first call failed.
+        * To avoid the deadlock, we just refuse the second call through
+        * a static variable.
+        */
+       if (tk_load_failed) {
+               PyErr_SetString(Tkinter_TclError, TKINTER_LOADTK_ERRMSG);
                return NULL;
        }
-          
+#endif
+
        /* We want to guard against calling Tk_Init() multiple times */
        CHECK_TCL_APPARTMENT;
        ENTER_TCL
@@ -2704,8 +2739,10 @@ Tkapp_TkInit(PyObject *self, PyObject *args)
        }
        if (_tk_exists == NULL || strcmp(_tk_exists, "1") != 0) {
                if (Tk_Init(interp)     == TCL_ERROR) {
-                       PyErr_SetString(Tkinter_TclError, Tcl_GetStringResult(Tkapp_Interp(self)));
-                       has_failed = 1;
+                       PyErr_SetString(Tkinter_TclError, Tcl_GetStringResult(Tkapp_Interp(self)));
+#ifdef TKINTER_PROTECT_LOADTK
+                       tk_load_failed = 1;
+#endif
                        return NULL;
                }
        }
index de04b0dfb6e48df47fcdd0fb91ff282fbc780daa..cfbd20c4f4099ddeaa5c93ec51035d50d91ad536 100644 (file)
 #include <tcl.h>
 #include <tk.h>
 
+#include "tkinter.h"
+
+#ifdef TKINTER_PROTECT_LOADTK
+/* See Tkapp_TkInit in _tkinter.c for the usage of tk_load_faile */
+static int tk_load_failed;
+#endif
+
 int
 Tcl_AppInit(Tcl_Interp *interp)
 {
        Tk_Window main_window;
-       const char * _tkinter_skip_tk_init;
+       const char *_tkinter_skip_tk_init;
+#ifdef TKINTER_PROTECT_LOADTK
+       const char *_tkinter_tk_failed;
+#endif
 
 #ifdef TK_AQUA
 #ifndef MAX_PATH_LEN
@@ -74,12 +84,32 @@ Tcl_AppInit(Tcl_Interp *interp)
                /* Initialize modules that don't require Tk */
 #endif
 
-       _tkinter_skip_tk_init = Tcl_GetVar(interp, "_tkinter_skip_tk_init", TCL_GLOBAL_ONLY);
-       if (_tkinter_skip_tk_init != NULL && strcmp(_tkinter_skip_tk_init, "1") == 0) {
+       _tkinter_skip_tk_init = Tcl_GetVar(interp,
+                       "_tkinter_skip_tk_init", TCL_GLOBAL_ONLY);
+       if (_tkinter_skip_tk_init != NULL &&
+                       strcmp(_tkinter_skip_tk_init, "1") == 0) {
                return TCL_OK;
        }
-       if (Tk_Init(interp) == TCL_ERROR)
+
+#ifdef TKINTER_PROTECT_LOADTK
+       _tkinter_tk_failed = Tcl_GetVar(interp,
+                       "_tkinter_tk_failed", TCL_GLOBAL_ONLY);
+
+       if (tk_load_failed || (
+                               _tkinter_tk_failed != NULL &&
+                               strcmp(_tkinter_tk_failed, "1") == 0)) {
+               Tcl_SetResult(interp, TKINTER_LOADTK_ERRMSG, TCL_STATIC);
+               return TCL_ERROR;
+       }
+#endif
+
+       if (Tk_Init(interp) == TCL_ERROR) {
+#ifdef TKINTER_PROTECT_LOADTK
+               tk_load_failed = 1;
+               Tcl_SetVar(interp, "_tkinter_tk_failed", "1", TCL_GLOBAL_ONLY);
+#endif
                return TCL_ERROR;
+       }
 
        main_window = Tk_MainWindow(interp);
 
diff --git a/Modules/tkinter.h b/Modules/tkinter.h
new file mode 100644 (file)
index 0000000..57be878
--- /dev/null
@@ -0,0 +1,25 @@
+#ifndef TKINTER_H
+#define TKINTER_H
+
+/* This header is used to share some macros between _tkinter.c and
+ * tkappinit.c */
+
+/* TK_RELEASE_LEVEL is always one of the following:
+ *     TCL_ALPHA_RELEASE   0
+ *  TCL_BETA_RELEASE    1
+ *  TCL_FINAL_RELEASE   2
+ */
+#define TK_VERSION_HEX ((TK_MAJOR_VERSION << 24) | \
+               (TK_MINOR_VERSION << 16) | \
+               (TK_RELEASE_SERIAL << 8) | \
+               (TK_RELEASE_LEVEL << 0))
+
+/* Protect Tk 8.4.13 and older from a deadlock that happens when trying
+ * to load tk after a failed attempt. */
+#if TK_VERSION_HEX < 0x08040e02
+#define TKINTER_PROTECT_LOADTK
+#define TKINTER_LOADTK_ERRMSG \
+       "Calling Tk_Init again after a previous call failed might deadlock"
+#endif
+
+#endif /* !TKINTER_H */