Skip to content

Commit 17c4299

Browse files
PEP 788: Address feedback from second round (#4478)
* Add a thread reference parameter to PyThreadState_Ensure() and PyThreadState_Release() * Remove usage of the term 'native thread'. * Rename the PEP. * Update the examples to use a PyThreadRef parameter. * Remove references to old title in the abstract. * Use the 'thread state' term rather than a 'PyThreadState' reference. --------- Co-authored-by: Petr Viktorin <[email protected]>
1 parent 6ed819a commit 17c4299

File tree

1 file changed

+49
-58
lines changed

1 file changed

+49
-58
lines changed

peps/pep-0788.rst

Lines changed: 49 additions & 58 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
PEP: 788
2-
Title: Reimagining Native Threads
2+
Title: Interpreter References
33
Author: Peter Bierma <[email protected]>
44
Sponsor: Victor Stinner <[email protected]>
55
Discussions-To: https://discuss.python.org/t/93653
@@ -32,10 +32,9 @@ inside of subinterpreters, primarily because :c:func:`PyGILState_Ensure`
3232
always creates a thread state for the main interpreter in threads where
3333
Python hasn't ever run.
3434

35-
This PEP intends to solve these kinds issues by *reimagining* how we approach
36-
thread states in the C API. This is done through the introduction of interpreter
37-
references that prevent an interpreter from finalizing (or more technically,
38-
entering a stage in which attachment of a thread state hangs).
35+
This PEP intends to solve these kinds issues through the introduction of
36+
interpreter references that prevent an interpreter from finalizing (or more
37+
technically, entering a stage in which attachment of a thread state hangs).
3938
This allows for more structure and reliability when it comes to thread state
4039
management, because it forces a layer of synchronization between the
4140
interpreter and the caller.
@@ -49,37 +48,11 @@ this in CPython is :c:func:`PyGILState_Ensure`. As part of this proposal,
4948
:c:func:`PyThreadState_Ensure` is provided as a modern replacement that
5049
takes a strong interpreter reference.
5150

52-
Terminology
53-
===========
54-
55-
Interpreters
56-
------------
57-
58-
In this proposal, "interpreter" refers to a singular, isolated interpreter
59-
(see :pep:`684`), with its own :c:type:`PyInterpreterState` pointer (referred
60-
to as an "interpreter-state"). "Interpreter" *does not* refer to the entirety
61-
of a Python process.
62-
63-
The "current interpreter" refers to the interpreter-state
64-
pointer on an :term:`attached thread state`, as returned by
65-
:c:func:`PyThreadState_GetInterpreter` or :c:func:`PyInterpreterState_Get`.
66-
67-
Native and Python Threads
68-
-------------------------
69-
70-
This PEP refers to a thread created using the C API as a "native thread",
71-
also sometimes referred to as a "non-Python created thread", where a "Python
72-
created" is a thread created by the :mod:`threading` module.
73-
74-
A native thread is typically registered with the interpreter by
75-
:c:func:`PyGILState_Ensure`, but any thread with an :term:`attached thread state`
76-
qualifies as a native thread.
77-
7851
Motivation
7952
==========
8053

81-
Native Threads Always Hang During Finalization
82-
----------------------------------------------
54+
Non-Python Threads Always Hang During Finalization
55+
--------------------------------------------------
8356

8457
Many large libraries might need to call Python code in highly-asynchronous
8558
situations where the desired interpreter
@@ -111,7 +84,7 @@ Generally, this pattern would look something like this:
11184
/* ... */
11285
}
11386
114-
In the current C API, any "native" thread (one not created via the
87+
In the current C API, any non-Python thread (one not created via the
11588
:mod:`threading` module) is considered to be "daemon", meaning that the interpreter
11689
won't wait on that thread before shutting down. Instead, the interpreter will hang the
11790
thread when it goes to :term:`attach <attached thread state>` a :term:`thread state`,
@@ -123,7 +96,7 @@ interpreter is finalizing isn't enough to safely call Python code. (Note that ha
12396
the thread is relatively new behavior; in prior versions, the thread would exit,
12497
but the issue is the same.)
12598

