From bff07405549a6eb173a4cfc9bbc3fc7c6da5cdd7 Mon Sep 17 00:00:00 2001 From: Ben Darnell Date: Sat, 17 Nov 2012 16:35:42 -0500 Subject: [PATCH] Fix a memory leak in stack_context. 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. --- tornado/gen.py | 1 + tornado/stack_context.py | 1 + tornado/test/gen_test.py | 22 ++++++++++++++++++++++ 3 files changed, 24 insertions(+) diff --git a/tornado/gen.py b/tornado/gen.py index 51d64a3aa..88ded2eff 100644 --- a/tornado/gen.py +++ b/tornado/gen.py @@ -374,6 +374,7 @@ class Runner(object): "finished without waiting for callbacks %r" % self.pending_callbacks) self.deactivate_stack_context() + self.deactivate_stack_context = None return except Exception: self.finished = True diff --git a/tornado/stack_context.py b/tornado/stack_context.py index b0bf108ac..d4aec3c5e 100644 --- a/tornado/stack_context.py +++ b/tornado/stack_context.py @@ -160,6 +160,7 @@ class ExceptionStackContext(object): return self.exception_handler(type, value, traceback) finally: _state.contexts = self.old_contexts + self.old_contexts = None class NullContext(object): diff --git a/tornado/test/gen_test.py b/tornado/test/gen_test.py index 6c674c5ad..b25d1050f 100644 --- a/tornado/test/gen_test.py +++ b/tornado/test/gen_test.py @@ -270,6 +270,28 @@ class GenTest(AsyncTestCase): 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 -- 2.47.2