Skip to content

Commit

Permalink
Fix WaitIterator when no hard reference is kept.
Browse files Browse the repository at this point in the history
  • Loading branch information
bdarnell committed Apr 18, 2015
1 parent b450ff2 commit d167681
Show file tree
Hide file tree
Showing 2 changed files with 18 additions and 0 deletions.
8 changes: 8 additions & 0 deletions tornado/gen.py
Original file line number Diff line number Diff line change
Expand Up @@ -336,6 +336,8 @@ def __init__(self, *args, **kwargs):
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(
Expand All @@ -356,6 +358,12 @@ def next(self):
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())
Expand Down
10 changes: 10 additions & 0 deletions tornado/test/gen_test.py
Original file line number Diff line number Diff line change
Expand Up @@ -1249,5 +1249,15 @@ def test_iterator(self):
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()

0 comments on commit d167681

Please sign in to comment.