126-
This means that any non-Python/native thread may be terminated at any point, which
99+
This means that any non-Python thread may be terminated at any point, which
127100
is severely limiting for users who want to do more than just execute Python
128101
code in their stream of calls.
129102

@@ -219,7 +192,7 @@ Joining the Thread isn't Always a Good Idea
219192
*******************************************
220193

221194
Even in daemon threads, it's generally *possible* to prevent hanging of
222-
native threads through :mod:`atexit` functions.
195+
non-Python threads through :mod:`atexit` functions.
223196
A thread could be started by some C function, and then as long as
224197
that thread is joined by :mod:`atexit`, then the thread won't hang.
225198

@@ -332,13 +305,13 @@ at the same time, causing a data race.
332305
An Interpreter Can Concurrently Deallocate
333306
------------------------------------------
334307

335-
The other way of creating a native thread that can invoke Python,
336-
:c:func:`PyThreadState_New` and :c:func:`PyThreadState_Swap`, is a lot better
337-
for supporting subinterpreters (because :c:func:`PyThreadState_New` takes an
338-
explicit interpreter, rather than assuming that the main interpreter was
339-
requested), but is still limited by the current hanging problems in the C API.
340-
Manual creation of thread states ("manual" in contrast to the implicit creation
341-
of one in :c:func:`PyGILState_Ensure`) does not solve any of the aforementioned
308+
The other way of creating a non-Python thread, :c:func:`PyThreadState_New` and
309+
:c:func:`PyThreadState_Swap`, is a lot better for supporting subinterpreters
310+
(because :c:func:`PyThreadState_New` takes an explicit interpreter, rather than
311+
assuming that the main interpreter was requested), but is still limited by the
312+
current hanging problems in the C API. Manual creation of thread states
313+
("manual" in contrast to the implicit creation of one in
314+
:c:func:`PyGILState_Ensure`) does not solve any of the aforementioned
342315
thread-safety issues with thread states.
343316

