python / python/cpython

tkinter runs a busy-wait loop on interactive Python

Open
#140,488 5 comments 0 reactions 0 assignees View on GitHub

Nobody has claimed this yet.

extension-modules topic-tkinter type-bug
Dominant language
Python
Stars
77.2k
Forks
35.9k
PR merge metrics
PR metrics pending

Description

Bug report

Bug description:

In EventHook in Modules/_tkinter.c, we used to have the following block to run the event loop:

#if defined(WITH_THREAD) || defined(MS_WINDOWS)
        Py_BEGIN_ALLOW_THREADS
        if(tcl_lock)PyThread_acquire_lock(tcl_lock, 1);
        tcl_tstate = event_tstate;
        
        result = Tcl_DoOneEvent(TCL_DONT_WAIT);
        
        tcl_tstate = NULL;
        if(tcl_lock)PyThread_release_lock(tcl_lock);
        if (result == 0)
            Sleep(Tkinter_busywaitinterval);
        Py_END_ALLOW_THREADS
#else
        result = Tcl_DoOneEvent(0);
#endif

in bpo-31370, support for threads-less builds was removed, and this block became

        Py_BEGIN_ALLOW_THREADS
        if(tcl_lock)PyThread_acquire_lock(tcl_lock, 1);
        tcl_tstate = event_tstate;
        
        result = Tcl_DoOneEvent(TCL_DONT_WAIT);
        
        tcl_tstate = NULL;
        if(tcl_lock)PyThread_release_lock(tcl_lock);
        if (result == 0)
            Sleep(Tkinter_busywaitinterval);
        Py_END_ALLOW_THREADS

This is a bit unfortunate, as we now back to running a busy-wait loop. See Guido van Rossum's comment of May 23, 1998: 7bf15648

I guess this block could be

        if (self->threaded) {
            /* Allow other Python threads to run. */
            ENTER_TCL
            result = Tcl_DoOneEvent(0);
            LEAVE_TCL
        }
        else {
            Py_BEGIN_ALLOW_THREADS
            if(tcl_lock)PyThread_acquire_lock(tcl_lock, 1);
            tcl_tstate = tstate;
            result = Tcl_DoOneEvent(TCL_DONT_WAIT);
            tcl_tstate = NULL;
            if(tcl_lock)PyThread_release_lock(tcl_lock);
            if (result == 0)
                Sleep(Tkinter_busywaitinterval);
            Py_END_ALLOW_THREADS
        }

in analogy with the event loop in _tkinter_tkapp_mainloop_impl. As self->threaded is true in most builds, this would avoid the busy-wait loop.

I am happy to submit a PR, but I'll wait until PR #140147 is in to avoid unnecessary complications to this code.

CPython versions tested on:

CPython main branch

Operating systems tested on:

macOS

Contributor guide

Open the contributing guide

First steps

  1. Read the whole issue, then the project's contributing guide.
  2. Comment on the issue to say you are picking it up — it saves two people doing the same work.
  3. Fork the repository and make your change on a branch.
  4. Open a pull request that references the issue number.

Research direction

Start in Modules/_tkinter.c at EventHook and compare its event-loop block with _tkinter_tkapp_mainloop_impl. Check PR #140147 before changing this code. Done means interactive Python avoids the busy-wait loop in threaded builds while preserving the shown non-threaded behavior.

Written by the indexing model from the issue text.

Assessment

Tech stack
c, python
Domain
desktop
Issue type
Bug
Difficulty
4/5
Estimated time
3-5 days
Activity status
Quiet
Clarity
Clearly specified
Newbie friendliness
55/100

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.