[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:
which.linden
2008-02-19 01:10:56 -05:00
parent 6783d60365
commit 91961b0250
3 changed files with 27 additions and 10 deletions

View File

@@ -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:

View File

@@ -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]

View File

@@ -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