]> 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>
Sat, 17 Nov 2012 21:35:42 +0000 (16:35 -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 51d64a3aa3d810efb692bb5f38d9ecfb85efe796..88ded2effbede315f03d886c55330af14a5e734d 100644 (file)
@@ -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
index b0bf108acc6f3003ca496d06f25dc566964771d2..d4aec3c5eeb30f43ced1c2dce7a5a958139a12ec 100644 (file)
@@ -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):
index 6c674c5ada773e6547f0233a3d6c1ed758df26d7..b25d1050f2eddce1e92262375764ee915c73fbbe 100644 (file)
@@ -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