Skip to content

Commit

Permalink
Fix a memory leak in stack_context.
Browse files Browse the repository at this point in the history
The old_contexts reference in StackContexts could maintain a chain of
old irrelevant contexts, so clear it once it's no longer needed.
This was mainly a problem in gen.engine, where additional contexts
would accumulate in memory (but not on the stack) for each asynchronous
operation.

Also clear the deactivate_stack_context in gen.Runner to allow
the StackContext to be garbage-collected sooner.
  • Loading branch information
bdarnell committed Nov 22, 2012
1 parent 488c0d2 commit 18a66be
Show file tree
Hide file tree
Showing 3 changed files with 24 additions and 0 deletions.
1 change: 1 addition & 0 deletions tornado/gen.py
Original file line number Diff line number Diff line change
Expand Up @@ -354,6 +354,7 @@ def run(self):
"finished without waiting for callbacks %r" %
self.pending_callbacks)
self.deactivate_stack_context()
self.deactivate_stack_context = None
return
except Exception:
self.finished = True
Expand Down
1 change: 1 addition & 0 deletions tornado/stack_context.py
Original file line number Diff line number Diff line change
Expand Up @@ -161,6 +161,7 @@ def __exit__(self, type, value, traceback):
return self.exception_handler(type, value, traceback)
finally:
_state.contexts = self.old_contexts
self.old_contexts = None


class NullContext(object):
Expand Down
22 changes: 22 additions & 0 deletions tornado/test/gen_test.py
Original file line number Diff line number Diff line change
Expand Up @@ -269,6 +269,28 @@ def outer():
initial_stack_depth = len(stack_context._state.contexts)
self.run_gen(outer)

def test_stack_context_leak_exception(self):
# same as previous, but with a function that exits with an exception
from tornado import stack_context

@gen.engine
def inner(callback):
yield gen.Task(self.io_loop.add_callback)
1 / 0

@gen.engine
def outer():
for i in xrange(10):
try:
yield gen.Task(inner)
except ZeroDivisionError:
pass
stack_increase = len(stack_context._state.contexts) - initial_stack_depth
self.assertTrue(stack_increase <= 2)
self.stop()
initial_stack_depth = len(stack_context._state.contexts)
self.run_gen(outer)


class GenSequenceHandler(RequestHandler):
@asynchronous
Expand Down

0 comments on commit 18a66be

Please sign in to comment.