]> git.ipfire.org Git - thirdparty/tornado.git/commitdiff
asyncio: Fix a race between close and initialize 2395/head
authorBen Darnell <ben@bendarnell.com>
Sun, 20 May 2018 15:28:03 +0000 (11:28 -0400)
committerBen Darnell <ben@bendarnell.com>
Sun, 20 May 2018 15:29:32 +0000 (11:29 -0400)
The close method of one IOLoop could race with the initialize method
of another one, leading to KeyErrors raised in close().

Fixes #2367

tornado/platform/asyncio.py
tornado/test/ioloop_test.py

index b6a490afac5c76727d5dbb280177cc0604418d54..e0042e1d9b2b29b67e9340f703b77edecdf54a40 100644 (file)
@@ -62,8 +62,13 @@ class BaseAsyncIOLoop(IOLoop):
             self.remove_handler(fd)
             if all_fds:
                 self.close_fd(fileobj)
-        self.asyncio_loop.close()
+        # Remove the mapping before closing the asyncio loop. If this
+        # happened in the other order, we could race against another
+        # initialize() call which would see the closed asyncio loop,
+        # assume it was closed from the asyncio side, and do this
+        # cleanup for us, leading to a KeyError.
         del IOLoop._ioloop_for_asyncio[self.asyncio_loop]
+        self.asyncio_loop.close()
 
     def add_handler(self, fd, handler, events):
         fd, fileobj = self.split_fd(fd)
index 646a1d431d982a69a065a74695c64e70f4426fdb..b38d0ba32b1c5b29f10f86c53f3fdb551998ca70 100644 (file)
@@ -458,6 +458,16 @@ class TestIOLoop(AsyncTestCase):
             client.close()
             server.close()
 
+    @gen_test
+    def test_init_close_race(self):
+        # Regression test for #2367
+        def f():
+            for i in range(10):
+                loop = IOLoop()
+                loop.close()
+
+        yield gen.multi([self.io_loop.run_in_executor(None, f) for i in range(2)])
+
 
 # Deliberately not a subclass of AsyncTestCase so the IOLoop isn't
 # automatically set as current.