Combination of patches from the grugq and Augusto Becciu; try really hard not to leak timers as garbage.

This commit is contained in:
donovan
2008-07-16 10:43:05 -07:00
parent 63eb7e78f5
commit a6b7e58671
4 changed files with 58 additions and 25 deletions

View File

@@ -22,6 +22,7 @@ THE SOFTWARE.
""" """
import bisect import bisect
import weakref
import sys import sys
import socket import socket
import errno import errno
@@ -237,14 +238,20 @@ class BaseHub(object):
def track_timer(self, timer): def track_timer(self, timer):
current_greenlet = greenlet.getcurrent() current_greenlet = greenlet.getcurrent()
timer.greenlet = current_greenlet timer.greenlet = current_greenlet
if current_greenlet not in self.timers_by_greenlet: self.timers_by_greenlet.setdefault(
self.timers_by_greenlet[current_greenlet] = {} current_greenlet,
self.timers_by_greenlet[current_greenlet][timer] = True weakref.WeakKeyDictionary())[timer] = True
def timer_canceled(self, timer): def timer_finished(self, timer):
try:
del self.timers_by_greenlet[timer.greenlet][timer] del self.timers_by_greenlet[timer.greenlet][timer]
if not self.timers_by_greenlet[timer.greenlet]: if not self.timers_by_greenlet[timer.greenlet]:
del self.timers_by_greenlet[timer.greenlet] del self.timers_by_greenlet[timer.greenlet]
except KeyError:
pass
def timer_canceled(self, timer):
self.timer_finished(timer)
def prepare_timers(self): def prepare_timers(self):
ins = bisect.insort_right ins = bisect.insort_right
@@ -279,21 +286,26 @@ class BaseHub(object):
except: except:
self.squelch_timer_exception(timer, sys.exc_info()) self.squelch_timer_exception(timer, sys.exc_info())
finally: finally:
try: self.timer_finished(timer)
del self.timers_by_greenlet[timer.greenlet][timer]
except KeyError:
pass
del t[:last] del t[:last]
def cancel_timers(self, greenlet, quiet=False): def cancel_timers(self, greenlet, quiet=False):
if greenlet not in self.timers_by_greenlet: if greenlet not in self.timers_by_greenlet:
return return
for timer in self.timers_by_greenlet[greenlet]: for timer in self.timers_by_greenlet[greenlet].keys():
if not timer.cancelled and not timer.called and timer.seconds: if not timer.cancelled and not timer.called and timer.seconds:
## If timer.seconds is 0, this isn't a timer, it's ## If timer.seconds is 0, this isn't a timer, it's
## actually eventlet's silly way of specifying whether ## actually eventlet's silly way of specifying whether
## a coroutine is "ready to run" or not. ## a coroutine is "ready to run" or not.
try:
# this might be None due to weirdness with weakrefs
timer.cancel() timer.cancel()
except TypeError:
pass
if _g_debug and not quiet: if _g_debug and not quiet:
print 'Hub cancelling left-over timer %s' % timer print 'Hub cancelling left-over timer %s' % timer
try:
del self.timers_by_greenlet[greenlet] del self.timers_by_greenlet[greenlet]
except KeyError:
pass

View File

@@ -115,11 +115,22 @@ class Hub(hub.BaseHub):
eventtimer.start() eventtimer.start()
self.track_timer(timer) self.track_timer(timer)
def timer_finished(self, timer):
try:
timer.impltimer.stop()
del timer.impltimer
# XXX might this raise other errors?
except (AttributeError, TypeError):
pass
finally:
super(Hub, self).timer_finished(timer)
def timer_canceled(self, timer): def timer_canceled(self, timer):
""" Cancels the underlying libevent timer. """ """ Cancels the underlying libevent timer. """
try: try:
timer.impltimer.stop() timer.impltimer.stop()
except AttributeError: del timer.impltimer
except (AttributeError, TypeError):
pass pass
finally: finally:
super(Hub, self).timer_canceled(timer) super(Hub, self).timer_canceled(timer)

View File

@@ -124,11 +124,22 @@ class Hub(hub.BaseHub):
eventtimer.add() eventtimer.add()
self.track_timer(timer) self.track_timer(timer)
def timer_finished(self, timer):
try:
timer.impltimer.delete()
del timer.impltimer
# XXX might this raise other exceptions? double delete?
except (AttributeError, TypeError):
pass
finally:
super(Hub, self).timer_finished(timer)
def timer_canceled(self, timer): def timer_canceled(self, timer):
""" Cancels the underlying libevent timer. """ """ Cancels the underlying libevent timer. """
try: try:
timer.impltimer.delete() timer.impltimer.delete()
except AttributeError: del timer.impltimer
except (AttributeError, TypeError):
pass pass
finally: finally:
super(Hub, self).timer_canceled(timer) super(Hub, self).timer_canceled(timer)

View File

@@ -29,7 +29,7 @@ useful for debugging leaking timers, to find out where the timer was set up. """
_g_debug = False _g_debug = False
class Timer(object): class Timer(object):
__slots__ = ['seconds', 'tpl', 'called', 'cancelled', 'scheduled_time', 'greenlet', 'traceback', 'impltimer'] #__slots__ = ['seconds', 'tpl', 'called', 'cancelled', 'scheduled_time', 'greenlet', 'traceback', 'impltimer']
def __init__(self, seconds, cb, *args, **kw): def __init__(self, seconds, cb, *args, **kw):
"""Create a timer. """Create a timer.
seconds: The minimum number of seconds to wait before calling seconds: The minimum number of seconds to wait before calling
@@ -40,7 +40,6 @@ class Timer(object):
This timer will not be run unless it is scheduled in a runloop by This timer will not be run unless it is scheduled in a runloop by
calling timer.schedule() or runloop.add_timer(timer). calling timer.schedule() or runloop.add_timer(timer).
""" """
self.impltimer = None
self.cancelled = False self.cancelled = False
self.seconds = seconds self.seconds = seconds
self.tpl = cb, args, kw self.tpl = cb, args, kw
@@ -73,11 +72,11 @@ class Timer(object):
def __call__(self, *args): def __call__(self, *args):
if not self.called: if not self.called:
self.called = True self.called = True
if self.impltimer is not None:
del get_hub().timers_by_greenlet[self.greenlet][self]
cb, args, kw = self.tpl cb, args, kw = self.tpl
try:
cb(*args, **kw) cb(*args, **kw)
finally:
get_hub().timer_finished(self)
def cancel(self): def cancel(self):
"""Prevent this timer from being called. If the timer has already """Prevent this timer from being called. If the timer has already