]> granicus.if.org Git - python/commitdiff
Changes to make it possible to write multi-threaded programs using
authorGuido van Rossum <guido@python.org>
Thu, 28 May 1998 23:06:38 +0000 (23:06 +0000)
committerGuido van Rossum <guido@python.org>
Thu, 28 May 1998 23:06:38 +0000 (23:06 +0000)
Tkinter.  This adds a separate lock -- read the comments.  (This was
also needed for Mark Hammond's attempts to make PythonWin
Tkinter-friendly.)

The changes have affected the EventHook slightly, too; and I've done
some more cleanup of the code that deals with the different versions
of Tcl_CreateFileHandler().

Modules/_tkinter.c

index f63bd3805c278dd499390fdc95ab824a9c5d8780..d2bbff743e18f60686ff8ceab74c7e9a1858f01b 100644 (file)
@@ -58,6 +58,10 @@ PERFORMANCE OF THIS SOFTWARE.
 #include "Python.h"
 #include <ctype.h>
 
+#ifdef WITH_THREAD
+#include "thread.h"
+#endif
+
 #ifdef macintosh
 #define MAC_TCL
 #include "myselect.h"
@@ -68,6 +72,10 @@ PERFORMANCE OF THIS SOFTWARE.
 
 #define TKMAJORMINOR (TK_MAJOR_VERSION*1000 + TK_MINOR_VERSION)
 
+#if TKMAJORMINOR < 4001
+       #error "Tk 4.0 or 3.x are not supported -- use 4.1 or higher"
+#endif
+
 #if TKMAJORMINOR >= 8000 && defined(macintosh)
 /* Sigh, we have to include this to get at the tcl qd pointer */
 #include <tkMac.h>
@@ -75,14 +83,17 @@ PERFORMANCE OF THIS SOFTWARE.
 #include <Menus.h>
 #endif
 
-#if TKMAJORMINOR < 4001
-       #error "Tk 4.0 or 3.x are not supported -- use 4.1 or higher"
-#endif
-
 #if TKMAJORMINOR < 8000 || !defined(MS_WINDOWS)
 #define HAVE_CREATEFILEHANDLER
 #endif
 
+#ifdef HAVE_CREATEFILEHANDLER
+
+/* Tcl_CreateFileHandler() changed several times; these macros deal with the
+   messiness.  In Tcl 8.0 and later, it is not available on Windows (and on
+   Unix, only because Jack added it back); when available on Windows, it only
+   applies to sockets. */
+
 #ifdef MS_WINDOWS
 #define FHANDLETYPE TCL_WIN_SOCKET
 #else
@@ -97,10 +108,74 @@ PERFORMANCE OF THIS SOFTWARE.
 #define MAKEFHANDLE(fd) (fd)
 #endif
 
-#if defined(HAVE_CREATEFILEHANDLER) && !defined(MS_WINDOWS)
+/* If Tcl can wait for a Unix file descriptor, define the EventHook() routine
+   which uses this to handle Tcl events while the user is typing commands. */
+
+#if FHANDLETYPE == TCL_UNIX_FD
 #define WAIT_FOR_STDIN
 #endif
 
+#endif /* HAVE_CREATEFILEHANDLER */
+
+#ifdef WITH_THREAD
+
+/* The threading situation is complicated.  Tcl is not thread-safe, except for
+   Tcl 8.1, which will probably remain in alpha status for another 6 months
+   (and the README says that Tk will probably remain thread-unsafe forever).
+   So we need to use a lock around all uses of Tcl.  Previously, the Python
+   interpreter lock was used for this.  However, this causes problems when
+   other Python threads need to run while Tcl is blocked waiting for events.
+
+   To solve this problem, a separate lock for Tcl is introduced.  Holding it
+   is incompatible with holding Python's interpreter lock.  The following four
+   macros manipulate both locks together.
+
+   ENTER_TCL and LEAVE_TCL are brackets, just like Py_BEGIN_ALLOW_THREADS and
+   Py_END_ALLOW_THREADS.  They should be used whenever a call into Tcl is made
+   that could call an event handler, or otherwise affect the state of a Tcl
+   interpreter.  These assume that the surrounding code has the Python
+   interpreter lock; inside the brackets, the Python interpreter lock has been 
+   released and the lock for Tcl has been acquired.
+
+   By contrast, ENTER_PYTHON(tstate) and LEAVE_PYTHON are used in Tcl event
+   handlers when the handler needs to use Python.  Such event handlers are
+   entered while the lock for Tcl is held; the event handler presumably needs
+   to use Python.  ENTER_PYTHON(tstate) releases the lock for Tcl and acquires
+   the Python interpreter lock, restoring the appropriate thread state, and
+   LEAVE_PYTHON releases the Python interpreter lock and re-acquires the lock
+   for Tcl.  It is okay for ENTER_TCL/LEAVE_TCL pairs to be contained inside
+   the code between ENTER_PYTHON(tstate) and LEAVE_PYTHON.
+
+   These locks expand to several statements and brackets; they should not be
+   used in branches of if statements and the like.
+
+*/
+
+static type_lock tcl_lock = 0;
+
+#define ENTER_TCL \
+       Py_BEGIN_ALLOW_THREADS acquire_lock(tcl_lock, 1);
+
+#define LEAVE_TCL \
+       release_lock(tcl_lock); Py_END_ALLOW_THREADS
+
+#define ENTER_PYTHON(tstate) \
+       if (PyThreadState_Swap(NULL) != NULL) \
+               Py_FatalError("ENTER_PYTHON with non-NULL tstate\n"); \
+       release_lock(tcl_lock); PyEval_RestoreThread((tstate));
+
+#define LEAVE_PYTHON \
+       PyEval_SaveThread(); acquire_lock(tcl_lock, 1);
+
+#else
+
+#define ENTER_TCL
+#define LEAVE_TCL
+#define ENTER_PYTHON(tstate)
+#define LEAVE_PYTHON
+
+#endif
+
 extern int Tk_GetNumMainWindows();
 
 #ifdef macintosh
