[Python-Dev] PEP needed? Introducing Tcl objects

Martin v. Loewis martin@v.loewis.de
22 Feb 2002 11:03:53 +0100


"Jeff Hobbs" <JeffH@ActiveState.com> writes:

> That's correct - I should have looked a bit more into what I did
> before (I was always tying in another GUI's event loop).  However,
> I don't see why you should not consider the extra event source.
> Tk uses this itself for X.  It would be something like:

That does not work, either. I'm using the patch attached below, and
I'm getting the output

...
setupproc called 729
setupproc called 730
setupproc called 731
setupproc called 732
setupproc called 733
setupproc called 734
setupproc called 735
...

That is, even though the setupproc is called, and even though the
select is not blocking anymore, DoOneEvent does not return (I don't
see the "Event done" messages).

Regards,
Martin

Index: _tkinter.c
===================================================================
RCS file: /cvsroot/python/python/dist/src/Modules/_tkinter.c,v
retrieving revision 1.123
diff -u -r1.123 _tkinter.c
--- _tkinter.c	26 Jan 2002 20:21:50 -0000	1.123
+++ _tkinter.c	22 Feb 2002 09:56:13 -0000
@@ -133,6 +139,10 @@
    These locks expand to several statements and brackets; they should not be
    used in branches of if statements and the like.
 
+   To give other threads a chance to access Tcl while the Tk mainloop is
+   runnning, an input source is registered with Tcl which results in Tcl
+   not blocking for more than 20ms.
+
 */
 
 static PyThread_type_lock tcl_lock = 0;
@@ -237,24 +248,6 @@
 
 /**** Utils ****/
 
-#ifdef WITH_THREAD
-#ifndef MS_WINDOWS
-
-/* Millisecond sleep() for Unix platforms. */
-
-static void
-Sleep(int milli)
-{
-	/* XXX Too bad if you don't have select(). */
-	struct timeval t;
-	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_WINDOWS */
-#endif /* WITH_THREAD */
-
-
 static char *
 AsString(PyObject *value, PyObject *tmp)
 {
@@ -1671,6 +1948,37 @@
 
 /** Event Loop **/
 
+#ifdef WITH_THREAD
+static int setupproc_registered;
+/*
+ *----------------------------------------------------------------------
+ *
+ * TkinterSetupProc --
+ *
+ *	This procedure implements the setup part of the Tkinter
+ *	event source.  It is invoked by Tcl_DoOneEvent before entering
+ *	the notifier to check for events on all displays.
+ *
+ * Results:
+ *	None.
+ *
+ * Side effects:
+ *	The maximum block time will be set to 20000 usecs to ensure that
+ *	the notifier returns control to Tcl.
+ *
+ *----------------------------------------------------------------------
+ */
+
+static void
+TkinterSetupProc(ClientData clientData, int flags)
+{
+    static Tcl_Time blockTime = { 0, 20000 };
+    static int i = 0;
+    printf("setupproc called %d\n",++i);
+    Tcl_SetMaxBlockTime(&blockTime);
+}
+#endif
+
 static PyObject *
 Tkapp_MainLoop(PyObject *self, PyObject *args)
 {
@@ -1682,22 +1990,29 @@
 	if (!PyArg_ParseTuple(args, "|i:mainloop", &threshold))
 		return NULL;
 
+#ifdef WITH_THREAD
+	if (!setupproc_registered) {
+		Tcl_CreateEventSource(TkinterSetupProc, NULL, NULL);
+		setupproc_registered = 1;
+	}
+#endif
+
 	quitMainLoop = 0;
 	while (Tk_GetNumMainWindows() > threshold &&
 	       !quitMainLoop &&
 	       !errorInCmd)
 	{
 		int result;
+						    
 
 #ifdef WITH_THREAD
 		Py_BEGIN_ALLOW_THREADS
 		PyThread_acquire_lock(tcl_lock, 1);
 		tcl_tstate = tstate;
-		result = Tcl_DoOneEvent(TCL_DONT_WAIT);
+		result = Tcl_DoOneEvent(0);
+		printf("Event done\n");
 		tcl_tstate = NULL;
 		PyThread_release_lock(tcl_lock);
-		if (result == 0)
-			Sleep(20);
 		Py_END_ALLOW_THREADS
 #else
 		result = Tcl_DoOneEvent(0);
@@ -2033,12 +2364,10 @@
 		PyThread_acquire_lock(tcl_lock, 1);
 		tcl_tstate = event_tstate;
 
-		result = Tcl_DoOneEvent(TCL_DONT_WAIT);
+		result = Tcl_DoOneEvent(0);
 
 		tcl_tstate = NULL;
 		PyThread_release_lock(tcl_lock);
-		if (result == 0)
-			Sleep(20);
 		Py_END_ALLOW_THREADS
 #else
 		result = Tcl_DoOneEvent(0);