Skip to content

Commit a3cf5f4

Browse files
committed
Redo some of the motivation.
1 parent d2a257a commit a3cf5f4

1 file changed

Lines changed: 83 additions & 36 deletions

File tree

peps/pep-0788.rst

Lines changed: 83 additions & 36 deletions
Original file line numberDiff line numberDiff line change
@@ -16,13 +16,13 @@ Abstract
1616

1717
In the C API, threads are able to interact with an interpreter by holding an
1818
:term:`attached thread state` for the current thread. This works well, but
19-
can get complicated when it comes to creating and Attaching
19+
can get complicated when it comes to creating and attaching
2020
:term:`thread states <thread state>` in a thread-safe manner.
2121

2222
Specifically, the C API doesn't have any way to ensure that an interpreter
2323
is in a state where it can be called when creating and/or attaching a thread
24-
state. As such, attachment might hang the thread, or in subinterpreters, it
25-
might flat-out crash due to the interpreter's structure being deallocated.
24+
state. As such, attachment might hang the thread, or it might flat-out crash
25+
due to the interpreter's structure being deallocated in subinterpreters.
2626
This can be a frustrating issue to deal with in large applications that
2727
want to execute Python code alongside some other native code.
2828

@@ -33,14 +33,20 @@ Python hasn't ever run.
3333

3434
This PEP intends to solve these kinds issues by *reimagining* how we approach
3535
thread states in the C API. This is done through the introduction of interpreter
36-
references that prevent an interpreter from entering a stage where threads will
37-
hang, as well as :c:func:`PyThreadState_Ensure` and :c:func:`PyThreadState_Release`
38-
for replacing :c:func:`PyGILState_Ensure` and :c:func:`PyGILState_Release`.
39-
40-
For example, in APIs that don't require the caller to hold an attached thread
41-
state, a strong interpreter reference should be passed to the API to ensure
42-
that it targets the correct interpreter, and that the interpreter doesn't
43-
concurrently deallocate itself.
36+
references that prevent an interpreter from finalizing (or more technically,
37+
entering a stage in which attachment of a thread state hangs).
38+
This allows for more structure and reliability when it comes to thread state
39+
management, because it forces a layer of synchronization between the
40+
interpreter and the caller.
41+
42+
With this new system, there are a lot of changes needed in CPython and
43+
third-party libraries to adopt it. For example, in APIs that don't require
44+
the caller to hold an attached thread state, a strong interpreter reference
45+
should be passed to ensure that it targets the correct interpreter, and that
46+
the interpreter doesn't concurrently deallocate itself. The best example of
47+
this in CPython is :c:func:`PyGILState_Ensure`. As part of this proposal,
48+
:c:func:`PyThreadState_Ensure` is provided as a modern replacement that
49+
takes a strong interpreter reference.
4450

4551
Terminology
4652
===========
@@ -120,25 +126,6 @@ This means that any non-Python/native thread may be terminated at any point, whi
120126
is severely limiting for users who want to do more than just execute Python
121127
code in their stream of calls.
122128

123-
Joining the Thread isn't Always Possible
124-
****************************************
125-
126-
In general, it's possible to prevent hanging of threads created while Python
127-
is active through :mod:`atexit` functions. A thread could be started by some
128-
C function, and then as long as that thread is joined by :mod:`atexit`, then
129-
the thread won't hang. Reasonable enough, right?
130-
131-
Unfortunately, :mod:`atexit` isn't always an option, because to call it, you
132-
need to already have an :term:`attached thread state` for the thread. If
133-
there's no guarantee of that, then :func:`atexit.register` cannot be safely
134-
called without the risk of hanging the thread.
135-
136-
For example, large C++ applications might want to expose an interface that can
137-
call Python code. To do this, a function would take a Python object, and then
138-
call :c:func:`PyGILState_Ensure` to safely interact with it (e.g., by calling
139-
it). If the interpreter is finalizing or has shut down, then the thread is
140-
hung, disrupting the C++ caller.
141-
142129
``Py_IsFinalizing`` is Insufficient
143130
***********************************
144131

@@ -176,6 +163,66 @@ to fix it with the current API. For example,
176163
remarks that the :mod:`ssl` module can emit a fatal error when used at
177164
finalization, because a daemon thread got hung while holding the lock.
178165

