Combination of patches from the grugq and Augusto Becciu; try really hard not to leak timers as garbage.
This commit is contained in:
@@ -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_finished(self, timer):
|
||||||
|
try:
|
||||||
|
del self.timers_by_greenlet[timer.greenlet][timer]
|
||||||
|
if not self.timers_by_greenlet[timer.greenlet]:
|
||||||
|
del self.timers_by_greenlet[timer.greenlet]
|
||||||
|
except KeyError:
|
||||||
|
pass
|
||||||
|
|
||||||
def timer_canceled(self, timer):
|
def timer_canceled(self, timer):
|
||||||
del self.timers_by_greenlet[timer.greenlet][timer]
|
self.timer_finished(timer)
|
||||||
if not self.timers_by_greenlet[timer.greenlet]:
|
|
||||||
del self.timers_by_greenlet[timer.greenlet]
|
|
||||||
|
|
||||||
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.
|
||||||
timer.cancel()
|
try:
|
||||||
|
# this might be None due to weirdness with weakrefs
|
||||||
|
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
|
||||||
del self.timers_by_greenlet[greenlet]
|
try:
|
||||||
|
del self.timers_by_greenlet[greenlet]
|
||||||
|
except KeyError:
|
||||||
|
pass
|
||||||
|
|
||||||
|
@@ -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)
|
||||||
|
@@ -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)
|
||||||
|
@@ -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
|
||||||
@@ -57,28 +56,28 @@ class Timer(object):
|
|||||||
secs, cb, args, kw)
|
secs, cb, args, kw)
|
||||||
if _g_debug and hasattr(self, 'traceback'):
|
if _g_debug and hasattr(self, 'traceback'):
|
||||||
retval += '\n' + self.traceback.getvalue()
|
retval += '\n' + self.traceback.getvalue()
|
||||||
return retval
|
return retval
|
||||||
|
|
||||||
def copy(self):
|
def copy(self):
|
||||||
cb, args, kw = self.tpl
|
cb, args, kw = self.tpl
|
||||||
return self.__class__(self.seconds, cb, *args, **kw)
|
return self.__class__(self.seconds, cb, *args, **kw)
|
||||||
|
|
||||||
def schedule(self):
|
def schedule(self):
|
||||||
"""Schedule this timer to run in the current runloop.
|
"""Schedule this timer to run in the current runloop.
|
||||||
"""
|
"""
|
||||||
self.called = False
|
self.called = False
|
||||||
self.scheduled_time = get_hub().add_timer(self)
|
self.scheduled_time = get_hub().add_timer(self)
|
||||||
return self
|
return self
|
||||||
|
|
||||||
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
|
||||||
cb(*args, **kw)
|
try:
|
||||||
|
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
|
||||||
been called, has no effect.
|
been called, has no effect.
|
||||||
|
Reference in New Issue
Block a user