344317
In addition, subinterpreters typically have a much shorter lifetime than the
@@ -563,7 +536,17 @@ Ensuring and Releasing Thread States
563536
This proposal includes two new high-level threading APIs that intend to
564537
replace :c:func:`PyGILState_Ensure` and :c:func:`PyGILState_Release`.
565538
566-
.. c:function:: int PyThreadState_Ensure(PyInterpreterRef ref)
539+
.. c:type:: PyThreadRef
540+
541+
An opaque reference to a :term:`thread state`.
542+
543+
In the initial implementation, holding a thread reference will
544+
not block finalization of threads or interpreters.
545+
This may change in the future.
546+
547+
This type is guaranteed to be pointer-sized.
548+
549+
.. c:function:: int PyThreadState_Ensure(PyInterpreterRef ref, PyThreadRef *thread)
567550
568551
Ensure that the thread has an :term:`attached thread state` for the
569552
interpreter denoted by *ref*, and thus can safely invoke that
@@ -580,9 +563,12 @@ replace :c:func:`PyGILState_Ensure` and :c:func:`PyGILState_Release`.
580563
if the interpreter matches *ref*, it is attached, and otherwise a new
581564
thread state is created.
582565
583-
Return ``0`` on success, and ``-1`` on failure.
566+
The old thread state is stored as a thread reference in *\*thread*, and is
567+
to be restored by :c:func:`PyThreadState_Release`.
568+
569+
Return ``0`` on success, and ``-1`` without an exception set on failure.
584570
585-
.. c:function:: void PyThreadState_Release()
571+
.. c:function:: void PyThreadState_Release(PyThreadRef ref)
586572
587573
Release a :c:func:`PyThreadState_Ensure` call.
588574
@@ -668,7 +654,8 @@ With this PEP, you'd implement it like this:
668654
return -1;
669655
}
670656
671-
if (PyThreadState_Ensure(ref) < 0) {
657+
PyThreadRef thread_ref;
658+
if (PyThreadState_Ensure(ref, &thread_ref) < 0) {
672659
PyInterpreterRef_Close(ref);
673660
puts("Out of memory.\n", stderr);
674661
return -1;
@@ -679,7 +666,7 @@ With this PEP, you'd implement it like this:
679666
free(to_write);
680667
PyErr_Print();
681668
682-
PyThreadState_Release();
669+
PyThreadState_Release(thread_ref);
683670
PyInterpreterRef_Close(ref);
684671
return res < 0;
685672
}
@@ -770,14 +757,15 @@ This is the same code, rewritten to use the new functions:
770757
thread_func(void *arg)
771758
{
772759
PyInterpreterRef interp = (PyInterpreterRef)arg;
773-
if (PyThreadState_Ensure(interp) < 0) {
760+
PyThreadRef thread_ref;
761+
if (PyThreadState_Ensure(interp, &thread_ref) < 0) {
774762
PyInterpreterRef_Close(interp);
775763
return -1;
776764
}
777765
if (PyRun_SimpleString("print(42)") < 0) {
778766
PyErr_Print();
779767
}
780-
PyThreadState_Release();
768+
PyThreadState_Release(thread_ref);
781769
PyInterpreterRef_Close(interp);
782770
return 0;
783771
}
@@ -807,7 +795,7 @@ This is the same code, rewritten to use the new functions:
807795
Example: A Daemon Thread
808796
************************
809797
810-
With this PEP, daemon threads are very similar to how native threads are used
798+
With this PEP, daemon threads are very similar to how non-Python threads work
811799
in the C API today. After calling :c:func:`PyThreadState_Ensure`, simply
812800
release the interpreter reference, allowing the interpreter to shut down.
813801
@@ -817,7 +805,8 @@ release the interpreter reference, allowing the interpreter to shut down.
817805
thread_func(void *arg)
818806
{
819807
PyInterpreterRef ref = (PyInterpreterRef)arg;
820-
if (PyThreadState_Ensure(ref) < 0) {
808+
PyThreadRef thread_ref;
809+
if (PyThreadState_Ensure(ref, &thread_ref) < 0) {
821810
PyInterpreterRef_Close(ref);
822811
return -1;
823812
}
@@ -827,7 +816,7 @@ release the interpreter reference, allowing the interpreter to shut down.
827816
if (PyRun_SimpleString("print(42)") < 0) {
828817
PyErr_Print();
829818
}
830-
PyThreadState_Release();
819+
PyThreadState_Release(thread_ref);
831820
return 0;
832821
}
833822
@@ -873,14 +862,15 @@ deadlock the interpreter if it's not released.
873862
return -1;
874863
}
875864
876-
if (PyThreadState_Ensure(ref) < 0) {
865+
PyThreadRef thread_ref;
866+
if (PyThreadState_Ensure(ref, &thread_ref) < 0) {
877867
PyInterpreterRef_Close(ref);
878868
return -1;
879869
}
880870
if (PyRun_SimpleString("print(42)") < 0) {
881871
PyErr_Print();
882872
}
883-
PyThreadState_Release();
873+
PyThreadState_Release(thread_ref);
884874
PyInterpreterRef_Close(ref);
885875
return 0;
886876
}
@@ -931,14 +921,15 @@ interpreter here.
931921
return;
932922
}
933923
934-
if (PyThreadState_Ensure(ref) < 0) {
924+
PyThreadRef thread_ref;
925+
if (PyThreadState_Ensure(ref, &thread_ref) < 0) {
935926
PyInterpreterRef_Close(ref);
936927
return -1;
937928
}
938929
if (PyRun_SimpleString("print(42)") < 0) {
939930
PyErr_Print();
940931
}
941-
PyThreadState_Release();
932+
PyThreadState_Release(thread_ref);
942933
PyInterpreterRef_Close(ref);
943934
return 0;
944935
}
@@ -1015,7 +1006,7 @@ of requiring less magic:
10151006
on 32-bit systems, where ``void *`` is too small for an ``int64_t``.
10161007
- To retain usability, interpreter ID APIs would still need to keep a
10171008
reference count, otherwise the interpreter could be finalizing before
1018-
the native thread gets a chance to attach. The problem with using an
1009+
the non-Python thread gets a chance to attach. The problem with using an
10191010
interpreter ID is that the reference count has to be "invisible"; it
10201011
must be tracked elsewhere in the interpreter, likely being *more*
10211012
complex than :c:func:`PyInterpreterRef_Get`. There's also a lack

0 commit comments

Comments
 (0)