Commit f9434efe authored by Guido van Rossum's avatar Guido van Rossum

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().
parent 5c8e30d4
...@@ -58,6 +58,10 @@ PERFORMANCE OF THIS SOFTWARE. ...@@ -58,6 +58,10 @@ PERFORMANCE OF THIS SOFTWARE.
#include "Python.h" #include "Python.h"
#include <ctype.h> #include <ctype.h>
#ifdef WITH_THREAD
#include "thread.h"
#endif
#ifdef macintosh #ifdef macintosh
#define MAC_TCL #define MAC_TCL
#include "myselect.h" #include "myselect.h"
...@@ -68,6 +72,10 @@ PERFORMANCE OF THIS SOFTWARE. ...@@ -68,6 +72,10 @@ PERFORMANCE OF THIS SOFTWARE.
#define TKMAJORMINOR (TK_MAJOR_VERSION*1000 + TK_MINOR_VERSION) #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) #if TKMAJORMINOR >= 8000 && defined(macintosh)
/* Sigh, we have to include this to get at the tcl qd pointer */ /* Sigh, we have to include this to get at the tcl qd pointer */
#include <tkMac.h> #include <tkMac.h>
...@@ -75,14 +83,17 @@ PERFORMANCE OF THIS SOFTWARE. ...@@ -75,14 +83,17 @@ PERFORMANCE OF THIS SOFTWARE.
#include <Menus.h> #include <Menus.h>
#endif #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) #if TKMAJORMINOR < 8000 || !defined(MS_WINDOWS)
#define HAVE_CREATEFILEHANDLER #define HAVE_CREATEFILEHANDLER
#endif #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 #ifdef MS_WINDOWS
#define FHANDLETYPE TCL_WIN_SOCKET #define FHANDLETYPE TCL_WIN_SOCKET
#else #else
...@@ -97,10 +108,74 @@ PERFORMANCE OF THIS SOFTWARE. ...@@ -97,10 +108,74 @@ PERFORMANCE OF THIS SOFTWARE.
#define MAKEFHANDLE(fd) (fd) #define MAKEFHANDLE(fd) (fd)
#endif #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 #define WAIT_FOR_STDIN
#endif #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(); extern int Tk_GetNumMainWindows();
#ifdef macintosh #ifdef macintosh
...@@ -145,12 +220,10 @@ extern int SIOUXIsAppWindow(WindowPtr); ...@@ -145,12 +220,10 @@ extern int SIOUXIsAppWindow(WindowPtr);
staticforward PyTypeObject Tkapp_Type; staticforward PyTypeObject Tkapp_Type;
typedef struct typedef struct {
{
PyObject_HEAD PyObject_HEAD
Tcl_Interp *interp; Tcl_Interp *interp;
} } TkappObject;
TkappObject;
#define Tkapp_Check(v) ((v)->ob_type == &Tkapp_Type) #define Tkapp_Check(v) ((v)->ob_type == &Tkapp_Type)
#define Tkapp_Interp(v) (((TkappObject *) (v))->interp) #define Tkapp_Interp(v) (((TkappObject *) (v))->interp)
...@@ -181,18 +254,28 @@ Tkinter_Error(v) ...@@ -181,18 +254,28 @@ Tkinter_Error(v)
} }
int
PythonCmd_Error(interp) /**** Utils ****/
Tcl_Interp *interp;
#ifdef WITH_THREAD
#ifndef MS_WIN32
/* Millisecond sleep() for Unix platforms. */
static void
Sleep(milli)
int milli;
{ {
errorInCmd = 1; /* XXX Too bad if you don't have select(). */
PyErr_Fetch(&excInCmd, &valInCmd, &trbInCmd); struct timeval t;
return TCL_ERROR; 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 * static char *
AsString(value, tmp) AsString(value, tmp)
PyObject *value; PyObject *value;
...@@ -286,8 +369,7 @@ Merge(args) ...@@ -286,8 +369,7 @@ Merge(args)
static PyObject * static PyObject *
Split(self, list) Split(list)
PyObject *self;
char *list; char *list;
{ {
int argc; int argc;
...@@ -299,13 +381,11 @@ Split(self, list) ...@@ -299,13 +381,11 @@ Split(self, list)
return Py_None; 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. /* Not a list.
* Could be a quoted string containing funnies, e.g. {"}. * Could be a quoted string containing funnies, e.g. {"}.
* Return the string itself. * Return the string itself.
*/ */
PyErr_Clear();
return PyString_FromString(list); return PyString_FromString(list);
} }
...@@ -318,7 +398,7 @@ Split(self, list) ...@@ -318,7 +398,7 @@ Split(self, list)
PyObject *w; PyObject *w;
for (i = 0; i < argc; i++) { for (i = 0; i < argc; i++) {
if ((w = Split(self, argv[i])) == NULL) { if ((w = Split(argv[i])) == NULL) {
Py_DECREF(v); Py_DECREF(v);
v = NULL; v = NULL;
break; break;
...@@ -326,7 +406,7 @@ Split(self, list) ...@@ -326,7 +406,7 @@ Split(self, list)
PyTuple_SetItem(v, i, w); PyTuple_SetItem(v, i, w);
} }
} }
ckfree(FREECAST argv); Tcl_Free(FREECAST argv);
return v; return v;
} }
...@@ -498,12 +578,16 @@ Tkapp_Call(self, args) ...@@ -498,12 +578,16 @@ Tkapp_Call(self, args)
if (Py_VerboseFlag >= 2) if (Py_VerboseFlag >= 2)
PySys_WriteStderr("... use TclEval "); PySys_WriteStderr("... use TclEval ");
cmd = Tcl_Merge(argc, argv); cmd = Tcl_Merge(argc, argv);
ENTER_TCL
i = Tcl_Eval(interp, cmd); i = Tcl_Eval(interp, cmd);
LEAVE_TCL
ckfree(cmd); ckfree(cmd);
} }
else { else {
Tcl_ResetResult(interp); Tcl_ResetResult(interp);
ENTER_TCL
i = (*info.proc)(info.clientData, interp, argc, argv); i = (*info.proc)(info.clientData, interp, argc, argv);
LEAVE_TCL
} }
if (i == TCL_ERROR) { if (i == TCL_ERROR) {
if (Py_VerboseFlag >= 2) if (Py_VerboseFlag >= 2)
...@@ -551,10 +635,16 @@ Tkapp_GlobalCall(self, args) ...@@ -551,10 +635,16 @@ Tkapp_GlobalCall(self, args)
if (!cmd) if (!cmd)
PyErr_SetString(Tkinter_TclError, "merge failed"); PyErr_SetString(Tkinter_TclError, "merge failed");
else if (Tcl_GlobalEval(Tkapp_Interp(self), cmd) == TCL_ERROR) else {
res = Tkinter_Error(self); int err;
else ENTER_TCL
res = PyString_FromString(Tkapp_Result(self)); 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) if (cmd)
ckfree(cmd); ckfree(cmd);
...@@ -568,11 +658,15 @@ Tkapp_Eval(self, args) ...@@ -568,11 +658,15 @@ Tkapp_Eval(self, args)
PyObject *args; PyObject *args;
{ {
char *script; char *script;
int err;
if (!PyArg_ParseTuple(args, "s", &script)) if (!PyArg_ParseTuple(args, "s", &script))
return NULL; 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 Tkinter_Error(self);
return PyString_FromString(Tkapp_Result(self)); return PyString_FromString(Tkapp_Result(self));
...@@ -584,13 +678,17 @@ Tkapp_GlobalEval(self, args) ...@@ -584,13 +678,17 @@ Tkapp_GlobalEval(self, args)
PyObject *args; PyObject *args;
{ {
char *script; char *script;
int err;
if (!PyArg_ParseTuple(args, "s", &script)) if (!PyArg_ParseTuple(args, "s", &script))
return NULL; 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 Tkinter_Error(self);
return PyString_FromString(Tkapp_Result(self)); return PyString_FromString(Tkapp_Result(self));
} }
...@@ -600,11 +698,15 @@ Tkapp_EvalFile(self, args) ...@@ -600,11 +698,15 @@ Tkapp_EvalFile(self, args)
PyObject *args; PyObject *args;
{ {
char *fileName; char *fileName;
int err;
if (!PyArg_ParseTuple(args, "s", &fileName)) if (!PyArg_ParseTuple(args, "s", &fileName))
return NULL; 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 Tkinter_Error(self);
return PyString_FromString(Tkapp_Result(self)); return PyString_FromString(Tkapp_Result(self));
...@@ -616,12 +718,15 @@ Tkapp_Record(self, args) ...@@ -616,12 +718,15 @@ Tkapp_Record(self, args)
PyObject *args; PyObject *args;
{ {
char *script; char *script;
int err;
if (!PyArg_ParseTuple(args, "s", &script)) if (!PyArg_ParseTuple(args, "s", &script))
return NULL; return NULL;
if (TCL_ERROR == Tcl_RecordAndEval(Tkapp_Interp(self), ENTER_TCL
script, TCL_NO_EVAL)) err = Tcl_RecordAndEval(Tkapp_Interp(self), script, TCL_NO_EVAL);
LEAVE_TCL
if (err == TCL_ERROR)
return Tkinter_Error(self); return Tkinter_Error(self);
return PyString_FromString(Tkapp_Result(self)); return PyString_FromString(Tkapp_Result(self));
...@@ -636,7 +741,9 @@ Tkapp_AddErrorInfo(self, args) ...@@ -636,7 +741,9 @@ Tkapp_AddErrorInfo(self, args)
if (!PyArg_ParseTuple(args, "s", &msg)) if (!PyArg_ParseTuple(args, "s", &msg))
return NULL; return NULL;
ENTER_TCL
Tcl_AddErrorInfo(Tkapp_Interp(self), msg); Tcl_AddErrorInfo(Tkapp_Interp(self), msg);
LEAVE_TCL
Py_INCREF(Py_None); Py_INCREF(Py_None);
return Py_None; return Py_None;
...@@ -652,25 +759,31 @@ SetVar(self, args, flags) ...@@ -652,25 +759,31 @@ SetVar(self, args, flags)
PyObject *args; PyObject *args;
int flags; int flags;
{ {
char *name1, *name2, *ok; char *name1, *name2, *ok, *s;
PyObject *newValue; PyObject *newValue;
PyObject *tmp = PyList_New(0); PyObject *tmp = PyList_New(0);
if (!tmp) if (!tmp)
return NULL; return NULL;
if (PyArg_ParseTuple(args, "sO", &name1, &newValue)) if (PyArg_ParseTuple(args, "sO", &name1, &newValue)) {
/* XXX Merge? */ /* XXX Merge? */
ok = Tcl_SetVar(Tkapp_Interp(self), name1, s = AsString(newValue, tmp);
AsString(newValue, tmp), flags); ENTER_TCL
ok = Tcl_SetVar(Tkapp_Interp(self), name1, s, flags);
LEAVE_TCL
}
else { else {
PyErr_Clear(); 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, ok = Tcl_SetVar2(Tkapp_Interp(self), name1, name2,
AsString (newValue, tmp), flags); s, flags);
LEAVE_TCL
}
else { else {
Py_DECREF (tmp); Py_DECREF(tmp);
return NULL; return NULL;
} }
} }
...@@ -711,11 +824,13 @@ GetVar(self, args, flags) ...@@ -711,11 +824,13 @@ GetVar(self, args, flags)
if (!PyArg_ParseTuple(args, "s|s", &name1, &name2)) if (!PyArg_ParseTuple(args, "s|s", &name1, &name2))
return NULL; return NULL;
ENTER_TCL
if (name2 == NULL) if (name2 == NULL)
s = Tcl_GetVar(Tkapp_Interp (self), name1, flags); s = Tcl_GetVar(Tkapp_Interp (self), name1, flags);
else else
s = Tcl_GetVar2(Tkapp_Interp(self), name1, name2, flags); s = Tcl_GetVar2(Tkapp_Interp(self), name1, name2, flags);
LEAVE_TCL
if (s == NULL) if (s == NULL)
return Tkinter_Error(self); return Tkinter_Error(self);
...@@ -752,11 +867,13 @@ UnsetVar(self, args, flags) ...@@ -752,11 +867,13 @@ UnsetVar(self, args, flags)
if (!PyArg_ParseTuple(args, "s|s", &name1, &name2)) if (!PyArg_ParseTuple(args, "s|s", &name1, &name2))
return NULL; return NULL;
ENTER_TCL
if (name2 == NULL) if (name2 == NULL)
code = Tcl_UnsetVar(Tkapp_Interp(self), name1, flags); code = Tcl_UnsetVar(Tkapp_Interp(self), name1, flags);
else else
code = Tcl_UnsetVar2(Tkapp_Interp(self), name1, name2, flags); code = Tcl_UnsetVar2(Tkapp_Interp(self), name1, name2, flags);
LEAVE_TCL
if (code == TCL_ERROR) if (code == TCL_ERROR)
return Tkinter_Error(self); return Tkinter_Error(self);
...@@ -836,10 +953,14 @@ Tkapp_ExprString(self, args) ...@@ -836,10 +953,14 @@ Tkapp_ExprString(self, args)
PyObject *args; PyObject *args;
{ {
char *s; char *s;
int retval;
if (!PyArg_ParseTuple(args, "s", &s)) if (!PyArg_ParseTuple(args, "s", &s))
return NULL; 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 Tkinter_Error(self);
return Py_BuildValue("s", Tkapp_Result(self)); return Py_BuildValue("s", Tkapp_Result(self));
} }
...@@ -850,11 +971,15 @@ Tkapp_ExprLong(self, args) ...@@ -850,11 +971,15 @@ Tkapp_ExprLong(self, args)
PyObject *args; PyObject *args;
{ {
char *s; char *s;
int retval;
long v; long v;
if (!PyArg_ParseTuple(args, "s", &s)) if (!PyArg_ParseTuple(args, "s", &s))
return NULL; 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 Tkinter_Error(self);
return Py_BuildValue("l", v); return Py_BuildValue("l", v);
} }
...@@ -871,7 +996,9 @@ Tkapp_ExprDouble(self, args) ...@@ -871,7 +996,9 @@ Tkapp_ExprDouble(self, args)
if (!PyArg_ParseTuple(args, "s", &s)) if (!PyArg_ParseTuple(args, "s", &s))
return NULL; return NULL;
PyFPE_START_PROTECT("Tkapp_ExprDouble", return 0) PyFPE_START_PROTECT("Tkapp_ExprDouble", return 0)
ENTER_TCL
retval = Tcl_ExprDouble(Tkapp_Interp(self), s, &v); retval = Tcl_ExprDouble(Tkapp_Interp(self), s, &v);
LEAVE_TCL
PyFPE_END_PROTECT(retval) PyFPE_END_PROTECT(retval)
if (retval == TCL_ERROR) if (retval == TCL_ERROR)
return Tkinter_Error(self); return Tkinter_Error(self);
...@@ -884,11 +1011,15 @@ Tkapp_ExprBoolean(self, args) ...@@ -884,11 +1011,15 @@ Tkapp_ExprBoolean(self, args)
PyObject *args; PyObject *args;
{ {
char *s; char *s;
int retval;
int v; int v;
if (!PyArg_ParseTuple(args, "s", &s)) if (!PyArg_ParseTuple(args, "s", &s))
return NULL; 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 Tkinter_Error(self);
return Py_BuildValue("i", v); return Py_BuildValue("i", v);
} }
...@@ -938,7 +1069,7 @@ Tkapp_Split(self, args) ...@@ -938,7 +1069,7 @@ Tkapp_Split(self, args)
if (!PyArg_ParseTuple(args, "s", &list)) if (!PyArg_ParseTuple(args, "s", &list))
return NULL; return NULL;
return Split(self, list); return Split(list);
} }
static PyObject * static PyObject *
...@@ -963,24 +1094,45 @@ Tkapp_Merge(self, args) ...@@ -963,24 +1094,45 @@ Tkapp_Merge(self, args)
/** Tcl Command **/ /** 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 /* This is the Tcl command that acts as a wrapper for Python
* function or method. * function or method.
*/ */
static int static int
PythonCmd(clientData, interp, argc, argv) PythonCmd(clientData, interp, argc, argv)
ClientData clientData; /* Is (self, func) */ ClientData clientData;
Tcl_Interp *interp; Tcl_Interp *interp;
int argc; int argc;
char *argv[]; char *argv[];
{ {
PythonCmd_ClientData *data = (PythonCmd_ClientData *)clientData;
PyObject *self, *func, *arg, *res, *tmp; PyObject *self, *func, *arg, *res, *tmp;
int i; int i;
/* XXX Should create fresh thread state? */
ENTER_PYTHON(data->tstate)
/* TBD: no error checking here since we know, via the /* TBD: no error checking here since we know, via the
* Tkapp_CreateCommand() that the client data is a two-tuple * Tkapp_CreateCommand() that the client data is a two-tuple
*/ */
self = PyTuple_GetItem((PyObject *) clientData, 0); self = data->self;
func = PyTuple_GetItem((PyObject *) clientData, 1); func = data->func;
/* Create argument list (argv1, ..., argvN) */ /* Create argument list (argv1, ..., argvN) */
if (!(arg = PyTuple_New(argc - 1))) if (!(arg = PyTuple_New(argc - 1)))
...@@ -1008,14 +1160,22 @@ PythonCmd(clientData, interp, argc, argv) ...@@ -1008,14 +1160,22 @@ PythonCmd(clientData, interp, argc, argv)
Py_DECREF(res); Py_DECREF(res);
Py_DECREF(tmp); Py_DECREF(tmp);
LEAVE_PYTHON
return TCL_OK; return TCL_OK;
} }
static void static void
PythonCmdDelete(clientData) 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) ...@@ -1025,9 +1185,10 @@ Tkapp_CreateCommand(self, args)
PyObject *self; PyObject *self;
PyObject *args; PyObject *args;
{ {
PythonCmd_ClientData *data;
char *cmdName; char *cmdName;
PyObject *func; PyObject *func;
PyObject *data; Tcl_Command err;
if (!PyArg_ParseTuple(args, "sO", &cmdName, &func)) if (!PyArg_ParseTuple(args, "sO", &cmdName, &func))
return NULL; return NULL;
...@@ -1036,15 +1197,22 @@ Tkapp_CreateCommand(self, args) ...@@ -1036,15 +1197,22 @@ Tkapp_CreateCommand(self, args)
return NULL; return NULL;
} }
data = Py_BuildValue("OO", self, func); data = PyMem_NEW(PythonCmd_ClientData, 1);
if (!data) if (!data)
return NULL; return NULL;
data->tstate = PyThreadState_Get();
if (Tcl_CreateCommand(Tkapp_Interp(self), cmdName, PythonCmd, Py_XINCREF(self);
(ClientData) data, PythonCmdDelete) == NULL) 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"); PyErr_SetString(Tkinter_TclError, "can't create Tcl command");
Py_DECREF(data); PyMem_DEL(data);
return NULL; return NULL;
} }
...@@ -1060,11 +1228,14 @@ Tkapp_DeleteCommand(self, args) ...@@ -1060,11 +1228,14 @@ Tkapp_DeleteCommand(self, args)
PyObject *args; PyObject *args;
{ {
char *cmdName; char *cmdName;
int err;
if (!PyArg_ParseTuple(args, "s", &cmdName)) if (!PyArg_ParseTuple(args, "s", &cmdName))
return NULL; 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"); PyErr_SetString(Tkinter_TclError, "can't delete Tcl command");
return NULL; return NULL;
} }
...@@ -1074,17 +1245,71 @@ Tkapp_DeleteCommand(self, args) ...@@ -1074,17 +1245,71 @@ Tkapp_DeleteCommand(self, args)
#ifdef HAVE_CREATEFILEHANDLER
/** File Handler **/ /** 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 static void
FileHandler(clientData, mask) FileHandler(clientData, mask)
ClientData clientData; /* Is: (func, file) */ ClientData clientData;
int mask; int mask;
{ {
FileHandler_ClientData *data = (FileHandler_ClientData *)clientData;
PyObject *func, *file, *arg, *res; PyObject *func, *file, *arg, *res;
func = PyTuple_GetItem((PyObject *) clientData, 0); /* XXX Should create fresh thread state? */
file = PyTuple_GetItem((PyObject *) clientData, 1); ENTER_PYTHON(data->tstate)
func = data->func;
file = data->file;
arg = Py_BuildValue("(Oi)", file, (long) mask); arg = Py_BuildValue("(Oi)", file, (long) mask);
res = PyEval_CallObject(func, arg); res = PyEval_CallObject(func, arg);
...@@ -1095,6 +1320,7 @@ FileHandler(clientData, mask) ...@@ -1095,6 +1320,7 @@ FileHandler(clientData, mask)
PyErr_Fetch(&excInCmd, &valInCmd, &trbInCmd); PyErr_Fetch(&excInCmd, &valInCmd, &trbInCmd);
} }
Py_XDECREF(res); Py_XDECREF(res);
LEAVE_PYTHON
} }
static int static int
...@@ -1140,25 +1366,16 @@ GetFileNo(file) ...@@ -1140,25 +1366,16 @@ GetFileNo(file)
return id; return id;
} }
static PyObject* Tkapp_ClientDataDict = NULL;
#ifdef HAVE_CREATEFILEHANDLER
static PyObject * static PyObject *
Tkapp_CreateFileHandler(self, args) Tkapp_CreateFileHandler(self, args)
PyObject *self; PyObject *self;
PyObject *args; /* Is (file, mask, func) */ PyObject *args; /* Is (file, mask, func) */
{ {
PyObject *file, *func, *data; FileHandler_ClientData *data;
PyObject *idkey; PyObject *file, *func;
int mask, id; int mask, id;
FHANDLE tfile; FHANDLE tfile;
if (!Tkapp_ClientDataDict) {
if (!(Tkapp_ClientDataDict = PyDict_New()))
return NULL;
}
if (!PyArg_ParseTuple(args, "OiO", &file, &mask, &func)) if (!PyArg_ParseTuple(args, "OiO", &file, &mask, &func))
return NULL; return NULL;
id = GetFileNo(file); id = GetFileNo(file);
...@@ -1169,35 +1386,26 @@ Tkapp_CreateFileHandler(self, args) ...@@ -1169,35 +1386,26 @@ Tkapp_CreateFileHandler(self, args)
return NULL; return NULL;
} }
if (!(idkey = PyInt_FromLong(id))) data = NewFHCD(func, file, id);
if (data == NULL)
return 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); tfile = MAKEFHANDLE(id);
/* Ought to check for null Tcl_File object... */ /* Ought to check for null Tcl_File object... */
ENTER_TCL
Tcl_CreateFileHandler(tfile, mask, FileHandler, (ClientData) data); Tcl_CreateFileHandler(tfile, mask, FileHandler, (ClientData) data);
/* XXX fileHandlerDict */ LEAVE_TCL
Py_INCREF(Py_None); Py_INCREF(Py_None);
return Py_None; return Py_None;
} }
static PyObject * static PyObject *
Tkapp_DeleteFileHandler(self, args) Tkapp_DeleteFileHandler(self, args)
PyObject *self; PyObject *self;
PyObject *args; /* Args: file */ PyObject *args; /* Args: file */
{ {
PyObject *file; PyObject *file;
PyObject *idkey; FileHandler_ClientData *data;
PyObject *data;
int id; int id;
FHANDLE tfile; FHANDLE tfile;
...@@ -1207,21 +1415,13 @@ Tkapp_DeleteFileHandler(self, args) ...@@ -1207,21 +1415,13 @@ Tkapp_DeleteFileHandler(self, args)
if (id < 0) if (id < 0)
return NULL; return NULL;
if (!(idkey = PyInt_FromLong(id))) DeleteFHCD(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);
tfile = MAKEFHANDLE(id); tfile = MAKEFHANDLE(id);
/* Ought to check for null Tcl_File object... */ /* Ought to check for null Tcl_File object... */
ENTER_TCL
Tcl_DeleteFileHandler(tfile); Tcl_DeleteFileHandler(tfile);
/* XXX fileHandlerDict */ LEAVE_TCL
Py_INCREF(Py_None); Py_INCREF(Py_None);
return Py_None; return Py_None;
} }
...@@ -1232,13 +1432,12 @@ Tkapp_DeleteFileHandler(self, args) ...@@ -1232,13 +1432,12 @@ Tkapp_DeleteFileHandler(self, args)
staticforward PyTypeObject Tktt_Type; staticforward PyTypeObject Tktt_Type;
typedef struct typedef struct {
{
PyObject_HEAD PyObject_HEAD
Tcl_TimerToken token; Tcl_TimerToken token;
PyObject *func; PyObject *func;
} PyThreadState *tstate;
TkttObject; } TkttObject;
static PyObject * static PyObject *
Tktt_DeleteTimerHandler(self, args) Tktt_DeleteTimerHandler(self, args)
...@@ -1246,13 +1445,18 @@ Tktt_DeleteTimerHandler(self, args) ...@@ -1246,13 +1445,18 @@ Tktt_DeleteTimerHandler(self, args)
PyObject *args; PyObject *args;
{ {
TkttObject *v = (TkttObject *)self; TkttObject *v = (TkttObject *)self;
PyObject *func = v->func;
if (!PyArg_ParseTuple(args, "")) if (!PyArg_ParseTuple(args, ""))
return NULL; return NULL;
if (v->func != NULL) { if (v->token != NULL) {
Tcl_DeleteTimerHandler(v->token); Tcl_DeleteTimerHandler(v->token);
PyMem_DEL(v->func); v->token = NULL;
}
if (func != NULL) {
v->func = NULL; v->func = NULL;
Py_DECREF(func);
Py_DECREF(v); /* See Tktt_New() */
} }
Py_INCREF(Py_None); Py_INCREF(Py_None);
return Py_None; return Py_None;
...@@ -1265,8 +1469,7 @@ static PyMethodDef Tktt_methods[] = ...@@ -1265,8 +1469,7 @@ static PyMethodDef Tktt_methods[] =
}; };
static TkttObject * static TkttObject *
Tktt_New(token, func) Tktt_New(func)
Tcl_TimerToken token;
PyObject *func; PyObject *func;
{ {
TkttObject *v; TkttObject *v;
...@@ -1275,9 +1478,13 @@ Tktt_New(token, func) ...@@ -1275,9 +1478,13 @@ Tktt_New(token, func)
if (v == NULL) if (v == NULL)
return NULL; return NULL;
v->token = token; Py_INCREF(func);
v->token = NULL;
v->func = func; 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; return v;
} }
...@@ -1285,6 +1492,11 @@ static void ...@@ -1285,6 +1492,11 @@ static void
Tktt_Dealloc(self) Tktt_Dealloc(self)
PyObject *self; PyObject *self;
{ {
TkttObject *v = (TkttObject *)self;
PyObject *func = v->func;
Py_XDECREF(func);
PyMem_DEL(self); PyMem_DEL(self);
} }
...@@ -1335,8 +1547,20 @@ static void ...@@ -1335,8 +1547,20 @@ static void
TimerHandler(clientData) TimerHandler(clientData)
ClientData clientData; ClientData clientData;
{ {
PyObject *func = (PyObject *)clientData; TkttObject *v = (TkttObject *)clientData;
PyObject *res = PyEval_CallObject(func, NULL); 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) { if (res == NULL) {
errorInCmd = 1; errorInCmd = 1;
...@@ -1344,6 +1568,8 @@ TimerHandler(clientData) ...@@ -1344,6 +1568,8 @@ TimerHandler(clientData)
} }
else else
Py_DECREF(res); Py_DECREF(res);
LEAVE_PYTHON
} }
static PyObject * static PyObject *
...@@ -1353,7 +1579,7 @@ Tkapp_CreateTimerHandler(self, args) ...@@ -1353,7 +1579,7 @@ Tkapp_CreateTimerHandler(self, args)
{ {
int milliseconds; int milliseconds;
PyObject *func; PyObject *func;
Tcl_TimerToken token; TkttObject *v;
if (!PyArg_ParseTuple(args, "iO", &milliseconds, &func)) if (!PyArg_ParseTuple(args, "iO", &milliseconds, &func))
return NULL; return NULL;
...@@ -1361,13 +1587,13 @@ Tkapp_CreateTimerHandler(self, args) ...@@ -1361,13 +1587,13 @@ Tkapp_CreateTimerHandler(self, args)
PyErr_SetString(PyExc_TypeError, "bad argument list"); PyErr_SetString(PyExc_TypeError, "bad argument list");
return NULL; return NULL;
} }
token = Tcl_CreateTimerHandler(milliseconds, TimerHandler, v = Tktt_New(func);
(ClientData)func); v->token = Tcl_CreateTimerHandler(milliseconds, TimerHandler,
(ClientData)v);
return (PyObject *) Tktt_New(token, func); return (PyObject *) v;
} }
/** Event Loop **/ /** Event Loop **/
...@@ -1387,21 +1613,18 @@ Tkapp_MainLoop(self, args) ...@@ -1387,21 +1613,18 @@ Tkapp_MainLoop(self, args)
!errorInCmd) !errorInCmd)
{ {
int result; int result;
#ifdef HAVE_PYTCL_WAITUNTILEVENT
#ifdef WITH_THREAD
ENTER_TCL
result = Tcl_DoOneEvent(TCL_DONT_WAIT); result = Tcl_DoOneEvent(TCL_DONT_WAIT);
if (PyErr_CheckSignals() != 0) release_lock(tcl_lock);
return NULL; if (result == 0)
if (result) Sleep(20);
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();
Py_END_ALLOW_THREADS Py_END_ALLOW_THREADS
#else #else
result = Tcl_DoOneEvent(0); result = Tcl_DoOneEvent(0);
#endif #endif
if (PyErr_CheckSignals() != 0) if (PyErr_CheckSignals() != 0)
return NULL; return NULL;
if (result < 0) if (result < 0)
...@@ -1430,7 +1653,9 @@ Tkapp_DoOneEvent(self, args) ...@@ -1430,7 +1653,9 @@ Tkapp_DoOneEvent(self, args)
if (!PyArg_ParseTuple(args, "|i", &flags)) if (!PyArg_ParseTuple(args, "|i", &flags))
return NULL; return NULL;
ENTER_TCL
rv = Tcl_DoOneEvent(flags); rv = Tcl_DoOneEvent(flags);
LEAVE_TCL
return Py_BuildValue("i", rv); return Py_BuildValue("i", rv);
} }
...@@ -1511,7 +1736,9 @@ static void ...@@ -1511,7 +1736,9 @@ static void
Tkapp_Dealloc(self) Tkapp_Dealloc(self)
PyObject *self; PyObject *self;
{ {
ENTER_TCL
Tcl_DeleteInterp(Tkapp_Interp(self)); Tcl_DeleteInterp(Tkapp_Interp(self));
LEAVE_TCL
PyMem_DEL(self); PyMem_DEL(self);
DisableEventHook(); DisableEventHook();
} }
...@@ -1588,7 +1815,6 @@ static PyMethodDef moduleMethods[] = ...@@ -1588,7 +1815,6 @@ static PyMethodDef moduleMethods[] =
}; };
#ifdef WAIT_FOR_STDIN #ifdef WAIT_FOR_STDIN
#define WAITFLAG 0
static int stdin_ready = 0; static int stdin_ready = 0;
...@@ -1599,60 +1825,67 @@ MyFileProc(clientData, mask) ...@@ -1599,60 +1825,67 @@ MyFileProc(clientData, mask)
{ {
stdin_ready = 1; stdin_ready = 1;
} }
#else
#define WAITFLAG TCL_DONT_WAIT
#endif
static PyInterpreterState *event_interp = NULL; static PyThreadState *event_tstate = NULL;
static int static int
EventHook() EventHook()
{ {
PyThreadState *tstate, *save_tstate; FHANDLE tfile;
#ifdef WAIT_FOR_STDIN
FHANDLE tfile = MAKEFHANDLE(((int)fileno(stdin)));
ENTER_PYTHON(event_tstate)
tfile = MAKEFHANDLE(fileno(stdin));
stdin_ready = 0; stdin_ready = 0;
Tcl_CreateFileHandler(tfile, TCL_READABLE, MyFileProc, NULL); 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 #endif
tstate = PyThreadState_New(event_interp);
save_tstate = PyThreadState_Swap(NULL); if (result < 0)
PyEval_RestoreThread(tstate); break;
#ifdef WAIT_FOR_STDIN }
while (!errorInCmd && !stdin_ready) Tcl_DeleteFileHandler(tfile);
#endif
Tcl_DoOneEvent(WAITFLAG);
if (errorInCmd) { if (errorInCmd) {
errorInCmd = 0; errorInCmd = 0;
PyErr_Restore(excInCmd, valInCmd, trbInCmd); PyErr_Restore(excInCmd, valInCmd, trbInCmd);
excInCmd = valInCmd = trbInCmd = NULL; excInCmd = valInCmd = trbInCmd = NULL;
PyErr_Print(); PyErr_Print();
} }
PyThreadState_Clear(tstate); LEAVE_PYTHON
PyEval_SaveThread();
PyThreadState_Swap(save_tstate);
PyThreadState_Delete(tstate);
#ifdef WAIT_FOR_STDIN
Tcl_DeleteFileHandler(tfile);
#endif
return 0; return 0;
} }
#endif
static void static void
EnableEventHook() EnableEventHook()
{ {
#ifdef WAIT_FOR_STDIN
if (PyOS_InputHook == NULL) { if (PyOS_InputHook == NULL) {
event_interp = PyThreadState_Get()->interp; event_tstate = PyThreadState_Get();
PyOS_InputHook = EventHook; PyOS_InputHook = EventHook;
} }
#endif
} }
static void static void
DisableEventHook() DisableEventHook()
{ {
#ifdef WAIT_FOR_STDIN
if (Tk_GetNumMainWindows() == 0 && PyOS_InputHook == EventHook) { if (Tk_GetNumMainWindows() == 0 && PyOS_InputHook == EventHook) {
PyOS_InputHook = NULL; PyOS_InputHook = NULL;
} }
#endif
} }
...@@ -1689,7 +1922,10 @@ init_tkinter() ...@@ -1689,7 +1922,10 @@ init_tkinter()
PyObject *m, *d; PyObject *m, *d;
Tkapp_Type.ob_type = &PyType_Type; 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); m = Py_InitModule("_tkinter", moduleMethods);
...@@ -1710,6 +1946,8 @@ init_tkinter() ...@@ -1710,6 +1946,8 @@ init_tkinter()
ins_string(d, "TCL_VERSION", TCL_VERSION); ins_string(d, "TCL_VERSION", TCL_VERSION);
PyDict_SetItemString(d, "TkappType", (PyObject *)&Tkapp_Type); PyDict_SetItemString(d, "TkappType", (PyObject *)&Tkapp_Type);
Tktt_Type.ob_type = &PyType_Type;
PyDict_SetItemString(d, "TkttType", (PyObject *)&Tktt_Type); PyDict_SetItemString(d, "TkttType", (PyObject *)&Tktt_Type);
if (PyErr_Occurred()) if (PyErr_Occurred())
...@@ -1769,8 +2007,8 @@ PyMacConvertEvent(eventPtr) ...@@ -1769,8 +2007,8 @@ PyMacConvertEvent(eventPtr)
{ {
WindowPtr frontwin; WindowPtr frontwin;
/* /*
** Sioux eats too many events, so we don't pass it everything. ** Sioux eats too many events, so we don't pass it everything. We
** We always pass update events to Sioux, and we only pass other events if ** 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 ** 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. ** in that case, but at least we can scroll the sioux window.
** Note that the SIOUXIsAppWindow() routine we use here is not really ** Note that the SIOUXIsAppWindow() routine we use here is not really
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment