From: A. Jesse Jiryu Davis Date: Wed, 21 Sep 2016 19:47:39 +0000 (-0400) Subject: io_loop: Don't spin CPU if callback returns {} X-Git-Tag: v4.5.0~65^2 X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=7c9ca5070179eed6476585da009398dfffe2ab3c;p=thirdparty%2Ftornado.git io_loop: Don't spin CPU if callback returns {} Fixes an infinite loop by preventing _run_callback from directly calling add_callback again when a callback returns {} or []. --- diff --git a/tornado/ioloop.py b/tornado/ioloop.py index cadb41161..d61831766 100644 --- a/tornado/ioloop.py +++ b/tornado/ioloop.py @@ -616,10 +616,14 @@ class IOLoop(Configurable): # result, which should just be ignored. pass else: - self.add_future(ret, lambda f: f.result()) + self.add_future(ret, self._discard_future_result) except Exception: self.handle_callback_exception(callback) + def _discard_future_result(self, future): + """Avoid unhandled-exception warnings from spawned coroutines.""" + future.result() + def handle_callback_exception(self, callback): """This method is called whenever a callback run by the `IOLoop` throws an exception. diff --git a/tornado/test/ioloop_test.py b/tornado/test/ioloop_test.py index 8570e73f0..1bb8ce081 100644 --- a/tornado/test/ioloop_test.py +++ b/tornado/test/ioloop_test.py @@ -9,6 +9,7 @@ import socket import sys import threading import time +import types from tornado import gen from tornado.ioloop import IOLoop, TimeoutError, PollIOLoop, PeriodicCallback @@ -61,6 +62,25 @@ class FakeTimeIOLoop(PollIOLoop): class TestIOLoop(AsyncTestCase): + def test_add_callback_return_sequence(self): + # A callback returning {} or [] shouldn't spin the CPU, see Issue #1803. + self.calls = 0 + + loop = self.io_loop + test = self + old_add_callback = loop.add_callback + + def add_callback(self, callback, *args, **kwargs): + test.calls += 1 + old_add_callback(callback, *args, **kwargs) + + loop.add_callback = types.MethodType(add_callback, loop) + loop.add_callback(lambda: {}) + loop.add_callback(lambda: []) + loop.add_timeout(datetime.timedelta(milliseconds=50), loop.stop) + loop.start() + self.assertLess(self.calls, 10) + @skipOnTravis def test_add_callback_wakeup(self): # Make sure that add_callback from inside a running IOLoop