]> git.ipfire.org Git - thirdparty/tornado.git/commitdiff
Fix WaitIterator when no hard reference is kept.
authorBen Darnell <ben@bendarnell.com>
Sat, 18 Apr 2015 23:19:00 +0000 (19:19 -0400)
committerBen Darnell <ben@bendarnell.com>
Sat, 18 Apr 2015 23:19:00 +0000 (19:19 -0400)
Closes #1397.

tornado/gen.py
tornado/test/gen_test.py

index e184c43371c44b6d946aa2edd52a61dc74ab983a..86635c0d6db3dafa94cfd7f48b88f8ecb0730d26 100644 (file)
@@ -336,6 +336,8 @@ class WaitIterator(object):
         self.current_index = self.current_future = None
         self._running_future = None
 
+        # Use a weak reference to self to avoid cycles that may delay
+        # garbage collection.
         self_ref = weakref.ref(self)
         for future in futures:
             future.add_done_callback(functools.partial(
@@ -356,6 +358,12 @@ class WaitIterator(object):
         the inputs.
         """
         self._running_future = TracebackFuture()
+        # As long as there is an active _running_future, we must
+        # ensure that the WaitIterator is not GC'd (due to the
+        # use of weak references in __init__). Add a callback that
+        # references self so there is a hard reference that will be
+        # cleared automatically when this Future finishes.
+        self._running_future.add_done_callback(lambda f: self)
 
         if self._finished:
             self._return_result(self._finished.popleft())
index d9188fec30dac9a33f8b749101966407a3d9073f..fdaa0ec804dd56c1efc3a9c46837bf453bb61ea4 100644 (file)
@@ -1249,5 +1249,15 @@ class WaitIteratorTest(AsyncTestCase):
                     self.assertEqual(g.current_index, 3, 'wrong index')
             i += 1
 
+    @gen_test
+    def test_no_ref(self):
+        # In this usage, there is no direct hard reference to the
+        # WaitIterator itself, only the Future it returns. Since
+        # WaitIterator uses weak references internally to improve GC
+        # performance, this used to cause problems.
+        yield gen.with_timeout(datetime.timedelta(seconds=0.1),
+                               gen.WaitIterator(gen.sleep(0)).next())
+
+
 if __name__ == '__main__':
     unittest.main()