From 05e35c93d1a0251c27e150f06b3533a1d0f87852 Mon Sep 17 00:00:00 2001 From: Edward George Date: Wed, 12 Dec 2012 22:19:24 +0000 Subject: [PATCH] prevent infinite recursion with linking to current greenthread --- eventlet/greenthread.py | 17 +++++++++++++---- tests/greenthread_test.py | 15 +++++++++++++++ 2 files changed, 28 insertions(+), 4 deletions(-) diff --git a/eventlet/greenthread.py b/eventlet/greenthread.py index 96b7fff..9b0e252 100644 --- a/eventlet/greenthread.py +++ b/eventlet/greenthread.py @@ -1,3 +1,4 @@ +from collections import deque import sys from eventlet import event @@ -156,6 +157,7 @@ class GreenThread(greenlet.greenlet): def __init__(self, parent): greenlet.greenlet.__init__(self, self.main, parent) self._exit_event = event.Event() + self._resolving_links = False def wait(self): """ Returns the result of the main function of this GreenThread. If the @@ -182,7 +184,7 @@ class GreenThread(greenlet.greenlet): functions by doing things like switching explicitly to another greenthread. """ - self._exit_funcs = getattr(self, '_exit_funcs', []) + self._exit_funcs = getattr(self, '_exit_funcs', deque()) self._exit_funcs.append((func, curried_args, curried_kwargs)) if self._exit_event.ready(): self._resolve_links() @@ -200,9 +202,16 @@ class GreenThread(greenlet.greenlet): def _resolve_links(self): # ca and ckw are the curried function arguments - for f, ca, ckw in getattr(self, '_exit_funcs', []): - f(self, *ca, **ckw) - self._exit_funcs = [] # so they don't get called again + if self._resolving_links: + return + self._resolving_links = True + try: + exit_funcs = getattr(self, '_exit_funcs', deque()) + while exit_funcs: + f, ca, ckw = exit_funcs.popleft() + f(self, *ca, **ckw) + finally: + self._resolving_links = False def kill(self, *throw_args): """Kills the greenthread using :func:`kill`. After being killed diff --git a/tests/greenthread_test.py b/tests/greenthread_test.py index e2d1047..5bbac1d 100644 --- a/tests/greenthread_test.py +++ b/tests/greenthread_test.py @@ -86,6 +86,21 @@ class Spawn(LimitedTestCase, Asserts): gt.link(link_func, 4, b=5) self.assertEquals(results, [gt, (4,), {'b':5}]) + def test_link_relinks(self): + # test that linking in a linked func doesn't cause infinite recursion. + called = [] + + def link_func(g): + g.link(link_func_pass) + + def link_func_pass(g): + called.append(True) + + gt = greenthread.spawn(passthru) + gt.link(link_func) + gt.wait() + self.assertEquals(called, [True]) + class SpawnAfter(LimitedTestCase, Asserts): def test_basic(self): gt = greenthread.spawn_after(0.1, passthru, 20)