@@ -145,12 +220,10 @@ extern int SIOUXIsAppWindow(WindowPtr);
 
 staticforward PyTypeObject Tkapp_Type;
 
-typedef struct
-{
+typedef struct {
        PyObject_HEAD
        Tcl_Interp *interp;
-}
-TkappObject;
+} TkappObject;
 
 #define Tkapp_Check(v) ((v)->ob_type == &Tkapp_Type)
 #define Tkapp_Interp(v) (((TkappObject *) (v))->interp)
@@ -181,18 +254,28 @@ Tkinter_Error(v)
 }
 
 
-int
-PythonCmd_Error(interp)
-       Tcl_Interp *interp;
+\f
+/**** Utils ****/
+
+#ifdef WITH_THREAD
+#ifndef MS_WIN32
+/* Millisecond sleep() for Unix platforms. */
+
+static void
+Sleep(milli)
+       int milli;
 {
-       errorInCmd = 1;
-       PyErr_Fetch(&excInCmd, &valInCmd, &trbInCmd);
-       return TCL_ERROR;
+       /* XXX Too bad if you don't have select(). */
+       struct timeval t;
+       double frac;
+       t.tv_sec = milli/1000;
+       t.tv_usec = (milli%1000) * 1000;
+       select(0, (fd_set *)0, (fd_set *)0, (fd_set *)0, &t);
 }
-
+#endif /* MS_WIN32 */
+#endif /* WITH_THREAD */
 
 \f
-/**** Utils ****/
 static char *
 AsString(value, tmp)
        PyObject *value;
@@ -286,8 +369,7 @@ Merge(args)
 
 \f
 static PyObject *
-Split(self, list)
-       PyObject *self;
+Split(list)
        char *list;
 {
        int argc;
@@ -299,13 +381,11 @@ Split(self, list)
                return Py_None;
        }
 
