diff options
author | Kyle Stanley <aeros167@gmail.com> | 2020-03-27 19:31:22 (GMT) |
---|---|---|
committer | GitHub <noreply@github.com> | 2020-03-27 19:31:22 (GMT) |
commit | b61b818d916942aad1f8f3e33181801c4a1ed14b (patch) | |
tree | e755041eb620f5b2937639b32c0b98d1afa54c89 /Lib/concurrent | |
parent | 5f9c131c099d6675d1a9d0228497865488afd548 (diff) | |
download | cpython-b61b818d916942aad1f8f3e33181801c4a1ed14b.zip cpython-b61b818d916942aad1f8f3e33181801c4a1ed14b.tar.gz cpython-b61b818d916942aad1f8f3e33181801c4a1ed14b.tar.bz2 |
bpo-39812: Remove daemon threads in concurrent.futures (GH-19149)
Remove daemon threads from :mod:`concurrent.futures` by adding
an internal `threading._register_atexit()`, which calls registered functions
prior to joining all non-daemon threads. This allows for compatibility
with subinterpreters, which don't support daemon threads.
Diffstat (limited to 'Lib/concurrent')
-rw-r--r-- | Lib/concurrent/futures/process.py | 23 | ||||
-rw-r--r-- | Lib/concurrent/futures/thread.py | 20 |
2 files changed, 11 insertions, 32 deletions
diff --git a/Lib/concurrent/futures/process.py b/Lib/concurrent/futures/process.py index 39fadcc..4c39500 100644 --- a/Lib/concurrent/futures/process.py +++ b/Lib/concurrent/futures/process.py @@ -59,19 +59,6 @@ import itertools import sys import traceback -# Workers are created as daemon threads and processes. This is done to allow the -# interpreter to exit when there are still idle processes in a -# ProcessPoolExecutor's process pool (i.e. shutdown() was not called). However, -# allowing workers to die with the interpreter has two undesirable properties: -# - The workers would still be running during interpreter shutdown, -# meaning that they would fail in unpredictable ways. -# - The workers could be killed while evaluating a work item, which could -# be bad if the callable being evaluated has external side-effects e.g. -# writing to a file. -# -# To work around this problem, an exit handler is installed which tells the -# workers to exit when their work queues are empty and then waits until the -# threads/processes finish. _threads_wakeups = weakref.WeakKeyDictionary() _global_shutdown = False @@ -107,6 +94,12 @@ def _python_exit(): for t, _ in items: t.join() +# Register for `_python_exit()` to be called just before joining all +# non-daemon threads. This is used instead of `atexit.register()` for +# compatibility with subinterpreters, which no longer support daemon threads. +# See bpo-39812 for context. +threading._register_atexit(_python_exit) + # Controls how many more calls than processes will be queued in the call queue. # A smaller number will mean that processes spend more time idle waiting for # work while a larger number will make Future.cancel() succeed less frequently @@ -306,9 +299,7 @@ class _ExecutorManagerThread(threading.Thread): # {5: <_WorkItem...>, 6: <_WorkItem...>, ...} self.pending_work_items = executor._pending_work_items - # Set this thread to be daemonized super().__init__() - self.daemon = True def run(self): # Main loop for the executor manager thread. @@ -732,5 +723,3 @@ class ProcessPoolExecutor(_base.Executor): self._executor_manager_thread_wakeup = None shutdown.__doc__ = _base.Executor.shutdown.__doc__ - -atexit.register(_python_exit) diff --git a/Lib/concurrent/futures/thread.py b/Lib/concurrent/futures/thread.py index be79161..2aa4e17 100644 --- a/Lib/concurrent/futures/thread.py +++ b/Lib/concurrent/futures/thread.py @@ -13,19 +13,6 @@ import threading import weakref import os -# Workers are created as daemon threads. This is done to allow the interpreter -# to exit when there are still idle threads in a ThreadPoolExecutor's thread -# pool (i.e. shutdown() was not called). However, allowing workers to die with -# the interpreter has two undesirable properties: -# - The workers would still be running during interpreter shutdown, -# meaning that they would fail in unpredictable ways. -# - The workers could be killed while evaluating a work item, which could -# be bad if the callable being evaluated has external side-effects e.g. -# writing to a file. -# -# To work around this problem, an exit handler is installed which tells the -# workers to exit when their work queues are empty and then waits until the -# threads finish. _threads_queues = weakref.WeakKeyDictionary() _shutdown = False @@ -43,7 +30,11 @@ def _python_exit(): for t, q in items: t.join() -atexit.register(_python_exit) +# Register for `_python_exit()` to be called just before joining all +# non-daemon threads. This is used instead of `atexit.register()` for +# compatibility with subinterpreters, which no longer support daemon threads. +# See bpo-39812 for context. +threading._register_atexit(_python_exit) class _WorkItem(object): @@ -197,7 +188,6 @@ class ThreadPoolExecutor(_base.Executor): self._work_queue, self._initializer, self._initargs)) - t.daemon = True t.start() self._threads.add(t) _threads_queues[t] = self._work_queue |