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
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():
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