diff options
author | Guido van Rossum <guido@python.org> | 1998-05-28 23:06:38 (GMT) |
---|---|---|
committer | Guido van Rossum <guido@python.org> | 1998-05-28 23:06:38 (GMT) |
commit | 00d93066b010805c53f457e095294babd5e8793a (patch) | |
tree | 576b83fe1491253e78ee44ea9e87c62c46ab2426 | |
parent | 7e7912f2ca362a3fdcae3f785fb3bac47e86ff23 (diff) | |
download | cpython-00d93066b010805c53f457e095294babd5e8793a.zip cpython-00d93066b010805c53f457e095294babd5e8793a.tar.gz cpython-00d93066b010805c53f457e095294babd5e8793a.tar.bz2 |
Changes to make it possible to write multi-threaded programs using
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().
-rw-r--r-- | Modules/_tkinter.c | 542 |
1 files changed, 390 insertions, 152 deletions
diff --git a/Modules/_tkinter.c b/Modules/_tkinter.c index f63bd38..d2bbff7 100644 --- a/Modules/_tkinter.c +++ b/Modules/_tkinter.c @@ -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; + +/**** 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 */ -/**** Utils ****/ static char * AsString(value, tmp) PyObject *value; @@ -286,8 +369,7 @@ Merge(args) 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) /** 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) +#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; } - 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; } - /** 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 |