--- /dev/null
+Fix a busy loop in :mod:`tkinter` on interactive Python. When a Tcl command
+running its own event loop (such as ``vwait`` or :meth:`!wait_variable`) was
+active and input arrived on stdin, the event loop kept spinning at 100% CPU.
+The stdin file handler is now removed as soon as input is available. Based on
+a patch by Michiel de Hoon.
static void
MyFileProc(void *clientData, int mask)
{
+ int tfile = (int)(Py_intptr_t)clientData;
stdin_ready = 1;
+ /* Stop watching stdin now that input is available. Doing it here rather
+ than after the loop below ensures that a nested event loop (e.g. the one
+ started by wait_variable) does not keep waking up on the same unread
+ input, spinning at 100% CPU. */
+ Tcl_DeleteFileHandler(tfile);
}
#endif
errorInCmd = 0;
#ifndef MS_WINDOWS
tfile = fileno(stdin);
- Tcl_CreateFileHandler(tfile, TCL_READABLE, MyFileProc, NULL);
+ Tcl_CreateFileHandler(tfile, TCL_READABLE, MyFileProc,
+ (void *)(Py_intptr_t)tfile);
#endif
- while (!errorInCmd && !stdin_ready) {
+ while (!stdin_ready) {
int result;
#ifdef MS_WINDOWS
if (_kbhit()) {
Sleep(Tkinter_busywaitinterval);
Py_END_ALLOW_THREADS
+ /* Report an exception raised in a callback, but keep pumping events
+ instead of returning to the prompt: without readline there is no
+ input waiting on stdin yet, so returning here would block in fgets
+ until the user hits enter, freezing later callbacks. */
+ if (errorInCmd) {
+ errorInCmd = 0;
+ PyErr_SetRaisedException(excInCmd);
+ excInCmd = NULL;
+ PyErr_Print();
+ }
if (result < 0)
break;
}
#ifndef MS_WINDOWS
- Tcl_DeleteFileHandler(tfile);
+ if (!stdin_ready)
+ Tcl_DeleteFileHandler(tfile);
#endif
- if (errorInCmd) {
- errorInCmd = 0;
- PyErr_SetRaisedException(excInCmd);
- excInCmd = NULL;
- PyErr_Print();
- }
PyEval_SaveThread();
return 0;
}