Skip to content

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Appearance settings

gh-127989: C API: Refer to attached thread states instead of the GIL #127990

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 76 commits into from
Mar 20, 2025
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
Show all changes
76 commits
Select commit Hold shift + click to select a range
ad7ee2d
Document the term 'thread state.'
ZeroIntensity Dec 14, 2024
bea045a
Change term for PyDict_GetItem
ZeroIntensity Dec 14, 2024
e286ba5
Change term for PyErr_Occurred
ZeroIntensity Dec 14, 2024
6006b22
Change term for PyErr_SetInterrupt
ZeroIntensity Dec 14, 2024
8dc3928
Change term for PyErr_SetInterruptEx
ZeroIntensity Dec 14, 2024
f46ca3d
Change term for PyConfig_Get
ZeroIntensity Dec 14, 2024
555a03c
Change term for PyConfig_Names
ZeroIntensity Dec 14, 2024
ddb98cd
Change term for PyConfig_Set
ZeroIntensity Dec 14, 2024
5d423f8
Change term for PyUnstable_AtExit
ZeroIntensity Dec 14, 2024
d2d2f67
Change term for Py_Initialize note
ZeroIntensity Dec 14, 2024
aa9c4c6
Change term for PyInterpreterState_Get
ZeroIntensity Dec 14, 2024
c05be67
Change term for PyInterpreterState_GetID
ZeroIntensity Dec 14, 2024
3a69922
Change term for PyThreadState_SetAsyncEx
ZeroIntensity Dec 14, 2024
3336ad7
Change term for Py_NewInterpreterFromConfig
ZeroIntensity Dec 14, 2024
3991681
Change term for Py_FinalizeEx
ZeroIntensity Dec 14, 2024
6d397eb
Change term for Py_AddPendingCall
ZeroIntensity Dec 14, 2024
3b7b3ff
Change term for PyEval_SetProfile
ZeroIntensity Dec 14, 2024
5358d14
Change term for PyEval_SetProfileAllThreads
ZeroIntensity Dec 14, 2024
093ba05
Change term for PyEval_SetTrace
ZeroIntensity Dec 14, 2024
11582fa
Change term for PyEval_SetTraceAllThreads
ZeroIntensity Dec 14, 2024
3d55d2d
Change term for PyRefTracer_SetTracer
ZeroIntensity Dec 14, 2024
2785c1d
Change term for PyRefTracer_GetTracer
ZeroIntensity Dec 14, 2024
6d80b02
Change term for PyMutex_Lock
ZeroIntensity Dec 14, 2024
94e046b
Change term for allocator domains
ZeroIntensity Dec 14, 2024
713beb2
Change term for raw memory interface
ZeroIntensity Dec 14, 2024
2140bde
Change term for memory interface
ZeroIntensity Dec 14, 2024
f608211
Change term for object allocators
ZeroIntensity Dec 14, 2024
d619387
Change term for PyMem_SetAllocator
ZeroIntensity Dec 14, 2024
e7828ed
Change term for PyMem_SetAllocator (again)
ZeroIntensity Dec 14, 2024
6b33565
Change term for debug hooks
ZeroIntensity Dec 14, 2024
6230177
Change term for debug hooks (again)
ZeroIntensity Dec 14, 2024
f74832a
Change term for PyState*
ZeroIntensity Dec 14, 2024
7161fc0
Change term for various things
ZeroIntensity Dec 14, 2024
d58b74f
Change term for clock functions
ZeroIntensity Dec 14, 2024
a761c4a
Change term for raw clock functions
ZeroIntensity Dec 14, 2024
de122f5
Change term for tp_dealloc warning
ZeroIntensity Dec 14, 2024
0b0176b
Change term for the types tutorial
ZeroIntensity Dec 14, 2024
c15f5c4
Add more information about thread states.
ZeroIntensity Dec 14, 2024
0130b20
Change terms for 'cautions regarding runtime finalization'
ZeroIntensity Dec 14, 2024
a1e47b7
Simplify PyEval_SaveThread and PyEval_RestoreThread
ZeroIntensity Dec 14, 2024
0173c46
Some more terminology fixups.
ZeroIntensity Dec 14, 2024
166d970
Fix Sphinx build errors.
ZeroIntensity Dec 14, 2024
adae494
Change some usage of the term 'global interpreter lock'
ZeroIntensity Dec 14, 2024
a70ab11
Add some more terms for disambiguation.
ZeroIntensity Dec 14, 2024
8584c86
Change usage of some terms.
ZeroIntensity Dec 14, 2024
48b145a
Play around with terminology.
ZeroIntensity Dec 14, 2024
8ce3d7d
Various terminology changes.
ZeroIntensity Dec 14, 2024
e5f1d18
Change the glossary definition a little bit.
ZeroIntensity Dec 14, 2024
905ca5b
Change some phrasing.
ZeroIntensity Dec 14, 2024
f2826c4
Fix some usage of terms.
ZeroIntensity Dec 14, 2024
5c44391
Change some usage of 'global interpreter lock'
ZeroIntensity Dec 15, 2024
7d5877f
Fix pre-commit lint.
ZeroIntensity Dec 15, 2024
283fc92
Delete redundant file
ZeroIntensity Dec 16, 2024
a7488ab
Revert "Delete redundant file"
ZeroIntensity Dec 16, 2024
4c532b6
Remove accidental find-and-replace change.
ZeroIntensity Dec 16, 2024
352acc5
Switch to a reference
ZeroIntensity Dec 16, 2024
ed550bc
Use a less wordy phrase for detaching.
ZeroIntensity Dec 16, 2024
d7cf403
Fix a typo in PyInterpreterState_Get
ZeroIntensity Dec 16, 2024
f61a6da
Change some wording in PyEval_SaveThread and PyEval_RestoreThread
ZeroIntensity Dec 16, 2024
36af941
Change wording for PyThreadState_Swap
ZeroIntensity Dec 16, 2024
0b23daa
Make glossary terms more clear.
ZeroIntensity Dec 16, 2024
163b0b8
Change up wording for 'attached thread state'
ZeroIntensity Dec 16, 2024
3b2bed8
Clarify that the C API is what needs a thread state.
ZeroIntensity Dec 16, 2024
80ae46b
Update Doc/glossary.rst
ZeroIntensity Dec 17, 2024
3dee63f
Update Doc/glossary.rst
ZeroIntensity Dec 17, 2024
9ed3a0b
Drop the phrase 'current thread state' and only use 'attached thread …
ZeroIntensity Feb 12, 2025
b3cbbf8
Add extra note about free-threading.
ZeroIntensity Feb 12, 2025
1c37d59
Add a seealso.
ZeroIntensity Feb 12, 2025
32a4937
Merge from main.
ZeroIntensity Feb 12, 2025
2fba67c
Fix weird merge artifact.
ZeroIntensity Feb 12, 2025
a0dc12c
Merge branch 'main' into clarify-gil-tstate
ZeroIntensity Feb 27, 2025
94619f6
Change term for new PyConfig* APIs
ZeroIntensity Feb 27, 2025
e6f1f28
Change term for PyUnstable_InterpreterState_GetMainModule
ZeroIntensity Feb 27, 2025
828d24b
Change term for Py_fopen
ZeroIntensity Feb 27, 2025
ace174f
Merge branch 'main' into clarify-gil-tstate
encukou Mar 19, 2025
788d735
Change artifact from 'runtime context' to 'thread state'
ZeroIntensity Mar 20, 2025
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
Prev Previous commit
Next Next commit
Drop the phrase 'current thread state' and only use 'attached thread …
…state' in its place
  • Loading branch information
