]> git.ipfire.org Git - thirdparty/tornado.git/commitdiff
Revert "asyncio: Remove atexit hook"
authorBen Darnell <ben@bendarnell.com>
Tue, 3 Oct 2023 01:39:39 +0000 (21:39 -0400)
committerBen Darnell <ben@bendarnell.com>
Sat, 14 Oct 2023 02:47:34 +0000 (22:47 -0400)
This reverts commit 62363740c1cc0e137ff4344c3afc3d52e070f200.

We are again seeing hangs at shutdown in
SyncHTTPClientTest.test_destructor_log. Maybe putting this back will
help.

tornado/platform/asyncio.py

index 8d16aa6d15be0f7c537bb5e99f32fe5d87f4a8d5..79e60848b4fb4ccaf5c55fb5956b190be5c476ed 100644 (file)
@@ -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()