11PEP: 788
2- Title: Reimagining Native Threads
2+ Title: Interpreter References
33Author: Peter Bierma <zintensitydev@gmail.com>
44Sponsor: Victor Stinner <vstinner@python.org>
55Discussions-To: https://discuss.python.org/t/93653
@@ -32,10 +32,9 @@ inside of subinterpreters, primarily because :c:func:`PyGILState_Ensure`
3232always creates a thread state for the main interpreter in threads where
3333Python 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).
3938This allows for more structure and reliability when it comes to thread state
4039management, because it forces a layer of synchronization between the
4140interpreter 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
5049takes 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-
7851Motivation
7952==========
8053
81- Native Threads Always Hang During Finalization
82- ----------------------------------------------
54+ Non-Python Threads Always Hang During Finalization
55+ --------------------------------------------------
8356
8457Many large libraries might need to call Python code in highly-asynchronous
8558situations 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
11689won't wait on that thread before shutting down. Instead, the interpreter will hang the
11790thread 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
12396the thread is relatively new behavior; in prior versions, the thread would exit,
12497but 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
127100is severely limiting for users who want to do more than just execute Python
128101code in their stream of calls.
129102
@@ -219,7 +192,7 @@ Joining the Thread isn't Always a Good Idea
219192*******************************************
220193
221194Even 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.
223196A thread could be started by some C function, and then as long as
224197that 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.
332305An 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
342315thread-safety issues with thread states.
343316
344317In addition, subinterpreters typically have a much shorter lifetime than the
@@ -563,7 +536,17 @@ Ensuring and Releasing Thread States
563536This proposal includes two new high-level threading APIs that intend to
564537replace :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
811799in the C API today. After calling :c:func: `PyThreadState_Ensure `, simply
812800release 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