Skip to content
Merged
Changes from 2 commits
Commits
Show all changes
55 commits
Select commit Hold shift + click to select a range
64c88b3
Clarify what 'native thread' means.
ZeroIntensity May 1, 2025
bda3db1
Add a section clarifying finalization and change up some wording.
ZeroIntensity May 1, 2025
a57686c
Rewrite the abstract.
ZeroIntensity May 3, 2025
3387f81
A bunch of changes to the motivation and rationale.
ZeroIntensity May 3, 2025
ceeefea
Add PyThreadState_GetDaemon() and reword the deprecation rationale.
ZeroIntensity May 3, 2025
3cbfb26
Rewrite the entire damn specification.
ZeroIntensity May 3, 2025
d9de49a
Update the rejected ideas.
ZeroIntensity May 3, 2025
c742d93
Fix some outdated references.
ZeroIntensity May 3, 2025
ad1bf7f
Fix typo in rejected ideas.
ZeroIntensity May 4, 2025
bca6131
Adjust threading section.
ZeroIntensity May 4, 2025
868cdef
Specify that PyInterpreterRef is pointer-sized
ZeroIntensity May 4, 2025
6b3a447
Add clarity to reference counting.
ZeroIntensity May 4, 2025
f5e1af8
Fix typo in example.
ZeroIntensity May 4, 2025
98e7fcc
Formalize the headings.
ZeroIntensity May 4, 2025
95916a7
Add a terminology section.
ZeroIntensity May 4, 2025
257a252
Add PyInterpreterState_AsStrong()
ZeroIntensity May 4, 2025
6b9b74e
Add an example for PyInterpreterState_AsStrong()
ZeroIntensity May 4, 2025
48624ef
An editorial pass.
ZeroIntensity May 4, 2025
31d3f75
Fix typo in example.
ZeroIntensity May 4, 2025
8440057
Some clarifications and a new example.
ZeroIntensity May 5, 2025
9b08bf0
Fix wording.
ZeroIntensity May 5, 2025
0e5acc8
Update peps/pep-0788.rst
ZeroIntensity May 9, 2025
6d96645
Update peps/pep-0788.rst
ZeroIntensity May 9, 2025
a229f7b
Apply suggestions from code review
ZeroIntensity May 9, 2025
f8b0112
Merge branch 'pep-788-round-1' of https://github.com/ZeroIntensity/pe…
ZeroIntensity May 9, 2025
2332d3e
Fix typos.
ZeroIntensity May 9, 2025
d5630af
Use non-pointers for PyInterpreterRef
ZeroIntensity May 10, 2025
86b4b79
Change the API for PyInterpreterState_AsStrong() and PyInterpreterWea…
ZeroIntensity May 12, 2025
3212a61
Don't specify setting `NULL`
ZeroIntensity May 12, 2025
6e3550c
infinitely -> unbounded
ZeroIntensity May 13, 2025
6f45d71
Reword 'extremely common'.
ZeroIntensity May 13, 2025
1d41eb6
Use 'callback parameter' instead of 'closure'.
ZeroIntensity May 13, 2025
2a75bfd
Don't steal a reference in PyThreadState_Ensure().
ZeroIntensity May 18, 2025
1e6285f
Remove the rest of reference theft.
ZeroIntensity May 18, 2025
bcc1c73
Remove 'daemon'-ness as a property of threads.
ZeroIntensity May 18, 2025
57abedb
'removing' -> 'deprecating'
ZeroIntensity May 19, 2025
e2145b5
Some final updates in response to the reference implementation.
ZeroIntensity May 22, 2025
e547d05
Remove some redundant links.
ZeroIntensity May 22, 2025
dd6e2d1
Remove distinction between finalization and shutdown.
ZeroIntensity May 22, 2025
332394c
Shorten lock + daemon thread section in the motivation.
ZeroIntensity May 22, 2025
6e09820
Redo the abstract.
ZeroIntensity May 22, 2025
12344a9
Add the solution to the abstract.
ZeroIntensity May 23, 2025
45a846c
Fix lint.
ZeroIntensity May 23, 2025
d2a257a
Add a rejected idea for non-daemon thread states.
ZeroIntensity May 23, 2025
a3cf5f4
Redo some of the motivation.
ZeroIntensity May 23, 2025
81dd8d3
Fix lint.
ZeroIntensity May 23, 2025
2aad8fe
Update peps/pep-0788.rst
ZeroIntensity May 23, 2025
232208c
Fix typo.
ZeroIntensity May 24, 2025
558ed81
Fix misleading sentence.
ZeroIntensity May 24, 2025
b6e9e02
Simplify phrasing.
ZeroIntensity May 24, 2025
b0898a5
Add a comment.
ZeroIntensity May 24, 2025
48b408b
Some tidying up.
ZeroIntensity May 28, 2025
0c8042e
Change up a title.
ZeroIntensity May 28, 2025
ec1c5cc
Avoid the _ptr suffix.
ZeroIntensity May 28, 2025
977188c
Fix memory leak.
ZeroIntensity May 28, 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
77 changes: 50 additions & 27 deletions peps/pep-0788.rst
Original file line number Diff line number Diff line change
Expand Up @@ -333,8 +333,8 @@ object are wrong! There isn't any synchronization between the two GILs, so both
the thread (who thinks it's in the subinterpreter) and the main thread could try
to increment the reference count at the same time, causing a data race!

Concurrent Interpreter Deallocation is Frustrating
--------------------------------------------------
An Interpreter Can Concurrently Deallocate
------------------------------------------

The other way of creating a native thread that can invoke Python,
:c:func:`PyThreadState_New` and :c:func:`PyThreadState_Swap`, is a lot better
Expand Down Expand Up @@ -431,25 +431,32 @@ Strong Interpreter References

This type is guaranteed to be pointer-sized.

.. c:function:: PyInterpreterRef PyInterpreterRef_Get(void)
.. c:function:: int PyInterpreterRef_Get(PyInterpreterRef *ref_ptr)
Comment thread
vstinner marked this conversation as resolved.
Outdated

Acquire a strong reference to the current interpreter.

This function cannot fail, other than with a fatal error when the caller
doesn't hold an :term:`attached thread state`.
On success, this function returns ``0`` and sets *ref_ptr*
to a strong reference to the interpreter, and returns ``-1``
with an exception set on failure.

Failure typically indicates that the interpreter has
already finished waiting on strong references.

The caller must hold an :term:`attached thread state`.

.. c:function:: int PyInterpreterState_AsStrong(PyInterpreterState *interp, PyInterpreterRef *ref_ptr)
.. c:function:: int PyInterpreterRef_Main(PyInterpreterRef *ref_ptr)
Comment thread
vstinner marked this conversation as resolved.
Outdated

Acquire a strong reference to *interp*.
Acquire a strong reference to the main interpreter.

Unless *interp* is the main interpreter, this function can cause crashes
if *interp* shuts down in another thread! Prefer safely acquiring a
reference through :c:func:`PyInterpreterRef_Get` whenever possible.
This function only exists for special cases where a specific interpreter
can't be saved. Prefer safely acquiring a reference through
:c:func:`PyInterpreterRef_Get` whenever possible.

On success, this function will return ``0`` and set *ref_ptr* to a strong
reference, and on failure, this function will return ``-1``.
(Failure typically indicates that *interp* has already finished
waiting on its reference count.)

Failure typically indicates that the main interpreter has already finished
waiting on its reference count.

The caller does not need to hold an :term:`attached thread state`.

Expand Down Expand Up @@ -484,21 +491,22 @@ Weak Interpreter References
The interpreter will *not* wait for the reference to be
released before shutting down.

.. c:function:: PyInterpreterWeakRef PyInterpreterWeakRef_Get(void)
.. c:function:: int PyInterpreterWeakRef_Get(PyInterpreterWeakRef *wref_ptr)

Acquire a weak reference to the current interpreter.

This function is generally meant to be used in tandem with
:c:func:`PyInterpreterWeakRef_AsStrong`, and cannot fail.
:c:func:`PyInterpreterWeakRef_AsStrong`.

On success, this function returns ``0`` and sets *wref_ptr* to a
weak reference to the interpreter, and returns ``-1`` with an exception
set on failure.

The caller must hold an :term:`attached thread state`.

.. c:function:: PyInterpreterWeakRef PyInterpreterWeakRef_Dup(PyInterpreterWeakRef wref)
Comment thread
vstinner marked this conversation as resolved.

Duplicate a weak reference to *wref*.

This function is generally meant to be used in tandem with
:c:func:`PyInterpreterWeakRef_AsStrong`.
Duplicate a weak reference to an interpreter.

This function cannot fail, and the caller doesn't need to hold an
:term:`attached thread state`.
Expand All @@ -519,7 +527,7 @@ Weak Interpreter References

.. c:function:: void PyInterpreterWeakRef_Close(PyInterpreterWeakRef wref)

Release a weak reference, possibly deallocating it.
Release a weak reference to an interpreter.

This function cannot fail, and the caller doesn't need to hold an
:term:`attached thread state`.
Expand Down Expand Up @@ -547,7 +555,7 @@ replace :c:func:`PyGILState_Ensure` and :c:func:`PyGILState_Release`.
if the interpreter matches *ref*, it is attached, and otherwise a new
thread state is created.

Return zero on success, and non-zero on failure.
Return ``0`` on success, and ``-1`` on failure.

.. c:function:: void PyThreadState_Release()

Expand Down Expand Up @@ -620,7 +628,7 @@ With this PEP, you'd implement it like this:
{
PyInterpreterRef ref;
if (PyInterpreterWeakRef_AsStrong(wref, &ref) < 0) {
// Python interpreter has shut down
/* Python interpreter has shut down */
return -1;
}

Expand Down Expand Up @@ -662,7 +670,11 @@ held. Any future finalizer that wanted to acquire the lock would be deadlocked!
my_critical_operation(PyObject *self, PyObject *unused)
{
assert(PyThreadState_GetUnchecked() != NULL);
PyInterpreterRef ref = PyInterpreterRef_Get();
PyInterpreterRef ref;
if (PyInterpreterRef_Get(&ref) < 0) {
/* Python interpreter has shut down */
return NULL;
}
/* Temporarily hold a strong reference to ensure that the
lock is released. */
if (PyThreadState_Ensure(ref) < 0) {
Expand Down Expand Up @@ -748,7 +760,11 @@ This is the same code, rewritten to use the new functions:
PyThread_handle_t handle;
PyThead_indent_t indent;

PyInterpreterRef ref = PyInterpreterRef_Get();
PyInterpreterRef ref;
if (PyInterpreterRef_Get(&ref) < 0) {
return NULL;
}

if (PyThread_start_joinable_thread(thread_func, (void *)ref, &ident, &handle) < 0) {
PyInterpreterRef_Close(ref);
return NULL;
Expand Down Expand Up @@ -792,7 +808,11 @@ they can still be used with this API:
PyThread_handle_t handle;
PyThead_indent_t indent;

PyInterpreterRef ref = PyInterpreterRef_Get();
PyInterpreterRef ref;
if (PyInterpreterRef_Get(&ref) < 0) {
return NULL;
}

if (PyThread_start_joinable_thread(thread_func, (void *)ref, &ident, &handle) < 0) {
PyInterpreterRef_Close(ref);
return NULL;
Expand Down Expand Up @@ -846,7 +866,10 @@ deadlock the interpreter if it's not released.
PyErr_NoMemory();
return NULL;
}
PyInterpreterWeakRef wref = PyInterpreterWeakRef_Get();
PyInterpreterWeakRef wref;
if (PyInterpreterWeakRef_Get(&wref) < 0) {
Comment thread
vstinner marked this conversation as resolved.
return NULL;
}
tdata->wref = wref;
register_callback(async_callback, tdata);

Expand All @@ -859,7 +882,7 @@ Example: Calling Python Without a Callback Parameter
There are a few cases where callback functions don't take a callback parameter
(``void *arg``), so it's impossible to acquire a reference to any specific
interpreter. The solution to this problem is to acquire a reference to the main
interpreter through :c:func:`PyInterpreterState_AsStrong`.
interpreter through :c:func:`PyInterpreterRef_Main`.

But wait, won't that break with subinterpreters, per
:ref:`pep-788-subinterpreters-gilstate`? Fortunately, since the callback has
Expand All @@ -873,7 +896,7 @@ interpreter here.
call_python(void)
{
PyInterpreterRef ref;
if (PyInterpreterState_AsStrong(PyInterpreterState_Main(), &ref) < 0) {
if (PyInterpreterRef_Main(&ref) < 0) {
fputs("Python has shut down!", stderr);
return;
}
Expand Down