From: Ben Darnell Date: Sat, 17 Nov 2012 20:44:21 +0000 (-0500) Subject: Ensure that add_callback fails cleanly if called while IOLoop is closing. X-Git-Tag: v3.0.0~224 X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=b724652105c82f9aaec006cf61e04c3771119735;p=thirdparty%2Ftornado.git Ensure that add_callback fails cleanly if called while IOLoop is closing. Previously there would (sometimes) be an exception when writing to the waker pipe. Closes #635. --- diff --git a/tornado/ioloop.py b/tornado/ioloop.py index 5f01223be..3c9b05b9a 100644 --- a/tornado/ioloop.py +++ b/tornado/ioloop.py @@ -406,6 +406,7 @@ class PollIOLoop(IOLoop): self._timeouts = [] self._running = False self._stopped = False + self._closing = False self._thread_ident = None self._blocking_signal_threshold = None @@ -417,6 +418,8 @@ class PollIOLoop(IOLoop): self.READ) def close(self, all_fds=False): + with self._callback_lock: + self._closing = True self.remove_handler(self._waker.fileno()) if all_fds: for fd in self._handlers.keys()[:]: @@ -608,6 +611,8 @@ class PollIOLoop(IOLoop): def add_callback(self, callback): with self._callback_lock: + if self._closing: + raise RuntimeError("IOLoop is closing") list_empty = not self._callbacks self._callbacks.append(stack_context.wrap(callback)) if list_empty and thread.get_ident() != self._thread_ident: diff --git a/tornado/test/ioloop_test.py b/tornado/test/ioloop_test.py index 27c58bd35..569b03ca5 100644 --- a/tornado/test/ioloop_test.py +++ b/tornado/test/ioloop_test.py @@ -82,6 +82,25 @@ class TestIOLoop(AsyncTestCase): thread.join() 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. + closing = threading.Event() + def target(): + other_ioloop.add_callback(other_ioloop.stop) + other_ioloop.start() + closing.set() + other_ioloop.close(all_fds=True) + other_ioloop = IOLoop() + thread = threading.Thread(target=target) + thread.start() + closing.wait() + for i in range(1000): + try: + other_ioloop.add_callback(lambda: None) + except RuntimeError, e: + self.assertEqual("IOLoop is closing", str(e)) + break class TestIOLoopFutures(AsyncTestCase): def test_add_future_threads(self):