Skip to content

Rename PyGILState_Check() #131264

Open
Open

Description

@ericsnowcurrently

Feature or enhancement

Proposal:

Nearly all of the C-API expects that the current thread holds the GIL when called. Mostly, users of the C-API had to know before calling the C-API. Beck in the day it wasn't obvious how. This was the motivation behind adding PyGILState_Check().

The key point in this issue is that the name PyGILState_Check() unnecessarily associates the capability with the PyGILState_* API. Users would benefit from a different function name. I'd think something like Py_HoldsGIL() would be better.


Why was the function added as PyGILState_Check()? Perhaps @kristjanvalur remembers. It was added in 2013, 10 years after the PEP 311 implementation, so it's not because it was part of the PEP. I'd guess it's mostly because it was implemented using PyGILState_GetThisThreadState().

It's also notable that a lot of things have changed in the runtime since 2013. At the time PyGILState_Check() was added:

  • you could use PyThreadState_Get() (or PyThreadState_GET()) to get the tstate active in the current thread, but only if it held the GIL
  • you could get the last active tstate in the current thread using PyGILState_GetThisThreadState()
  • internally there was a static PyThreadState_IsCurrent() that sort of merged the two

Here some of the developments since then:

  • every tstate now knows whether or not it holds the GIL
  • the runtime now keeps track of the last tstate to hold the GIL
  • the current tstate has moved from a global variable to a thread-local variable
  • each interpreter now has its own GIL

Here's the history in some detail:

(expand)
  • 1997 - thread state (& interp state) introduced (commit a027efa)
    • current tstate stored in static global variable
    • PyThreadState_Get() exposes it
    • PyThreadState_Swap() modifies it
    • GIL must be held to call it, so that global effectively indicates which tstate (i.e. thread) holds the GIL
  • 1998 - added PyThreadState_GET() macro (commit 275ea67)
    • current tstate global variable moved to "internal" C-API (commit 18bc7c2)
  • 2003 - PEP 311 adds the PyGILState_* API (commit 8d98d2c),
    • last tstate used in current OS thread stored in thread-specific storage (autoTLSkey)
    • PyGILState_GetThisThreadState() exposes it
    • it is set to NULL when a tstate is destroyed and it was the last one used in the current thread
    • adds static PyThreadState_IsCurrent() that indicates if that thread-local tstate holds the GIL
    • PyGILState_Check() does not exist yet
  • 2009 - new GIL impl (commit 074e5ed)
    • adds new "last_holder" and "held" state
  • 2013 - PyGILState_Check() added (Add api PyGILState_Check #61724, commit 684cd0e)
    • effectively exposes PyThreadState_IsCurrent(PyGILState_GetThisThreadState())
  • 2017 - _PyRuntimeState added (commit 2ebc5ce)
    • moved "current" tstate from global var in pystate.c (_PyThreadState_Current) to _PyRuntime.gilstate.tstate_current
    • moved GILState-related global vars to _PyRuntime.gilstate
    • moved GIL from global vars to _PyRuntime.ceval.gil
  • 2018 - added _PyThreadState_GET(), without NULL check (commit 50b4857)
  • 2023 - moved "current" tstate from _PyRuntime.gilstate.tstate_current to _PyRuntime.tstate_current (commit 6036c3e)
  • 2023 - moved "current" tstate to a thread-local variable in pystate.c (commit f8abfa3)
  • 2023 - added internal current_thread_holds_gil (commit 92d8bff)
  • 2023 - moved GIL to the interpreter state (commit 5c9ee49)
  • 2024 - added PyThreadState._status.holds_gil (commit be1dfcc)

Has this already been discussed elsewhere?

No response given

Links to previous discussion of this feature:

No response

Activity

  1. added
    interpreter-core(Objects, Python, Grammar, and Parser dirs)
    and removed
    3.14bugs and security fixes
    on Mar 14, 2025
  2. ericsnowcurrently commented on Mar 14, 2025

    @ericsnowcurrently
    MemberAuthor

    FTR, there's a recent proposal that discusses some of the related name issues: https://discuss.python.org/t/a-new-api-for-ensuring-releasing-thread-states/83959.

  3. ZeroIntensity commented on Mar 15, 2025

    @ZeroIntensity
    Member

    I'd think something like Py_HoldsGIL() would be better.

    I'm pretty much against any new APIs with "GIL" in the name, because they're not very intuitive for free-threading. How about Py_HoldsTstate, named after the assertion API (_Py_AssertHoldsTstate)?

  4. ericsnowcurrently commented on Mar 15, 2025

    @ericsnowcurrently
    MemberAuthor

    FWIW, "HoldsTstate" doesn't mean much to me.

  5. ZeroIntensity commented on Mar 15, 2025

    @ZeroIntensity
    Member

    Yeah, there's not a good correlation between thread states and the GIL (yet, hopefully). If we want to stay away from thread states, how about something like Py_CanCallInterpreter?

  6. colesbury commented on Mar 17, 2025

    @colesbury
    Contributor

    PyThreadState_GetUnchecked() != NULL seems like a sufficient way to decide if it's safe to call into the interpreter.

  7. kristjanvalur commented on Mar 17, 2025

    @kristjanvalur
    Contributor

    Perhaps @kristjanvalur remembers.

    Not exactly, but everything GIL-related at the time was probably PyGILState_ prefixed. Also to avoid bike shedding, putting a new function in an existing naming scope was probably useful at the time :)

  8. ericsnowcurrently commented on Mar 17, 2025

    @ericsnowcurrently
    MemberAuthor

    Yeah, sounds about right. 😄

  9. ericsnowcurrently commented on Mar 17, 2025

    @ericsnowcurrently
    MemberAuthor

    PyThreadState_GetUnchecked() != NULL seems like a sufficient way to decide if it's safe to call into the interpreter.

    You're probably right.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions