Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
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.
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
Fix a hang in :mod:`tkinter` on interactive Python built without
:mod:`readline`. An exception raised in a callback no longer causes the
event loop to stop and wait for the user to press Enter; pending callbacks
now keep running until input is actually available on stdin.
30 changes: 21 additions & 9 deletions Modules/_tkinter.c
Original file line number Diff line number Diff line change
Expand Up @@ -3433,7 +3433,13 @@ static int stdin_ready = 0;
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

Expand All @@ -3450,9 +3456,10 @@ EventHook(void)
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()) {
Expand All @@ -3472,18 +3479,23 @@ EventHook(void)
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;
}
Expand Down
Loading