Skip to content

Commit 8c76be9

Browse files
committed
asyncio._selector_thread: avoid daemon thread
rely on private threading._register_atexit to run cleanup prior to thread join
1 parent a5fa125 commit 8c76be9

File tree

1 file changed

+10
-13
lines changed

1 file changed

+10
-13
lines changed

Lib/asyncio/_selector_thread.py

Lines changed: 10 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -47,16 +47,13 @@ def _atexit_callback() -> None:
4747
loop._waker_w.send(b"a")
4848
except BlockingIOError:
4949
pass
50-
if loop._thread is not None:
51-
# If we don't join our (daemon) thread here, we may get a deadlock
52-
# during interpreter shutdown. I don't really understand why. This
53-
# deadlock happens every time in CI (both travis and appveyor) but
54-
# I've never been able to reproduce locally.
55-
loop._thread.join()
5650
_selector_loops.clear()
5751

5852

59-
atexit.register(_atexit_callback)
53+
# use internal _register_atexit to avoid need for daemon threads
54+
# I can't find a public API for equivalent functionality
55+
# to run something prior to thread join during process teardown
56+
threading._register_atexit(_atexit_callback)
6057

6158

6259
class SelectorThread:
@@ -120,18 +117,18 @@ def close(self) -> None:
120117
async def _thread_manager(self) -> typing.AsyncGenerator[None, None]:
121118
# Create a thread to run the select system call. We manage this thread
122119
# manually so we can trigger a clean shutdown from an atexit hook. Note
123-
# that due to the order of operations at shutdown, only daemon threads
124-
# can be shut down in this way (non-daemon threads would require the
125-
# introduction of a new hook: https://bugs.python.org/issue41962)
120+
# that due to the order of operations at shutdown,
121+
# we rely on private `threading._register_atexit`
122+
# to wake the thread before joining to avoid hangs.
123+
# See https://github.com/python/cpython/issues/86128 for more info
126124
self._thread = threading.Thread(
127-
name="Asyncio selector",
128-
daemon=True,
125+
name="asyncio selector",
129126
target=self._run_select,
130127
)
131128
self._thread.start()
132129
self._start_select()
133130
try:
134-
# The presense of this yield statement means that this coroutine
131+
# The presence of this yield statement means that this coroutine
135132
# is actually an asynchronous generator, which has a special
136133
# shutdown protocol. We wait at this yield point until the
137134
# event loop's shutdown_asyncgens method is called, at which point

0 commit comments

Comments
 (0)