@@ -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
6259class 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