[svn r91] http://jira.secondlife.com/browse/EVT-13: Timer cleanup behavior is inconsistent between HTTP/1.1 and HTTP/1.0. This involved capturing a traceback on timer creation (based on a debug variable -- it'd be expensive to do that all the time), and adding timer cleanup code at the end of the handle method.
This commit is contained in:
@@ -490,6 +490,7 @@ class HttpProtocol(BaseHTTPServer.BaseHTTPRequestHandler):
|
|||||||
except (TypeError, ValueError), e:
|
except (TypeError, ValueError), e:
|
||||||
pass
|
pass
|
||||||
|
|
||||||
|
try:
|
||||||
try:
|
try:
|
||||||
try:
|
try:
|
||||||
self.server.site.handle_request(request)
|
self.server.site.handle_request(request)
|
||||||
@@ -498,6 +499,10 @@ class HttpProtocol(BaseHTTPServer.BaseHTTPRequestHandler):
|
|||||||
reason_phrase=err.reason,
|
reason_phrase=err.reason,
|
||||||
headers=err.headers,
|
headers=err.headers,
|
||||||
body=err.body)
|
body=err.body)
|
||||||
|
finally:
|
||||||
|
# clean up any timers that might have been left around by the handling code
|
||||||
|
api.get_hub().runloop.cancel_timers(api.getcurrent())
|
||||||
|
|
||||||
# throw an exception if it failed to write a body
|
# throw an exception if it failed to write a body
|
||||||
if not request.response_written():
|
if not request.response_written():
|
||||||
raise NotImplementedError("Handler failed to write response to request: %s" % request)
|
raise NotImplementedError("Handler failed to write response to request: %s" % request)
|
||||||
@@ -507,7 +512,7 @@ class HttpProtocol(BaseHTTPServer.BaseHTTPRequestHandler):
|
|||||||
request.read_body() ## read & discard body
|
request.read_body() ## read & discard body
|
||||||
except:
|
except:
|
||||||
pass
|
pass
|
||||||
continue
|
|
||||||
except socket.error, e:
|
except socket.error, e:
|
||||||
# Broken pipe, connection reset by peer
|
# Broken pipe, connection reset by peer
|
||||||
if e[0] in CONNECTION_CLOSED:
|
if e[0] in CONNECTION_CLOSED:
|
||||||
|
@@ -218,10 +218,11 @@ class RunLoop(object):
|
|||||||
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]:
|
||||||
if timer.seconds:
|
if not timer.cancelled 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()
|
timer.cancel()
|
||||||
|
print 'Runloop cancelling left-over timer %s' % timer
|
||||||
del self.timers_by_greenlet[greenlet]
|
del self.timers_by_greenlet[greenlet]
|
||||||
|
|
||||||
|
@@ -24,8 +24,12 @@ THE SOFTWARE.
|
|||||||
"""
|
"""
|
||||||
from eventlet.api import get_hub
|
from eventlet.api import get_hub
|
||||||
|
|
||||||
|
""" If true, captures a stack trace for each timer when constructed. This is
|
||||||
|
useful for debugging leaking timers, to find out where the timer was set up. """
|
||||||
|
_g_debug = False
|
||||||
|
|
||||||
class Timer(object):
|
class Timer(object):
|
||||||
__slots__ = ['seconds', 'tpl', 'called', 'cancelled', 'scheduled_time', 'greenlet']
|
__slots__ = ['seconds', 'tpl', 'called', 'cancelled', 'scheduled_time', 'greenlet', 'traceback']
|
||||||
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,12 +44,19 @@ class Timer(object):
|
|||||||
self.seconds = seconds
|
self.seconds = seconds
|
||||||
self.tpl = cb, args, kw
|
self.tpl = cb, args, kw
|
||||||
self.called = False
|
self.called = False
|
||||||
|
if _g_debug:
|
||||||
|
import traceback, cStringIO
|
||||||
|
self.traceback = cStringIO.StringIO()
|
||||||
|
traceback.print_stack(file=self.traceback)
|
||||||
|
|
||||||
def __repr__(self):
|
def __repr__(self):
|
||||||
secs = getattr(self, 'seconds', None)
|
secs = getattr(self, 'seconds', None)
|
||||||
cb, args, kw = getattr(self, 'tpl', (None, None, None))
|
cb, args, kw = getattr(self, 'tpl', (None, None, None))
|
||||||
return "Timer(%s, %s, *%s, **%s)" % (
|
retval = "Timer(%s, %s, *%s, **%s)" % (
|
||||||
secs, cb, args, kw)
|
secs, cb, args, kw)
|
||||||
|
if _g_debug and hasattr(self, 'traceback':
|
||||||
|
retval += '\n' + self.traceback.getvalue()
|
||||||
|
return retval
|
||||||
|
|
||||||
def copy(self):
|
def copy(self):
|
||||||
cb, args, kw = self.tpl
|
cb, args, kw = self.tpl
|
||||||
|
Reference in New Issue
Block a user