Skip to content

Commit 18a66be

Browse files
committed
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.
1 parent 488c0d2 commit 18a66be

File tree

3 files changed

+24
-0
lines changed

3 files changed

+24
-0
lines changed

tornado/gen.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -354,6 +354,7 @@ def run(self):
354354
"finished without waiting for callbacks %r" %
355355
self.pending_callbacks)
356356
self.deactivate_stack_context()
357+
self.deactivate_stack_context = None
357358
return
358359
except Exception:
359360
self.finished = True

tornado/stack_context.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -161,6 +161,7 @@ def __exit__(self, type, value, traceback):
161161
return self.exception_handler(type, value, traceback)
162162
finally:
163163
_state.contexts = self.old_contexts
164+
self.old_contexts = None
164165

165166

166167
class NullContext(object):

tornado/test/gen_test.py

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -269,6 +269,28 @@ def outer():
269269
initial_stack_depth = len(stack_context._state.contexts)
270270
self.run_gen(outer)
271271

272+
def test_stack_context_leak_exception(self):
273+
# same as previous, but with a function that exits with an exception
274+
from tornado import stack_context
275+
276+
@gen.engine
277+
def inner(callback):
278+
yield gen.Task(self.io_loop.add_callback)
279+
1 / 0
280+
281+
@gen.engine
282+
def outer():
283+
for i in xrange(10):
284+
try:
285+
yield gen.Task(inner)
286+
except ZeroDivisionError:
287+
pass
288+
stack_increase = len(stack_context._state.contexts) - initial_stack_depth
289+
self.assertTrue(stack_increase <= 2)
290+
self.stop()
291+
initial_stack_depth = len(stack_context._state.contexts)
292+
self.run_gen(outer)
293+
272294

273295
class GenSequenceHandler(RequestHandler):
274296
@asynchronous

0 commit comments

Comments
 (0)