166+
167+
Daemon Threads are not the Problem
168+
**********************************
169+
170+
Prior to this PEP, deprecating daemon threads was discussed
171+
`extensively <https://discuss.python.org/t/68836>`_. Daemon threads technically
172+
cause many of the issues outlined in this proposal, so removing daemon threads
173+
could be seen as a potential solution. The main argument for removing daemon
174+
threads is that they're a large cause of problems in the interpreter:
175+
176+
Except that daemon threads don’t actually work reliably. They’re attempting
177+
to run and use Python interpreter resources after the runtime has been shut
178+
down upon runtime finalization. As in they have pointers to global state for
179+
the interpreter.
180+
181+
In practice, daemon threads are useful for simplifying many threading applications
182+
in Python, and since the program is about to close in most cases, it's not worth
183+
the added complexity to try and gracefully shut down a thread.
184+
185+
When I’ve needed daemon threads, it’s usually been the case of “Long-running,
186+
uninterruptible, third-party task” in terms of the examples in the linked issue.
187+
Basically I’ve had something that I need running in the background, but I have
188+
no easy way to terminate it short of process termination. Unfortunately, I’m on
189+
Windows, so ``signal.pthread_kill`` isn’t an option. I guess I could use the
190+
Windows Terminate Thread API, but it’s a lot of work to wrap it myself compared
191+
to just letting process termination handle things.
192+
193+
Finally, removing Python-level daemon threads does not fix the whole problem.
194+
As noted by this PEP, extension modules are free to create their own threads
195+
and attach thread states for them. Similar to daemon threads, Python doesn't
196+
try and join them during finalization, so trying to remove daemon threads
197+
as a whole would involve trying to remove them from the C API, which would
198+
require a massive API change.
199+
200+
Realize however that even if we get rid of daemon threads, extension
201+
module code can and does spawn its own threads that are not tracked by
202+
Python. ... Those are realistically an alternate form of daemon thread
203+
... and those are never going to be forbidden.
204+
205+
Joining the Thread isn't Always a Good Idea
206+
*******************************************
207+
208+
Even in daemon threads, it's generally *possible* to prevent hanging of
209+
native threads through :mod:`atexit` functions.
210+
A thread could be started by some C function, and then as long as
211+
that thread is joined by :mod:`atexit`, then the thread won't hang.
212+
213+
:mod:`atexit` isn't always an option for a function, because to call it, it
214+
needs to already have an :term:`attached thread state` for the thread. If
215+
there's no guarantee of that, then :func:`atexit.register` cannot be safely
216+
called without the risk of hanging the thread. This shifts the contract
217+
of joining the thread to the caller rather than the callee, which again,
218+
isn't done in practice.
219+
220+
For example, large C++ applications might want to expose an interface that can
221+
call Python code. To do this, a C++ API would take a Python object, and then
222+
call :c:func:`PyGILState_Ensure` to safely interact with it (for example, by
223+
calling it). If the interpreter is finalizing or has shut down, then the thread
224+
is hung, disrupting the C++ stream of calls.
225+
179226
.. _pep-788-hanging-compat:
180227

181228
Finalization Behavior for ``PyGILState_Ensure`` Cannot Change
@@ -312,10 +359,9 @@ Preventing Interpreter Shutdown with Reference Counting
312359
-------------------------------------------------------
313360

314361
This PEP takes an approach where an interpreter is given a reference count
315-
that prevents it from shutting down.
316-
317-
So, holding a "strong reference" to the interpreter will make it safe to
318-
call the C API without worrying about the thread being hung.
362+
that prevents it from shutting down. So, holding a "strong reference" to the
363+
interpreter will make it safe to call the C API without worrying about the
364+
thread being hung.
319365

320366
This means that interfacing Python (for example, in a C++ library) will need
321367
a reference to the interpreter in order to safely call the object, which is
@@ -361,8 +407,9 @@ Interpreter References to Prevent Shutdown
361407

362408
An interpreter will keep a reference count that's managed by users of the
363409
C API. When the interpreter starts finalizing, it will until its reference count
364-
reaches zero before proceeding to a point where threads will be hung.
365-
Note that this *is not* the same as joining the thread; the interpreter will
410+
reaches zero before proceeding to a point where threads will be hung. This will
411+
happen around the same time when :class:`threading.Thread` objects are joined,
412+
but ote that this *is not* the same as joining the thread; the interpreter will
366413
only wait until the reference count is zero, and then proceed. The interpreter
367414
must not hang threads until this reference count has reached zero.
368415
After the reference count has reached zero, threads can no longer prevent the

0 commit comments

Comments
 (0)