[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:
@@ -492,12 +492,17 @@ class HttpProtocol(BaseHTTPServer.BaseHTTPRequestHandler):
|
||||
|
||||
try:
|
||||
try:
|
||||
self.server.site.handle_request(request)
|
||||
except ErrorResponse, err:
|
||||
request.response(code=err.code,
|
||||
reason_phrase=err.reason,
|
||||
headers=err.headers,
|
||||
body=err.body)
|
||||
try:
|
||||
self.server.site.handle_request(request)
|
||||
except ErrorResponse, err:
|
||||
request.response(code=err.code,
|
||||
reason_phrase=err.reason,
|
||||
headers=err.headers,
|
||||
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
|
||||
if not request.response_written():
|
||||
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
|
||||
except:
|
||||
pass
|
||||
continue
|
||||
|
||||
except socket.error, e:
|
||||
# Broken pipe, connection reset by peer
|
||||
if e[0] in CONNECTION_CLOSED:
|
||||
|
@@ -218,10 +218,11 @@ class RunLoop(object):
|
||||
if greenlet not in self.timers_by_greenlet:
|
||||
return
|
||||
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
|
||||
## actually eventlet's silly way of specifying whether
|
||||
## a coroutine is "ready to run" or not.
|
||||
timer.cancel()
|
||||
print 'Runloop cancelling left-over timer %s' % timer
|
||||
del self.timers_by_greenlet[greenlet]
|
||||
|
||||
|
@@ -24,8 +24,12 @@ THE SOFTWARE.
|
||||
"""
|
||||
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):
|
||||
__slots__ = ['seconds', 'tpl', 'called', 'cancelled', 'scheduled_time', 'greenlet']
|
||||
__slots__ = ['seconds', 'tpl', 'called', 'cancelled', 'scheduled_time', 'greenlet', 'traceback']
|
||||
def __init__(self, seconds, cb, *args, **kw):
|
||||
"""Create a timer.
|
||||
seconds: The minimum number of seconds to wait before calling
|
||||
@@ -40,12 +44,19 @@ class Timer(object):
|
||||
self.seconds = seconds
|
||||
self.tpl = cb, args, kw
|
||||
self.called = False
|
||||
if _g_debug:
|
||||
import traceback, cStringIO
|
||||
self.traceback = cStringIO.StringIO()
|
||||
traceback.print_stack(file=self.traceback)
|
||||
|
||||
def __repr__(self):
|
||||
secs = getattr(self, 'seconds', 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)
|
||||
if _g_debug and hasattr(self, 'traceback':
|
||||
retval += '\n' + self.traceback.getvalue()
|
||||
return retval
|
||||
|
||||
def copy(self):
|
||||
cb, args, kw = self.tpl
|
||||
|
Reference in New Issue
Block a user