]> git.ipfire.org Git - thirdparty/tornado.git/commitdiff
Fix a memory leak in stack_context.
authorBen Darnell <ben@bendarnell.com>
Sat, 17 Nov 2012 21:35:42 +0000 (16:35 -0500)
committerBen Darnell <ben@bendarnell.com>
Thu, 22 Nov 2012 18:28:50 +0000 (13:28 -0500)
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
tornado/stack_context.py
tornado/test/gen_test.py

index 506697d7b9a426c81bda2cb6456fef40947760ca..03d08ba651a9e48e01bef94bf0377b3c3ecf8fdd 100644 (file)
@@ -354,6 +354,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
index e70fe53d8b3d148649ee1453935b35bd48622158..68950676f545a95fac0f0a9f087b2dc07f158f15 100644 (file)
@@ -161,6 +161,7 @@ class ExceptionStackContext(object):
                 return self.exception_handler(type, value, traceback)
         finally:
             _state.contexts = self.old_contexts
+            self.old_contexts = None
 
 
 class NullContext(object):
index 4daad796c4c3c7dc446ddcd8b97afb60a4e2a450..e168c18a7c00bb68a6b48757b76fa6fe79f70ec3 100644 (file)
@@ -269,6 +269,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