From: Ben Darnell Date: Sun, 31 Dec 2017 22:05:05 +0000 (-0500) Subject: asyncio: Don't raise "IOLoop is closing" in add_callback X-Git-Tag: v5.0.0~22^2~1 X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=207ff24d273195043cc8d8e874da890160619b90;p=thirdparty%2Ftornado.git asyncio: Don't raise "IOLoop is closing" in add_callback This mirrors a previous change to PollIOLoop. Fix a test that had rotted and become meaningless. Fixes #2191 --- diff --git a/tornado/platform/asyncio.py b/tornado/platform/asyncio.py index adc7c1aeb..42c8a8001 100644 --- a/tornado/platform/asyncio.py +++ b/tornado/platform/asyncio.py @@ -130,13 +130,17 @@ class BaseAsyncIOLoop(IOLoop): timeout.cancel() def add_callback(self, callback, *args, **kwargs): - if self.closing: - # TODO: this is racy; we need a lock to ensure that the - # loop isn't closed during call_soon_threadsafe. - raise RuntimeError("IOLoop is closing") - self.asyncio_loop.call_soon_threadsafe( - self._run_callback, - functools.partial(stack_context.wrap(callback), *args, **kwargs)) + try: + self.asyncio_loop.call_soon_threadsafe( + self._run_callback, + functools.partial(stack_context.wrap(callback), *args, **kwargs)) + except RuntimeError: + # "Event loop is closed". Swallow the exception for + # consistency with PollIOLoop (and logical consistency + # with the fact that we can't guarantee that an + # add_callback that completes without error will + # eventually execute). + pass add_callback_from_signal = add_callback diff --git a/tornado/test/ioloop_test.py b/tornado/test/ioloop_test.py index 616099452..0ca52c66a 100644 --- a/tornado/test/ioloop_test.py +++ b/tornado/test/ioloop_test.py @@ -171,10 +171,9 @@ class TestIOLoop(AsyncTestCase): other_ioloop.close() def test_add_callback_while_closing(self): - # Issue #635: add_callback() should raise a clean exception - # if called while another thread is closing the IOLoop. - if IOLoop.configured_class().__name__.endswith('AsyncIOLoop'): - raise unittest.SkipTest("AsyncIOMainLoop shutdown not thread safe") + # add_callback should not fail if it races with another thread + # closing the IOLoop. The callbacks are dropped silently + # without executing. closing = threading.Event() def target(): @@ -187,11 +186,7 @@ class TestIOLoop(AsyncTestCase): thread.start() closing.wait() for i in range(1000): - try: - other_ioloop.add_callback(lambda: None) - except RuntimeError as e: - self.assertEqual("IOLoop is closing", str(e)) - break + other_ioloop.add_callback(lambda: None) def test_handle_callback_exception(self): # IOLoop.handle_callback_exception can be overridden to catch