From: Ben Darnell Date: Sun, 20 May 2018 15:28:03 +0000 (-0400) Subject: asyncio: Fix a race between close and initialize X-Git-Tag: v5.1.0b1~10^2 X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=refs%2Fpull%2F2395%2Fhead;p=thirdparty%2Ftornado.git asyncio: Fix a race between close and initialize The close method of one IOLoop could race with the initialize method of another one, leading to KeyErrors raised in close(). Fixes #2367 --- diff --git a/tornado/platform/asyncio.py b/tornado/platform/asyncio.py index b6a490afa..e0042e1d9 100644 --- a/tornado/platform/asyncio.py +++ b/tornado/platform/asyncio.py @@ -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) diff --git a/tornado/test/ioloop_test.py b/tornado/test/ioloop_test.py index 646a1d431..b38d0ba32 100644 --- a/tornado/test/ioloop_test.py +++ b/tornado/test/ioloop_test.py @@ -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.