-       if (Tcl_SplitList(Tkapp_Interp(self), list, &argc, &argv) == TCL_ERROR)
-       {
+       if (Tcl_SplitList((Tcl_Interp *)NULL, list, &argc, &argv) != TCL_OK) {
                /* Not a list.
                 * Could be a quoted string containing funnies, e.g. {"}.
                 * Return the string itself.
                 */
-               PyErr_Clear();
                return PyString_FromString(list);
        }
 
@@ -318,7 +398,7 @@ Split(self, list)
                PyObject *w;
 
                for (i = 0; i < argc; i++) {
-                       if ((w = Split(self, argv[i])) == NULL) {
+                       if ((w = Split(argv[i])) == NULL) {
                                Py_DECREF(v);
                                v = NULL;
                                break;
@@ -326,7 +406,7 @@ Split(self, list)
                        PyTuple_SetItem(v, i, w);
                }
        }
-       ckfree(FREECAST argv);
+       Tcl_Free(FREECAST argv);
        return v;
 }
 
@@ -498,12 +578,16 @@ Tkapp_Call(self, args)
                if (Py_VerboseFlag >= 2)
                        PySys_WriteStderr("... use TclEval ");
                cmd = Tcl_Merge(argc, argv);
+               ENTER_TCL
                i = Tcl_Eval(interp, cmd);
+               LEAVE_TCL
                ckfree(cmd);
        }
        else {
                Tcl_ResetResult(interp);
+               ENTER_TCL
                i = (*info.proc)(info.clientData, interp, argc, argv);
+               LEAVE_TCL
        }
        if (i == TCL_ERROR) {
                if (Py_VerboseFlag >= 2)
@@ -551,10 +635,16 @@ Tkapp_GlobalCall(self, args)
        if (!cmd)
                PyErr_SetString(Tkinter_TclError, "merge failed");
 
-       else if (Tcl_GlobalEval(Tkapp_Interp(self), cmd) == TCL_ERROR)
-               res = Tkinter_Error(self);
-       else
-               res = PyString_FromString(Tkapp_Result(self));
+       else {
+               int err;
+               ENTER_TCL
+               err = Tcl_GlobalEval(Tkapp_Interp(self), cmd);
+               LEAVE_TCL
+               if (err == TCL_ERROR)
+                       res = Tkinter_Error(self);
+               else
+                       res = PyString_FromString(Tkapp_Result(self));
+       }
 
        if (cmd)
                ckfree(cmd);
@@ -568,11 +658,15 @@ Tkapp_Eval(self, args)
        PyObject *args;
 {
        char *script;
+       int err;
   
        if (!PyArg_ParseTuple(args, "s", &script))
                return NULL;
 
-       if (Tcl_Eval(Tkapp_Interp(self), script) == TCL_ERROR)
+       ENTER_TCL
+       err = Tcl_Eval(Tkapp_Interp(self), script);
+       LEAVE_TCL
+       if (err == TCL_ERROR)
                return Tkinter_Error(self);
   
        return PyString_FromString(Tkapp_Result(self));
@@ -584,13 +678,17 @@ Tkapp_GlobalEval(self, args)
        PyObject *args;
 {
        char *script;
+       int err;
   
        if (!PyArg_ParseTuple(args, "s", &script))
                return NULL;
 
-       if (Tcl_GlobalEval(Tkapp_Interp(self), script) == TCL_ERROR)
+       ENTER_TCL
+       err = Tcl_GlobalEval(Tkapp_Interp(self), script);
+       LEAVE_TCL
+       if (err == TCL_ERROR)
                return Tkinter_Error(self);
-
+  
        return PyString_FromString(Tkapp_Result(self));
 }
 
@@ -600,11 +698,15 @@ Tkapp_EvalFile(self, args)
        PyObject *args;
 {
        char *fileName;
+       int err;
 
        if (!PyArg_ParseTuple(args, "s", &fileName))
                return NULL;
 
-       if (Tcl_EvalFile(Tkapp_Interp(self), fileName) == TCL_ERROR)
+       ENTER_TCL
+       err = Tcl_EvalFile(Tkapp_Interp(self), fileName);
+       LEAVE_TCL
+       if (err == TCL_ERROR)
                return Tkinter_Error(self);
 
        return PyString_FromString(Tkapp_Result(self));
@@ -616,12 +718,15 @@ Tkapp_Record(self, args)
        PyObject *args;
 {
        char *script;
+       int err;
 
        if (!PyArg_ParseTuple(args, "s", &script))
                return NULL;
 
-       if (TCL_ERROR == Tcl_RecordAndEval(Tkapp_Interp(self),
-                                          script, TCL_NO_EVAL))
+       ENTER_TCL
+       err = Tcl_RecordAndEval(Tkapp_Interp(self), script, TCL_NO_EVAL);
+       LEAVE_TCL
+       if (err == TCL_ERROR)
                return Tkinter_Error(self);
 
        return PyString_FromString(Tkapp_Result(self));
@@ -636,7 +741,9 @@ Tkapp_AddErrorInfo(self, args)
 
        if (!PyArg_ParseTuple(args, "s", &msg))
                return NULL;
+       ENTER_TCL
        Tcl_AddErrorInfo(Tkapp_Interp(self), msg);
+       LEAVE_TCL
 
        Py_INCREF(Py_None);
        return Py_None;
@@ -652,25 +759,31 @@ SetVar(self, args, flags)
        PyObject *args;
        int flags;
 {
-       char *name1, *name2, *ok;
+       char *name1, *name2, *ok, *s;
        PyObject *newValue;
        PyObject *tmp = PyList_New(0);
 
        if (!tmp)
                return NULL;
 
-       if (PyArg_ParseTuple(args, "sO", &name1, &newValue))
+       if (PyArg_ParseTuple(args, "sO", &name1, &newValue)) {
                /* XXX Merge? */
-               ok = Tcl_SetVar(Tkapp_Interp(self), name1, 
-                               AsString(newValue, tmp), flags);
-
+               s = AsString(newValue, tmp);
+               ENTER_TCL
+               ok = Tcl_SetVar(Tkapp_Interp(self), name1, s, flags);
+               LEAVE_TCL
+       }
        else {
                PyErr_Clear();
-               if (PyArg_ParseTuple(args, "ssO", &name1, &name2, &newValue))
+               if (PyArg_ParseTuple(args, "ssO", &name1, &name2, &newValue)) {
+                       s = AsString (newValue, tmp);
+                       ENTER_TCL
                        ok = Tcl_SetVar2(Tkapp_Interp(self), name1, name2, 
-                                        AsString (newValue, tmp), flags);
+                                        s, flags);
+                       LEAVE_TCL
+               }
                else {
-                       Py_DECREF (tmp);
+                       Py_DECREF(tmp);
                        return NULL;
                }
        }
@@ -711,11 +824,13 @@ GetVar(self, args, flags)
 
        if (!PyArg_ParseTuple(args, "s|s", &name1, &name2))
                return NULL;
+       ENTER_TCL
        if (name2 == NULL)
                s = Tcl_GetVar(Tkapp_Interp (self), name1, flags);
 
        else
                s = Tcl_GetVar2(Tkapp_Interp(self), name1, name2, flags);
+       LEAVE_TCL
 
        if (s == NULL)
                return Tkinter_Error(self);
@@ -752,11 +867,13 @@ UnsetVar(self, args, flags)
 
        if (!PyArg_ParseTuple(args, "s|s", &name1, &name2))
                return NULL;
+       ENTER_TCL
        if (name2 == NULL)
                code = Tcl_UnsetVar(Tkapp_Interp(self), name1, flags);
 
        else
                code = Tcl_UnsetVar2(Tkapp_Interp(self), name1, name2, flags);
+       LEAVE_TCL
 
        if (code == TCL_ERROR)
                return Tkinter_Error(self);
@@ -836,10 +953,14 @@ Tkapp_ExprString(self, args)
        PyObject *args;
 {
        char *s;
+       int retval;
 
        if (!PyArg_ParseTuple(args, "s", &s))
                return NULL;
-       if (Tcl_ExprString(Tkapp_Interp(self), s) == TCL_ERROR)
+       ENTER_TCL
+       retval = Tcl_ExprString(Tkapp_Interp(self), s);
+       LEAVE_TCL
+       if (retval == TCL_ERROR)
                return Tkinter_Error(self);
        return Py_BuildValue("s", Tkapp_Result(self));
 }
@@ -850,11 +971,15 @@ Tkapp_ExprLong(self, args)
        PyObject *args;
 {
        char *s;
+       int retval;
        long v;
 
        if (!PyArg_ParseTuple(args, "s", &s))
                return NULL;
-       if (Tcl_ExprLong(Tkapp_Interp(self), s, &v) == TCL_ERROR)
+       ENTER_TCL
+       retval = Tcl_ExprLong(Tkapp_Interp(self), s, &v);
+       LEAVE_TCL
+       if (retval == TCL_ERROR)
                return Tkinter_Error(self);
        return Py_BuildValue("l", v);
 }
@@ -871,7 +996,9 @@ Tkapp_ExprDouble(self, args)
        if (!PyArg_ParseTuple(args, "s", &s))
                return NULL;
        PyFPE_START_PROTECT("Tkapp_ExprDouble", return 0)
+       ENTER_TCL
        retval = Tcl_ExprDouble(Tkapp_Interp(self), s, &v);
+       LEAVE_TCL
        PyFPE_END_PROTECT(retval)
        if (retval == TCL_ERROR)
                return Tkinter_Error(self);
@@ -884,11 +1011,15 @@ Tkapp_ExprBoolean(self, args)
        PyObject *args;
 {
        char *s;
+       int retval;
        int v;
 
        if (!PyArg_ParseTuple(args, "s", &s))
                return NULL;
-       if (Tcl_ExprBoolean(Tkapp_Interp(self), s, &v) == TCL_ERROR)
+       ENTER_TCL
+       retval = Tcl_ExprBoolean(Tkapp_Interp(self), s, &v);
+       LEAVE_TCL
+       if (retval == TCL_ERROR)
                return Tkinter_Error(self);
        return Py_BuildValue("i", v);
 }
@@ -938,7 +1069,7 @@ Tkapp_Split(self, args)
 
        if (!PyArg_ParseTuple(args, "s", &list))
                return NULL;
-       return Split(self, list);
+       return Split(list);
 }
 
 static PyObject *
@@ -963,24 +1094,45 @@ Tkapp_Merge(self, args)
 \f
 /** Tcl Command **/
 
+/* Client data struct */
+typedef struct {
+       PyThreadState *tstate;
+       PyObject *self;
+       PyObject *func;
+} PythonCmd_ClientData;
+
+static int
+PythonCmd_Error(interp)
+       Tcl_Interp *interp;
+{
+       errorInCmd = 1;
+       PyErr_Fetch(&excInCmd, &valInCmd, &trbInCmd);
+       LEAVE_PYTHON
+       return TCL_ERROR;
+}
+
 /* This is the Tcl command that acts as a wrapper for Python
  * function or method.
  */
 static int
 PythonCmd(clientData, interp, argc, argv)
-       ClientData clientData;               /* Is (self, func) */
+       ClientData clientData;
        Tcl_Interp *interp;
        int argc;
        char *argv[];
 {
+       PythonCmd_ClientData *data = (PythonCmd_ClientData *)clientData;
        PyObject *self, *func, *arg, *res, *tmp;
        int i;
 
+       /* XXX Should create fresh thread state? */
+       ENTER_PYTHON(data->tstate)
+
        /* TBD: no error checking here since we know, via the
         * Tkapp_CreateCommand() that the client data is a two-tuple
         */
-       self = PyTuple_GetItem((PyObject *) clientData, 0);
-       func = PyTuple_GetItem((PyObject *) clientData, 1);
+       self = data->self;
+       func = data->func;
 
        /* Create argument list (argv1, ..., argvN) */
        if (!(arg = PyTuple_New(argc - 1)))
@@ -1008,14 +1160,22 @@ PythonCmd(clientData, interp, argc, argv)
        Py_DECREF(res);
        Py_DECREF(tmp);
 
+       LEAVE_PYTHON
+
        return TCL_OK;
 }
 
 static void
 PythonCmdDelete(clientData)
-       ClientData clientData;  /* Is (self, func) */
+       ClientData clientData;
 {
-       Py_DECREF((PyObject *) clientData);
+       PythonCmd_ClientData *data = (PythonCmd_ClientData *)clientData;
+
+       ENTER_PYTHON(data->tstate)
+       Py_XDECREF(data->self);
+       Py_XDECREF(data->func);
+       PyMem_DEL(data);
+       LEAVE_PYTHON
 }
 
 
@@ -1025,9 +1185,10 @@ Tkapp_CreateCommand(self, args)
        PyObject *self;
        PyObject *args;
 {
+       PythonCmd_ClientData *data;
        char *cmdName;
        PyObject *func;
-       PyObject *data;
+       Tcl_Command err;
 
        if (!PyArg_ParseTuple(args, "sO", &cmdName, &func))
                return NULL;
@@ -1036,15 +1197,22 @@ Tkapp_CreateCommand(self, args)
                return NULL;
        }
 
-       data = Py_BuildValue("OO", self, func);
+       data = PyMem_NEW(PythonCmd_ClientData, 1);
        if (!data)
                return NULL;
-
-       if (Tcl_CreateCommand(Tkapp_Interp(self), cmdName, PythonCmd,
-                             (ClientData) data, PythonCmdDelete) == NULL)
-       {
+       data->tstate = PyThreadState_Get();
+       Py_XINCREF(self);
+       Py_XINCREF(func);
+       data->self = self;
+       data->func = func;
+
+       ENTER_TCL
+       err = Tcl_CreateCommand(Tkapp_Interp(self), cmdName, PythonCmd,
+                               (ClientData)data, PythonCmdDelete);
+       LEAVE_TCL
+       if (err == NULL) {
                PyErr_SetString(Tkinter_TclError, "can't create Tcl command");
-               Py_DECREF(data);
+               PyMem_DEL(data);
                return NULL;
        }
 
@@ -1060,11 +1228,14 @@ Tkapp_DeleteCommand(self, args)
        PyObject *args;
 {
        char *cmdName;
+       int err;
 
        if (!PyArg_ParseTuple(args, "s", &cmdName))
                return NULL;
-       if (Tcl_DeleteCommand(Tkapp_Interp(self), cmdName) == -1)
-       {
+       ENTER_TCL
+       err = Tcl_DeleteCommand(Tkapp_Interp(self), cmdName);
+       LEAVE_TCL
+       if (err == -1) {
                PyErr_SetString(Tkinter_TclError, "can't delete Tcl command");
                return NULL;
        }
@@ -1074,17 +1245,71 @@ Tkapp_DeleteCommand(self, args)
 
 
 \f
+#ifdef HAVE_CREATEFILEHANDLER
 /** File Handler **/
 
+typedef struct _fhcdata {
+       PyThreadState *tstate;
+       PyObject *func;
+       PyObject *file;
+       int id;
+       struct _fhcdata *next;
+} FileHandler_ClientData;
+
+static FileHandler_ClientData *HeadFHCD;
+
+static FileHandler_ClientData *
+NewFHCD(func, file, id)
+       PyObject *func;
+       PyObject *file;
+       int id;
+{
+       FileHandler_ClientData *p;
+       p = PyMem_NEW(FileHandler_ClientData, 1);
+       if (p != NULL) {
+               Py_XINCREF(func);
+               Py_XINCREF(file);
+               p->tstate = PyThreadState_Get();
+               p->func = func;
+               p->file = file;
+               p->id = id;
+               p->next = HeadFHCD;
+               HeadFHCD = p;
+       }
+       return p;
+}
+
+static void
+DeleteFHCD(id)
+       int id;
+{
+       FileHandler_ClientData *p, **pp;
+       
+       pp = &HeadFHCD; 
+       while ((p = *pp) != NULL) {
+               if (p->id == id) {
+                       *pp = p->next;
+                       Py_XDECREF(p->func);
+                       Py_XDECREF(p->file);
+                       PyMem_DEL(p);
+               }
+               else
+                       pp = &p->next;
+       }
+}
+
 static void
 FileHandler(clientData, mask)
-       ClientData clientData;               /* Is: (func, file) */
+       ClientData clientData;
        int mask;
 {
+       FileHandler_ClientData *data = (FileHandler_ClientData *)clientData;
        PyObject *func, *file, *arg, *res;
 
-       func = PyTuple_GetItem((PyObject *) clientData, 0);
-       file = PyTuple_GetItem((PyObject *) clientData, 1);
+       /* XXX Should create fresh thread state? */
+       ENTER_PYTHON(data->tstate)
+       func = data->func;
+       file = data->file;
 
        arg = Py_BuildValue("(Oi)", file, (long) mask);
        res = PyEval_CallObject(func, arg);
@@ -1095,6 +1320,7 @@ FileHandler(clientData, mask)
                PyErr_Fetch(&excInCmd, &valInCmd, &trbInCmd);
        }
        Py_XDECREF(res);
+       LEAVE_PYTHON
 }
 
 static int
@@ -1140,25 +1366,16 @@ GetFileNo(file)
        return id;
 }
 
-
-static PyObject* Tkapp_ClientDataDict = NULL;
-
-#ifdef HAVE_CREATEFILEHANDLER
 static PyObject *
 Tkapp_CreateFileHandler(self, args)
        PyObject *self;
        PyObject *args;                      /* Is (file, mask, func) */
 {
-       PyObject *file, *func, *data;
-       PyObject *idkey;
+       FileHandler_ClientData *data;
+       PyObject *file, *func;
        int mask, id;
        FHANDLE tfile;
 
-       if (!Tkapp_ClientDataDict) {
-               if (!(Tkapp_ClientDataDict = PyDict_New()))
-                       return NULL;
-       }
-
        if (!PyArg_ParseTuple(args, "OiO", &file, &mask, &func))
                return NULL;
        id = GetFileNo(file);
@@ -1169,35 +1386,26 @@ Tkapp_CreateFileHandler(self, args)
                return NULL;
        }
 
-       if (!(idkey = PyInt_FromLong(id)))
+       data = NewFHCD(func, file, id);
+       if (data == NULL)
                return NULL;
 
-       /* ClientData is: (func, file) */
-       data = Py_BuildValue("(OO)", func, file);
-       if (!data || PyDict_SetItem(Tkapp_ClientDataDict, idkey, data)) {
-               Py_DECREF(idkey);
-               Py_XDECREF(data);
-               return NULL;
-       }
-       Py_DECREF(idkey);
-
        tfile = MAKEFHANDLE(id);
        /* Ought to check for null Tcl_File object... */
+       ENTER_TCL
        Tcl_CreateFileHandler(tfile, mask, FileHandler, (ClientData) data);
-       /* XXX fileHandlerDict */
+       LEAVE_TCL
        Py_INCREF(Py_None);
        return Py_None;
 }
 
-\f
 static PyObject *
 Tkapp_DeleteFileHandler(self, args)
        PyObject *self;
        PyObject *args;                      /* Args: file */
 {
        PyObject *file;
-       PyObject *idkey;
-       PyObject *data;
+       FileHandler_ClientData *data;
        int id;
        FHANDLE tfile;
   
@@ -1207,21 +1415,13 @@ Tkapp_DeleteFileHandler(self, args)
        if (id < 0)
                return NULL;
 
-       if (!(idkey = PyInt_FromLong(id)))
-               return NULL;
-       
-       /* find and free the object created in the
-        * Tkapp_CreateFileHandler() call
-        */
-       data = PyDict_GetItem(Tkapp_ClientDataDict, idkey);
-       Py_XDECREF(data);
-       PyDict_DelItem(Tkapp_ClientDataDict, idkey);
-       Py_DECREF(idkey);
+       DeleteFHCD(id);
 
        tfile = MAKEFHANDLE(id);
        /* Ought to check for null Tcl_File object... */
+       ENTER_TCL
        Tcl_DeleteFileHandler(tfile);
-       /* XXX fileHandlerDict */
+       LEAVE_TCL
        Py_INCREF(Py_None);
        return Py_None;
 }
@@ -1232,13 +1432,12 @@ Tkapp_DeleteFileHandler(self, args)
 
 staticforward PyTypeObject Tktt_Type;
 
-typedef struct
-{
+typedef struct {
        PyObject_HEAD
        Tcl_TimerToken token;
        PyObject *func;
-}
-TkttObject;
+       PyThreadState *tstate;
+TkttObject;
 
 static PyObject *
 Tktt_DeleteTimerHandler(self, args)
@@ -1246,13 +1445,18 @@ Tktt_DeleteTimerHandler(self, args)
        PyObject *args;
 {
        TkttObject *v = (TkttObject *)self;
+       PyObject *func = v->func;
 
        if (!PyArg_ParseTuple(args, ""))
                return NULL;
-       if (v->func != NULL) {
+       if (v->token != NULL) {
                Tcl_DeleteTimerHandler(v->token);
-               PyMem_DEL(v->func);
+               v->token = NULL;
+       }
+       if (func != NULL) {
                v->func = NULL;
+               Py_DECREF(func);
+               Py_DECREF(v); /* See Tktt_New() */
        }
        Py_INCREF(Py_None);
        return Py_None;
@@ -1265,8 +1469,7 @@ static PyMethodDef Tktt_methods[] =
 };
 
 static TkttObject *
-Tktt_New(token, func)
-       Tcl_TimerToken token;
+Tktt_New(func)
        PyObject *func;
 {
        TkttObject *v;
@@ -1275,9 +1478,13 @@ Tktt_New(token, func)
        if (v == NULL)
                return NULL;
 
-       v->token = token;
+       Py_INCREF(func);
+       v->token = NULL;
        v->func = func;
-       Py_INCREF(v->func);
+       v->tstate = PyThreadState_Get();
+
+       /* Extra reference, deleted when called or when handler is deleted */
+       Py_INCREF(v);
        return v;
 }
 
@@ -1285,6 +1492,11 @@ static void
 Tktt_Dealloc(self)
        PyObject *self;
 {
+       TkttObject *v = (TkttObject *)self;
+       PyObject *func = v->func;
+
+       Py_XDECREF(func);
+
        PyMem_DEL(self);
 }
 
@@ -1335,8 +1547,20 @@ static void
 TimerHandler(clientData)
        ClientData clientData;
 {
-       PyObject *func = (PyObject *)clientData;
-       PyObject *res  = PyEval_CallObject(func, NULL);
+       TkttObject *v = (TkttObject *)clientData;
+       PyObject *func = v->func;
+       PyObject *res;
+
+       if (func == NULL)
+               return;
+
+       v->func = NULL;
+
+       ENTER_PYTHON(v->tstate)
+
+       res  = PyEval_CallObject(func, NULL);
+       Py_DECREF(func);
+       Py_DECREF(v); /* See Tktt_New() */
 
        if (res == NULL) {
                errorInCmd = 1;
@@ -1344,6 +1568,8 @@ TimerHandler(clientData)
        }
        else
                Py_DECREF(res);
+
+       LEAVE_PYTHON
 }
 
 static PyObject *
@@ -1353,7 +1579,7 @@ Tkapp_CreateTimerHandler(self, args)
 {
        int milliseconds;
        PyObject *func;
-       Tcl_TimerToken token;
+       TkttObject *v;
 
        if (!PyArg_ParseTuple(args, "iO", &milliseconds, &func))
                return NULL;
@@ -1361,13 +1587,13 @@ Tkapp_CreateTimerHandler(self, args)
                PyErr_SetString(PyExc_TypeError, "bad argument list");
                return NULL;
        }
-       token = Tcl_CreateTimerHandler(milliseconds, TimerHandler,
-                                      (ClientData)func);
+       v = Tktt_New(func);
+       v->token = Tcl_CreateTimerHandler(milliseconds, TimerHandler,
+                                         (ClientData)v);
 
-       return (PyObject *) Tktt_New(token, func);
+       return (PyObject *) v;
 }
 
-
 \f
 /** Event Loop **/
 
@@ -1387,21 +1613,18 @@ Tkapp_MainLoop(self, args)
               !errorInCmd)
        {
                int result;
-#ifdef HAVE_PYTCL_WAITUNTILEVENT
+
+#ifdef WITH_THREAD
+               ENTER_TCL
                result = Tcl_DoOneEvent(TCL_DONT_WAIT);
-               if (PyErr_CheckSignals() != 0)
-                       return NULL;
-               if (result)
-                       continue;
-               /* XXX It's not *quite* certain that this is
-                  thread-safe, but it seems *rather* safe as long as
-                  no two threads call mainloop() simultaneously. */
-               Py_BEGIN_ALLOW_THREADS
-               result = PyTcl_WaitUntilEvent();
+               release_lock(tcl_lock);
+               if (result == 0)
+                       Sleep(20);
                Py_END_ALLOW_THREADS
 #else
                result = Tcl_DoOneEvent(0);
 #endif
+
                if (PyErr_CheckSignals() != 0)
                        return NULL;
                if (result < 0)
@@ -1430,7 +1653,9 @@ Tkapp_DoOneEvent(self, args)
        if (!PyArg_ParseTuple(args, "|i", &flags))
                return NULL;
 
+       ENTER_TCL
        rv = Tcl_DoOneEvent(flags);
+       LEAVE_TCL
        return Py_BuildValue("i", rv);
 }
 
@@ -1511,7 +1736,9 @@ static void
 Tkapp_Dealloc(self)
        PyObject *self;
 {
+       ENTER_TCL
        Tcl_DeleteInterp(Tkapp_Interp(self));
+       LEAVE_TCL
        PyMem_DEL(self);
        DisableEventHook();
 }
@@ -1588,7 +1815,6 @@ static PyMethodDef moduleMethods[] =
 };
 
 #ifdef WAIT_FOR_STDIN
-#define WAITFLAG 0
 
 static int stdin_ready = 0;
 
@@ -1599,60 +1825,67 @@ MyFileProc(clientData, mask)
 {
        stdin_ready = 1;
 }
-#else
-#define WAITFLAG TCL_DONT_WAIT
-#endif
 
-static PyInterpreterState *event_interp = NULL;
+static PyThreadState *event_tstate = NULL;
 
 static int
 EventHook()
 {
-       PyThreadState *tstate, *save_tstate;
-#ifdef WAIT_FOR_STDIN
-       FHANDLE tfile = MAKEFHANDLE(((int)fileno(stdin)));
+       FHANDLE tfile;
 
+       ENTER_PYTHON(event_tstate)
+       tfile = MAKEFHANDLE(fileno(stdin));
        stdin_ready = 0;
        Tcl_CreateFileHandler(tfile, TCL_READABLE, MyFileProc, NULL);
+       while (!errorInCmd && !stdin_ready) {
+               int result;
+
+#ifdef WITH_THREAD
+               ENTER_TCL
+               result = Tcl_DoOneEvent(TCL_DONT_WAIT);
+               release_lock(tcl_lock);
+               if (result == 0)
+                       Sleep(20);
+               Py_END_ALLOW_THREADS
+#else
+               result = Tcl_DoOneEvent(0);
 #endif
-       tstate = PyThreadState_New(event_interp);
-       save_tstate = PyThreadState_Swap(NULL);
-       PyEval_RestoreThread(tstate);
-#ifdef WAIT_FOR_STDIN
-       while (!errorInCmd && !stdin_ready)
-#endif
-               Tcl_DoOneEvent(WAITFLAG);
+
+               if (result < 0)
+                       break;
+       }
+       Tcl_DeleteFileHandler(tfile);
        if (errorInCmd) {
                errorInCmd = 0;
                PyErr_Restore(excInCmd, valInCmd, trbInCmd);
                excInCmd = valInCmd = trbInCmd = NULL;
                PyErr_Print();
        }
-       PyThreadState_Clear(tstate);
-       PyEval_SaveThread();
-       PyThreadState_Swap(save_tstate);
-       PyThreadState_Delete(tstate);
-#ifdef WAIT_FOR_STDIN
-       Tcl_DeleteFileHandler(tfile);
-#endif
+       LEAVE_PYTHON
        return 0;
 }
 
+#endif
+
 static void
 EnableEventHook()
 {
+#ifdef WAIT_FOR_STDIN
        if (PyOS_InputHook == NULL) {
-               event_interp = PyThreadState_Get()->interp;
+               event_tstate = PyThreadState_Get();
                PyOS_InputHook = EventHook;
        }
+#endif
 }
 
 static void
 DisableEventHook()
 {
+#ifdef WAIT_FOR_STDIN
        if (Tk_GetNumMainWindows() == 0 && PyOS_InputHook == EventHook) {
                PyOS_InputHook = NULL;
        }
+#endif
 }
 
 
@@ -1689,7 +1922,10 @@ init_tkinter()
        PyObject *m, *d;
 
        Tkapp_Type.ob_type = &PyType_Type;
-       Tktt_Type.ob_type = &PyType_Type;
+
+#ifdef WITH_THREAD
+       tcl_lock = allocate_lock();
+#endif
 
        m = Py_InitModule("_tkinter", moduleMethods);
 
@@ -1710,6 +1946,8 @@ init_tkinter()
        ins_string(d, "TCL_VERSION", TCL_VERSION);
 
        PyDict_SetItemString(d, "TkappType", (PyObject *)&Tkapp_Type);
+
+       Tktt_Type.ob_type = &PyType_Type;
        PyDict_SetItemString(d, "TkttType", (PyObject *)&Tktt_Type);
 
        if (PyErr_Occurred())
@@ -1769,8 +2007,8 @@ PyMacConvertEvent(eventPtr)
 {
        WindowPtr frontwin;
        /*
-       ** Sioux eats too many events, so we don't pass it everything.
-       ** We always pass update events to Sioux, and we only pass other events if
+       ** Sioux eats too many events, so we don't pass it everything.  We
+       ** always pass update events to Sioux, and we only pass other events if
        ** the Sioux window is frontmost. This means that Tk menus don't work
        ** in that case, but at least we can scroll the sioux window.
        ** Note that the SIOUXIsAppWindow() routine we use here is not really