From: Ben Darnell Date: Tue, 3 Oct 2023 01:39:39 +0000 (-0400) Subject: Revert "asyncio: Remove atexit hook" X-Git-Tag: v6.4.0b1~3^2~1 X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=dbbd42ebe4d554bdf2c36fad5790efe94188b5f2;p=thirdparty%2Ftornado.git Revert "asyncio: Remove atexit hook" This reverts commit 62363740c1cc0e137ff4344c3afc3d52e070f200. We are again seeing hangs at shutdown in SyncHTTPClientTest.test_destructor_log. Maybe putting this back will help. --- diff --git a/tornado/platform/asyncio.py b/tornado/platform/asyncio.py index 8d16aa6d1..79e60848b 100644 --- a/tornado/platform/asyncio.py +++ b/tornado/platform/asyncio.py @@ -23,6 +23,7 @@ the same event loop. """ import asyncio +import atexit import concurrent.futures import errno import functools @@ -59,6 +60,31 @@ _FileDescriptorLike = Union[int, _HasFileno] _T = TypeVar("_T") +# Collection of selector thread event loops to shut down on exit. +_selector_loops: Set["SelectorThread"] = set() + + +def _atexit_callback() -> None: + for loop in _selector_loops: + with loop._select_cond: + loop._closing_selector = True + loop._select_cond.notify() + try: + loop._waker_w.send(b"a") + except BlockingIOError: + pass + if loop._thread is not None: + # If we don't join our (daemon) thread here, we may get a deadlock + # during interpreter shutdown. I don't really understand why. This + # deadlock happens every time in CI (both travis and appveyor) but + # I've never been able to reproduce locally. + loop._thread.join() + _selector_loops.clear() + + +atexit.register(_atexit_callback) + + class BaseAsyncIOLoop(IOLoop): def initialize( # type: ignore self, asyncio_loop: asyncio.AbstractEventLoop, **kwargs: Any @@ -453,6 +479,7 @@ class SelectorThread: self._waker_r, self._waker_w = socket.socketpair() self._waker_r.setblocking(False) self._waker_w.setblocking(False) + _selector_loops.add(self) self.add_reader(self._waker_r, self._consume_waker) def close(self) -> None: @@ -464,6 +491,7 @@ class SelectorThread: self._wake_selector() if self._thread is not None: self._thread.join() + _selector_loops.discard(self) self.remove_reader(self._waker_r) self._waker_r.close() self._waker_w.close()