ZeroIntensity committed Feb 12, 2025
commit 9ed3a0b376066129879af38fd2671a32386bc763
62 changes: 30 additions & 32 deletions 62 Doc/c-api/init.rst
Original file line number Diff line number Diff line change
Expand Up @@ -963,8 +963,8 @@ a file, so that other Python threads can run in the meantime.

The Python interpreter keeps some thread-specific bookkeeping information
inside a data structure called :c:type:`PyThreadState`, known as a :term:`thread state`.
There's also one :term:`thread-local variable <current thread state>` pointing to the
current :c:type:`PyThreadState`: it can be retrieved using :c:func:`PyThreadState_Get`.
Each OS thread has a thread-local pointer to a :c:type:`PyThreadState`; a thread state
referenced by this pointer is considered to be :term:`attached <attached thread state>`.

A thread can only have one :term:`attached thread state` at a time. An attached
thread state is typically analogous with holding the :term:`GIL`, except on
Expand Down Expand Up @@ -1204,14 +1204,13 @@ code, or when embedding the Python interpreter:

.. c:function:: PyThreadState* PyEval_SaveThread()

Detach the :term:`thread state <attached thread state>` and
return it. The :term:`current thread state` will be ``NULL`` upon
returning.
Detach the :term:`attached thread state` and return it.
The thread will have no :term:`thread state` upon returning.


