1
1
PEP: 788
2
- Title: Reimagining Native Threads
2
+ Title: Interpreter References
3
3
Author: Peter Bierma <
[email protected] >
4
4
Sponsor: Victor Stinner <
[email protected] >
5
5
Discussions-To: https://discuss.python.org/t/93653
@@ -32,10 +32,9 @@ inside of subinterpreters, primarily because :c:func:`PyGILState_Ensure`
32
32
always creates a thread state for the main interpreter in threads where
33
33
Python hasn't ever run.
34
34
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).
39
38
This allows for more structure and reliability when it comes to thread state
40
39
management, because it forces a layer of synchronization between the
41
40
interpreter and the caller.
@@ -49,37 +48,11 @@ this in CPython is :c:func:`PyGILState_Ensure`. As part of this proposal,
49
48
:c:func: `PyThreadState_Ensure ` is provided as a modern replacement that
50
49
takes a strong interpreter reference.
51
50
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
-
78
51
Motivation
79
52
==========
80
53
81
- Native Threads Always Hang During Finalization
82
- ----------------------------------------------
54
+ Non-Python Threads Always Hang During Finalization
55
+ --------------------------------------------------
83
56
84
57
Many large libraries might need to call Python code in highly-asynchronous
85
58
situations where the desired interpreter
@@ -111,7 +84,7 @@ Generally, this pattern would look something like this:
111
84
/* ... */
112
85
}
113
86
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
115
88
:mod: `threading ` module) is considered to be "daemon", meaning that the interpreter
116
89
won't wait on that thread before shutting down. Instead, the interpreter will hang the
117
90
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
123
96
the thread is relatively new behavior; in prior versions, the thread would exit,
124
97
but the issue is the same.)
125
98
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
127
100
is severely limiting for users who want to do more than just execute Python
128
101
code in their stream of calls.
129
102
@@ -219,7 +192,7 @@ Joining the Thread isn't Always a Good Idea
219
192
*******************************************
220
193
221
194
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.
223
196
A thread could be started by some C function, and then as long as
224
197
that thread is joined by :mod: `atexit `, then the thread won't hang.
225
198
@@ -332,13 +305,13 @@ at the same time, causing a data race.
332
305
An Interpreter Can Concurrently Deallocate
333
306
------------------------------------------
334
307
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
342
315
thread-safety issues with thread states.
343
316
344
317
In addition, subinterpreters typically have a much shorter lifetime than the
@@ -563,7 +536,17 @@ Ensuring and Releasing Thread States
563
536
This proposal includes two new high-level threading APIs that intend to
564
537
replace :c:func:`PyGILState_Ensure` and :c:func:`PyGILState_Release`.
565
538
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)
567
550
568
551
Ensure that the thread has an :term: `attached thread state ` for the
569
552
interpreter denoted by *ref *, and thus can safely invoke that
@@ -580,9 +563,12 @@ replace :c:func:`PyGILState_Ensure` and :c:func:`PyGILState_Release`.
580
563
if the interpreter matches *ref *, it is attached, and otherwise a new
581
564
thread state is created.
582
565
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.
584
570
585
- .. c :function :: void PyThreadState_Release ()
571
+ .. c :function :: void PyThreadState_Release (PyThreadRef ref )
586
572
587
573
Release a :c:func:`PyThreadState_Ensure` call.
588
574
@@ -668,7 +654,8 @@ With this PEP, you'd implement it like this:
668
654
return -1;
669
655
}
670
656
671
- if (PyThreadState_Ensure(ref) < 0) {
657
+ PyThreadRef thread_ref;
658
+ if (PyThreadState_Ensure(ref, &thread_ref) < 0) {
672
659
PyInterpreterRef_Close (ref);
673
660
puts ("Out of memory.\n ", stderr);
674
661
return -1;
@@ -679,7 +666,7 @@ With this PEP, you'd implement it like this:
679
666
free (to_write);
680
667
PyErr_Print ();
681
668
682
- PyThreadState_Release ();
669
+ PyThreadState_Release (thread_ref );
683
670
PyInterpreterRef_Close (ref);
684
671
return res < 0 ;
685
672
}
@@ -770,14 +757,15 @@ This is the same code, rewritten to use the new functions:
770
757
thread_func(void *arg)
771
758
{
772
759
PyInterpreterRef interp = (PyInterpreterRef)arg;
773
- if (PyThreadState_Ensure(interp) < 0) {
760
+ PyThreadRef thread_ref;
761
+ if (PyThreadState_Ensure(interp, &thread_ref) < 0) {
774
762
PyInterpreterRef_Close(interp);
775
763
return -1;
776
764
}
777
765
if (PyRun_SimpleString("print(42)") < 0) {
778
766
PyErr_Print();
779
767
}
780
- PyThreadState_Release();
768
+ PyThreadState_Release(thread_ref );
781
769
PyInterpreterRef_Close(interp);
782
770
return 0;
783
771
}
@@ -807,7 +795,7 @@ This is the same code, rewritten to use the new functions:
807
795
Example: A Daemon Thread
808
796
************************
809
797
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
811
799
in the C API today. After calling :c:func: `PyThreadState_Ensure `, simply
812
800
release the interpreter reference, allowing the interpreter to shut down.
813
801
@@ -817,7 +805,8 @@ release the interpreter reference, allowing the interpreter to shut down.
817
805
thread_func(void *arg)
818
806
{
819
807
PyInterpreterRef ref = (PyInterpreterRef)arg;
820
- if (PyThreadState_Ensure(ref) < 0) {
808
+ PyThreadRef thread_ref;
809
+ if (PyThreadState_Ensure(ref, &thread_ref) < 0) {
821
810
PyInterpreterRef_Close(ref);
822
811
return -1;
823
812
}
@@ -827,7 +816,7 @@ release the interpreter reference, allowing the interpreter to shut down.
827
816
if (PyRun_SimpleString("print(42)") < 0) {
828
817
PyErr_Print();
829
818
}
830
- PyThreadState_Release();
819
+ PyThreadState_Release(thread_ref );
831
820
return 0;
832
821
}
833
822
@@ -873,14 +862,15 @@ deadlock the interpreter if it's not released.
873
862
return -1;
874
863
}
875
864
876
- if (PyThreadState_Ensure(ref) < 0) {
865
+ PyThreadRef thread_ref;
866
+ if (PyThreadState_Ensure(ref, &thread_ref) < 0) {
877
867
PyInterpreterRef_Close(ref);
878
868
return -1;
879
869
}
880
870
if (PyRun_SimpleString("print(42)") < 0) {
881
871
PyErr_Print();
882
872
}
883
- PyThreadState_Release();
873
+ PyThreadState_Release(thread_ref );
884
874
PyInterpreterRef_Close(ref);
885
875
return 0;
886
876
}
@@ -931,14 +921,15 @@ interpreter here.
931
921
return;
932
922
}
933
923
934
- if (PyThreadState_Ensure (ref) < 0) {
924
+ PyThreadRef thread_ref;
925
+ if (PyThreadState_Ensure (ref, &thread_ref) < 0) {
935
926
PyInterpreterRef_Close (ref);
936
927
return -1;
937
928
}
938
929
if (PyRun_SimpleString ("print (42)") < 0) {
939
930
PyErr_Print ();
940
931
}
941
- PyThreadState_Release ();
932
+ PyThreadState_Release (thread_ref );
942
933
PyInterpreterRef_Close (ref);
943
934
return 0;
944
935
}
@@ -1015,7 +1006,7 @@ of requiring less magic:
1015
1006
on 32-bit systems, where ``void *`` is too small for an ``int64_t``.
1016
1007
- To retain usability, interpreter ID APIs would still need to keep a
1017
1008
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
1019
1010
interpreter ID is that the reference count has to be "invisible"; it
1020
1011
must be tracked elsewhere in the interpreter, likely being *more *
1021
1012
complex than :c:func: `PyInterpreterRef_Get `. There's also a lack
0 commit comments