From: Ben Darnell Date: Mon, 12 May 2014 00:23:33 +0000 (-0400) Subject: IOStream: do not listen for close events if there is no close_callback. X-Git-Tag: v4.0.0b1~78 X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=4ee968c1d588d023e28812e004a32a4263f7ad01;p=thirdparty%2Ftornado.git IOStream: do not listen for close events if there is no close_callback. HTTP1Connection now only registers its close callback when it is done reading. This improves performance for synchronous handlers, which no longer interact with the IOLoop as often. --- diff --git a/tornado/http1connection.py b/tornado/http1connection.py index a0c96a2f2..b8109eed4 100644 --- a/tornado/http1connection.py +++ b/tornado/http1connection.py @@ -94,7 +94,6 @@ class HTTP1Connection(httputil.HTTPConnection): # and after it has been read in the client) self._disconnect_on_finish = False self._clear_callbacks() - self.stream.set_close_callback(self._on_connection_close) # Save the start lines after we read or write them; they # affect later processing (e.g. 304 responses and HEAD methods # have content-length but no bodies) @@ -196,7 +195,14 @@ class HTTP1Connection(httputil.HTTPConnection): if not self._write_finished or self.is_client: need_delegate_close = False delegate.finish() - yield self._finish_future + # If we're waiting for the application to produce an asynchronous + # response, and we're not detached, register a close callback + # on the stream (we didn't need one while we were reading) + if (not self._finish_future.done() and + self.stream is not None and + not self.stream.closed()): + self.stream.set_close_callback(self._on_connection_close) + yield self._finish_future if self.is_client and self._disconnect_on_finish: self.close() if self.stream is None: @@ -221,6 +227,8 @@ class HTTP1Connection(httputil.HTTPConnection): self._write_callback = None self._write_future = None self._close_callback = None + if self.stream is not None: + self.stream.set_close_callback(None) def set_close_callback(self, callback): """Sets a callback that will be run when the connection is closed. @@ -231,6 +239,9 @@ class HTTP1Connection(httputil.HTTPConnection): self._close_callback = stack_context.wrap(callback) def _on_connection_close(self): + # Note that this callback is only registered on the IOStream + # when we have finished reading the request and are waiting for + # the application to produce its response. if self._close_callback is not None: callback = self._close_callback self._close_callback = None @@ -240,8 +251,11 @@ class HTTP1Connection(httputil.HTTPConnection): self._clear_callbacks() def close(self): - self.stream.close() + if self.stream is not None: + self.stream.close() self._clear_callbacks() + if not self._finish_future.done(): + self._finish_future.set_result(None) def detach(self): """Take control of the underlying stream. @@ -251,6 +265,7 @@ class HTTP1Connection(httputil.HTTPConnection): `.HTTPMessageDelegate.headers_received`. Intended for implementing protocols like websockets that tunnel over an HTTP handshake. """ + self._clear_callbacks() stream = self.stream self.stream = None return stream diff --git a/tornado/iostream.py b/tornado/iostream.py index 597516eff..e5423fe69 100644 --- a/tornado/iostream.py +++ b/tornado/iostream.py @@ -316,6 +316,7 @@ class BaseIOStream(object): `StreamClosedError` when the stream is closed. """ self._close_callback = stack_context.wrap(callback) + self._maybe_add_error_listener() def close(self, exc_info=False): """Close this stream. @@ -764,7 +765,8 @@ class BaseIOStream(object): if self._state is None or self._state == ioloop.IOLoop.ERROR: if self.closed(): self._maybe_run_close_callback() - elif self._read_buffer_size == 0: + elif (self._read_buffer_size == 0 and + self._close_callback is not None): self._add_io_state(ioloop.IOLoop.READ) def _add_io_state(self, state): diff --git a/tornado/test/iostream_test.py b/tornado/test/iostream_test.py index efe357807..baca340af 100644 --- a/tornado/test/iostream_test.py +++ b/tornado/test/iostream_test.py @@ -199,9 +199,6 @@ class TestIOStreamMixin(object): server, client = self.make_iostream_pair() server.write(b'', callback=self.stop) self.wait() - # As a side effect, the stream is now listening for connection - # close (if it wasn't already), but is not listening for writes - self.assertEqual(server._state, IOLoop.READ | IOLoop.ERROR) server.close() client.close() diff --git a/tornado/websocket.py b/tornado/websocket.py index 66a4fedf9..218413f89 100644 --- a/tornado/websocket.py +++ b/tornado/websocket.py @@ -118,6 +118,7 @@ class WebSocketHandler(tornado.web.RequestHandler): self.open_kwargs = kwargs self.stream = self.request.connection.detach() + self.stream.set_close_callback(self.on_connection_close) # Upgrade header should be present and should be equal to WebSocket if self.request.headers.get("Upgrade", "").lower() != 'websocket':