.. c:function:: void PyEval_RestoreThread(PyThreadState *tstate)

Set the :term:`current thread state` to *tstate*, which must not be ``NULL``.
Set the :term:`attached thread state` to *tstate*.
The passed :term:`thread state` **should not** be :term:`attached <attached thread state>`,
otherwise deadlock ensues. *tstate* will be attached upon returning.

Expand All @@ -1227,13 +1226,13 @@ code, or when embedding the Python interpreter:

.. c:function:: PyThreadState* PyThreadState_Get()

Return the :term:`current thread state`. If the :term:`current thread state` is ``NULL``
(such as when inside of :c:macro:`Py_BEGIN_ALLOW_THREADS` block), then this issues a fatal
error (so that the caller needn't check for ``NULL``).
Return the :term:`attached thread state`. If the thread has no attached
thread state, (such as when inside of :c:macro:`Py_BEGIN_ALLOW_THREADS`
block), then this issues a fatal error (so that the caller needn't check
for ``NULL``).

See also :c:func:`PyThreadState_GetUnchecked`.


.. c:function:: PyThreadState* PyThreadState_GetUnchecked()

Similar to :c:func:`PyThreadState_Get`, but don't kill the process with a
Expand All @@ -1247,14 +1246,11 @@ code, or when embedding the Python interpreter:

.. c:function:: PyThreadState* PyThreadState_Swap(PyThreadState *tstate)

Set the :term:`current thread state` to *tstate* (which may be ``NULL``), and return
the old value.
Set the :term:`attached thread state` to *tstate*, and return the
:term:`thread state` that was attached prior to calling.

If there is an :term:`attached thread state` for the current
thread, it will be detached. Upon returning from this function,
*tstate* will become :term:`attached <attached thread state>` instead
(if it's not ``NULL``). If it is ``NULL``, then the :term:`current thread state`
will be ``NULL``.
This function is safe to call without an :term:`attached thread state`; it
will simply return ``NULL`` indicating that there was no prior thread state.


The following functions use thread-local storage, and are not compatible
Expand All @@ -1263,7 +1259,7 @@ with sub-interpreters:
.. c:function:: PyGILState_STATE PyGILState_Ensure()

Ensure that the current thread is ready to call the Python C API regardless
of the current state of Python, or of the :term:`current thread state`. This may
of the current state of Python, or of the :term:`attached thread state`. This may
be called as many times as desired by a thread as long as each call is
matched with a call to :c:func:`PyGILState_Release`. In general, other
thread-related APIs may be used between :c:func:`PyGILState_Ensure` and
Expand Down Expand Up @@ -1305,11 +1301,13 @@ with sub-interpreters:

.. c:function:: PyThreadState* PyGILState_GetThisThreadState()

Get the :term:`current thread state` for this thread. May return ``NULL`` if no
Get the :term:`attached thread state` for this thread. May return ``NULL`` if no
GILState API has been used on the current thread. Note that the main thread
always has such a thread-state, even if no auto-thread-state call has been
made on the main thread. This is mainly a helper/diagnostic function.

.. seealso: PyThreadState_Get


.. c:function:: int PyGILState_Check()

Expand Down Expand Up @@ -1420,10 +1418,11 @@ All of the following functions must be called after :c:func:`Py_Initialize`.

.. c:function:: void PyThreadState_DeleteCurrent(void)

Destroy the :term:`attached thread state` and set the :term:`current thread state`
to ``NULL``. The :term:`thread state <attached thread state>` must have been reset
with a previous call to :c:func:`PyThreadState_Clear`.
Detach the :term:`attached thread state` (which must have been reset
with a previous call to :c:func:`PyThreadState_Clear`) and then destroy it.

No :term:`thread state` will be :term:`attached <attached thread state>` upon
returning.

.. c:function:: PyFrameObject* PyThreadState_GetFrame(PyThreadState *tstate)

Expand Down Expand Up @@ -1564,8 +1563,10 @@ All of the following functions must be called after :c:func:`Py_Initialize`.

.. c:function:: void PyEval_AcquireThread(PyThreadState *tstate)

Set the :term:`current thread state` to *tstate*, which must not be ``NULL`` or
:term:`attached <attached thread state>`.
:term:`Attach <attached thread state>` *tstate* to the current thread,
which must not be ``NULL`` or already :term:`attached <attached thread state>`.

The calling thread must not already have an :term:`attached thread state`.

.. note::
Calling this function from a thread when the runtime is finalizing will
Expand All @@ -1588,7 +1589,7 @@ All of the following functions must be called after :c:func:`Py_Initialize`.

.. c:function:: void PyEval_ReleaseThread(PyThreadState *tstate)

Reset the :term:`current thread state` to ``NULL``.
Detach the :term:`attached thread state`.
The *tstate* argument, which must not be ``NULL``, is only used to check
that it represents the :term:`attached thread state` --- if it isn't, a fatal error is
reported.
Expand Down Expand Up @@ -1830,7 +1831,7 @@ function. You can create and destroy them using the following functions:

Destroy the (sub-)interpreter represented by the given :term:`thread state`.
The given thread state must be :term:`attached <attached thread state>`.
When the call returns, the :term:`current thread state` is ``NULL``.
When the call returns, there will be no :term:`attached thread state`.
All thread states associated with this interpreter are destroyed.

:c:func:`Py_FinalizeEx` will destroy all sub-interpreters that
Expand Down Expand Up @@ -1933,12 +1934,9 @@ pointer and a void pointer argument.
notification recursively, but it can still be interrupted to switch
threads if the :term:`thread state <attached thread state>` is detached.

This function doesn't need a current thread state to run, and it doesn't
need an :term:`attached thread state`.

To call this function in a subinterpreter, the caller must have an
:term:`attached thread state`. Otherwise, the function *func* can be scheduled to
be called from the wrong interpreter.
This function doesn't need an :term:`attached thread state`. However, to call this
function in a subinterpreter, the caller must have an :term:`attached thread state`.
Otherwise, the function *func* can be scheduled to be called from the wrong interpreter.

.. warning::
This is a low-level function, only useful for very special cases.
Expand Down
2 changes: 1 addition & 1 deletion 2 Doc/c-api/reflection.rst
Original file line number Diff line number Diff line change
Expand Up @@ -55,7 +55,7 @@ Reflection

.. c:function:: PyFrameObject* PyEval_GetFrame(void)

Return the current thread state's frame, which is ``NULL`` if no frame is
Return the :term:`attached thread state`'s frame, which is ``NULL`` if no frame is
currently executing.

See also :c:func:`PyThreadState_GetFrame`.
Expand Down
62 changes: 34 additions & 28 deletions 62 Doc/glossary.rst
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

A few points to consider, which are reflected in my other comments:

  • current does not imply attached, but attached does implies current
  • the glossary probably isn't the best place for C-API details
  • "runtime context" or "execution context" might be a clearer term for users than "thread state"

The only tricky part is that we already talk about "contexts" relative to asyncio.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'll commit the suggestions tomorrow when I'm available to do a full find-and-replace so I don't blow up CI in the meantime.

For the per-thread thread state pointer (what a mouthful!), I'm happy with something like "runtime context," because there was no prior term for it, but I think we do want to keep the term "thread state." Thread states are documented, and users like to read the source code to understand some C functions--seeing _PyThreadState_GET and "thread state" in the documentation makes things more clear. That, and "context" doesn't convey the fact that it's a thread-local too well, which is important.

the glossary probably isn't the best place for C-API details

Hmm, we do have "borrowed reference" and "strong reference" in there; where else should "thread state" point to?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

"runtime context" or "execution context" might be a clearer term for users than "thread state"

I like "thread state" :-)

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

+1 for sticking with thread state.

Between contextvars and context managers, and just the general idea of "context of execution", "context" is seriously overloaded already.

"thread state" isn't completely unambiguous (it could theoretically take on the holistic meaning of "all state associated with the current thread, including the execution stack and all thread local variables"), but the context(!) of use should help avoid that misinterpretation.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

"Runtime context" in particular is problematic, since the execution model is set up with the hierarchy of:

  • there is one runtime, which may be serially finalized and reinitialized, but you can never have more than one at a time
  • the runtime will host at least one interpreter, but may host more
  • each interpreter will have at least one thread state, but may have more
  • for an OS thread to execute Python code, it must be associated with a Python thread state (which also indirectly determines which interpreter it is associated with)

Original file line number Diff line number Diff line change
Expand Up @@ -134,18 +134,24 @@ Glossary

attached thread state

A :term:`thread state` that is stored in the :term:`current thread state`.
If no thread state is attached, then the :term:`current thread state` is ``NULL``.
Attempting to call Python's C API without an attached thread state will result
in a fatal error or an undefined behavior.
A :term:`thread state` that is active for the current OS thread.

A thread state can be attached and detached explicitly by the user, or
implicitly by the interpreter in between calls. For example, an attached
thread state is detached upon entering a :c:macro:`Py_BEGIN_ALLOW_THREADS`
block, and then re-attached when :c:macro:`Py_END_ALLOW_THREADS` is reached.
When a :term:`thread state` is attached, the OS thread has
access to the full Python C API and can safely invoke the
bytecode interpreter.

On most builds of Python, having an attached thread state means that the
caller holds the :term:`GIL` for the current interpreter.
Unless a function explicitly notes otherwise, attempting to call
the C API without an attached thread state will result in a fatal
error or undefined behavior. A thread state can be attached and detached
explicitly by the user through the C API, or implicitly by the runtime,
including during blocking C calls and by the bytecode interpreter in between
calls.

On most builds of Python, having an attached context means that the
caller holds the :term:`GIL` for the current interpreter, so only
one OS thread can have an attached thread state at a given moment. In
:term:`free-threaded <free threading>` builds of Python, threads can concurrently
hold an attached thread state, allowing for true parallelism.

attribute
A value associated with an object which is usually referenced by name
Expand Down Expand Up @@ -348,19 +354,6 @@ Glossary
tasks (see :mod:`asyncio`) associate each task with a context which
becomes the current context whenever the task starts or resumes execution.

current thread state

A per-thread :c:data:`PyThreadState` pointer.

The pointer might be ``NULL``, in which case Python code must not
get executed.

If the current thread state is non-``NULL``, then the :term:`thread state`
that it points to is considered to be :term:`attached <attached thread state>`.

The pointer for the calling thread can be acquired via :c:func:`PyThreadState_Get` or
:c:func:`PyThreadState_GetUnchecked`, if it might be ``NULL``.

decorator
A function returning another function, usually applied as a function
transformation using the ``@wrapper`` syntax. Common examples for
Expand Down Expand Up @@ -1314,11 +1307,24 @@ Glossary
:term:`bytes-like objects <bytes-like object>`.

thread state
In Python's C API, a thread state is a structure that holds
information about the current thread, typically in the :term:`current thread state`
pointer. A thread state can be attached or detached. An :term:`attached thread state`
is required to call most of the C API, unless a function explicitly documents
otherwise.

The information used by the :term:`CPython` runtime to run in an OS thread.
For example, this includes the current exception, if any, and the
state of the bytecode interpreter.

Each thread state is bound to a single OS thread, but threads may have
many thread states available. At most, one of them may be
:term:`attached <attached thread state>` at once.

An :term:`attached thread state` is required to call most
of Python's C API, unless a function explicitly documents otherwise.
The bytecode interpreter only runs under an attached thread state.

Each thread state belongs to a single interpreter, but each interpreter
may have many thread states, including multiple for the same OS thread.
Thread states from multiple interpreters may be bound to the same
thread, but only one can be :term:`attached <attached thread state>` in
that thread at any given moment.

See :ref:`Thread State and the Global Interpreter Lock <threads>` for more
information.
Expand Down
Morty Proxy This is a proxified and sanitized view of the page, visit original site.