Skip to content

Commit b0cc506

Browse files
PEP 788: Minor clarifications (#4391)
1 parent 574655a commit b0cc506

File tree

1 file changed

+32
-26
lines changed

1 file changed

+32
-26
lines changed

peps/pep-0788.rst

Lines changed: 32 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,7 @@ become problematic:
2323
- They aren't safe for finalization, either causing the calling thread to hang or
2424
crashing it with a segmentation fault, preventing further execution.
2525
- When they're called before finalization, they force the thread to be
26-
"daemon", meaning that the interpreter won't wait for it to reach any point
26+
"daemon", meaning that an interpreter won't wait for it to reach any point
2727
of execution. This is mostly frustrating for developers, but can lead to
2828
deadlocks!
2929
- Subinterpreters don't play nicely with them, because they all assume that
@@ -54,12 +54,12 @@ This is achieved by introducing two concepts into the C API:
5454

5555
- "Daemon" and "non-daemon" threads, similar to how it works in the
5656
:mod:`threading` module.
57-
- Interpreter reference counts which prevent the interpreter from finalizing.
57+
- Interpreter reference counts which prevent an interpreter from finalizing.
5858

5959
In :c:func:`PyThreadState_Ensure`, both of these ideas are applied. The
60-
calling thread is to store a reference to the interpreter via
60+
calling thread is to store a reference to an interpreter via
6161
:c:func:`PyInterpreterState_Hold`. :c:func:`PyInterpreterState_Hold`
62-
increases the reference count of the interpreter, requiring the thread
62+
increases the reference count of an interpreter, requiring the thread
6363
to finish (by eventually calling :c:func:`PyThreadState_Release`) before
6464
beginning finalization.
6565

@@ -168,6 +168,8 @@ finalization, because a daemon thread got hung while holding the lock. There
168168
are workarounds for this for pure-Python code, but native threads don't have
169169
such an option.
170170

171+
.. _pep-788-hanging-compat:
172+
171173
We can't change finalization behavior for ``PyGILState_Ensure``
172174
***************************************************************
173175

@@ -186,6 +188,11 @@ error, as noted in `python/cpython#124622 <https://github.com/python/cpython/iss
186188
proceed. The API was designed as "it'll block and only return once it has
187189
the GIL" without any other option.
188190

191+
For this reason, we can't make any real changes to how :c:func:`PyGILState_Ensure`
192+
works for finalization, because it would break existing code. Similarly, threads
193+
created with the existing C API will have to remain daemon, because extensions
194+
that implement native threads aren't guaranteed to work during finalization.
195+
189196
The existing APIs are broken and misleading
190197
-------------------------------------------
191198

@@ -330,7 +337,7 @@ that it always works. An approach that were to return a failure based on
330337
the start-time of the thread could cause spurious issues.
331338

332339
In the case where it is useful to let the interpreter finalize, such as in
333-
a signal handler where there's no guarantee that the thread will start,
340+
an asynchronous callback where there's no guarantee that the thread will start,
334341
strong references to an interpreter can be acquired through
335342
:c:func:`PyInterpreterState_Lookup`.
336343

@@ -348,14 +355,12 @@ way). This generally happens when a thread calls :c:func:`PyEval_RestoreThread`
348355
or in between bytecode instructions, based on :func:`sys.setswitchinterval`.
349356

350357
A new, internal field will be added to the ``PyThreadState`` structure that
351-
determines if the thread is daemon. If the thread is daemon, then it will
352-
hang during attachment as usual, but if it's not, then the interpreter will
353-
let the thread attach and continue execution. On with-GIL builds, this again
354-
means handing off the GIL to the thread. During finalization, the interpreter
358+
determines if the thread is daemon. Before finalization, an interpreter
355359
will wait until all non-daemon threads call :c:func:`PyThreadState_Delete`.
356360

357-
For backwards compatibility, all thread states created by existing APIs will
358-
remain daemon by default.
361+
For backwards compatibility, all thread states created by existing APIs,
362+
including :c:func:`PyGILState_Ensure`, will remain daemon by default.
363+
See :ref:`pep-788-hanging-compat`.
359364

360365
.. c:function:: int PyThreadState_SetDaemon(int is_daemon)
361366
@@ -374,8 +379,8 @@ remain daemon by default.
374379
Interpreter reference counting
375380
------------------------------
376381
377-
Internally, the interpreter will have to keep track of the number of
378-
non-daemon native threads, which will determine when the interpreter can
382+
Internally, an interpreter will have to keep track of the number of
383+
non-daemon native threads, which will determine when an interpreter can
379384
finalize. This is done to prevent use-after-free crashes in
380385
:c:func:`PyThreadState_Ensure` for interpreters with short lifetimes, and
381386
to remove needless layers of synchronization between the calling thread and
@@ -396,15 +401,15 @@ A non-zero reference count prevents the interpreter from finalizing.
396401
This function is generally meant to be used in tandem with
397402
:c:func:`PyThreadState_Ensure`.
398403
399-
The caller must have an :term:`attached thread state`, and cannot return
400-
``NULL``. Failures are always a fatal error.
404+
The caller must have an :term:`attached thread state`. This function
405+
cannot return ``NULL``. Failures are always a fatal error.
401406
402407
.. c:function:: PyInterpreterState *PyInterpreterState_Lookup(int64_t interp_id)
403408
404409
Similar to :c:func:`PyInterpreterState_Hold`, but looks up an interpreter
405410
based on an ID (see :c:func:`PyInterpreterState_GetID`). This has the
406411
benefit of allowing the interpreter to finalize in cases where the thread
407-
might not start, such as inside of a signal handler.
412+
might not start, such as inside of an asynchronous callback.
408413
409414
This function will return ``NULL`` without an exception set on failure.
410415
If the return value is non-``NULL``, then the returned interpreter will be
@@ -438,7 +443,7 @@ replace :c:func:`PyGILState_Ensure` and :c:func:`PyGILState_Release`.
438443
there is a subsequent call to :c:func:`PyThreadState_Release` that matches
439444
this one.
440445
441-
The interpreter's *interp* reference count is decremented by one.
446+
The reference to the interpreter *interp* is stolen by this function.
442447
As such, *interp* should have been acquired by
443448
:c:func:`PyInterpreterState_Hold`.
444449
@@ -454,8 +459,9 @@ replace :c:func:`PyGILState_Ensure` and :c:func:`PyGILState_Release`.
454459
455460
.. c:function:: void PyThreadState_Release()
456461
457-
Detach and destroy the :term:`attached thread state` set by
458-
:c:func:`PyThreadState_Ensure`.
462+
Release the :term:`attached thread state` set by
463+
:c:func:`PyThreadState_Ensure`. Any thread state that was set prior
464+
to the original call to :c:func:`PyThreadState_Ensure` will be restored.
459465
460466
This function cannot fail, but may hang the thread if the
461467
attached thread state prior to the original :c:func:`!PyThreadState_Ensure`
@@ -655,13 +661,13 @@ Asynchronous callback example
655661
*****************************
656662
657663
As stated in the Motivation_, there are many cases where it's desirable
658-
to call Python in an asynchronous callback, such as a signal handler. In that
659-
case, it's not safe to call :c:func:`PyInterpreterState_Hold`, because it's
660-
not guaranteed that :c:func:`PyThreadState_Ensure` will ever be called, which
661-
would deadlock finalization.
664+
to call Python in an asynchronous callback. In such cases, it's not safe to
665+
call :c:func:`PyInterpreterState_Hold`, because it's not guaranteed that
666+
:c:func:`PyThreadState_Ensure` will ever be called.
667+
If not, finalization becomes deadlocked.
662668
663-
This scenario requires :c:func:`PyInterpreterState_Lookup`, which only prevents
664-
finalization when the lookup has been made.
669+
This scenario requires using :c:func:`PyInterpreterState_Lookup` instead,
670+
which only prevents finalization once the lookup has been made.
665671
666672
For example:
667673
@@ -728,7 +734,7 @@ deleted and cause use-after-free violations. :c:func:`PyInterpreterState_Hold`
728734
fixes this issue anyway, but an interpreter ID does have the benefit of
729735
requiring less magic in the implementation, but has several downsides:
730736
731-
- Nearly all existing APIs already return a :c:type:`PyInterpreterState`
737+
- Nearly all existing interpreter APIs already return a :c:type:`PyInterpreterState`
732738
pointer, not an interpreter ID. Functions like
733739
:c:func:`PyThreadState_GetInterpreter` would have to be accompanied by
734740
frustrating calls to :c:func:`PyInterpreterState_GetID`. There's also

0 commit comments

Comments
 (0)