Skip to content

Commit

Permalink
Don't add an event handler for IOStream until we've done a read or wr…
Browse files Browse the repository at this point in the history
…ite.

This speeds things up a bit by avoiding a system call to add a handler
for errors that is just replaced by the first read or write.
  • Loading branch information
bdarnell committed Jul 6, 2011
1 parent 1270943 commit 2603b3c
Showing 1 changed file with 19 additions and 6 deletions.
25 changes: 19 additions & 6 deletions tornado/iostream.py
Original file line number Diff line number Diff line change
Expand Up @@ -96,10 +96,7 @@ def __init__(self, socket, io_loop=None, max_buffer_size=104857600,
self._close_callback = None
self._connect_callback = None
self._connecting = False
self._state = self.io_loop.ERROR
with stack_context.NullContext():
self.io_loop.add_handler(
self.socket.fileno(), self._handle_events, self._state)
self._state = None

def connect(self, address, callback=None):
"""Connects the socket to a remote address without blocking.
Expand Down Expand Up @@ -193,7 +190,8 @@ def close(self):
self._read_until_close = False
self._run_callback(callback,
self._consume(self._read_buffer_size))
self.io_loop.remove_handler(self.socket.fileno())
if self._state is not None:
self.io_loop.remove_handler(self.socket.fileno())
self.socket.close()
self.socket = None
if self._close_callback:
Expand Down Expand Up @@ -238,6 +236,8 @@ def _handle_events(self, fd, events):
if self.writing():
state |= self.io_loop.WRITE
if state != self._state:
assert self._state is not None, \
"shouldn't happen: _handle_events without self._state"
self._state = state
self.io_loop.update_handler(self.socket.fileno(), self._state)
except Exception:
Expand All @@ -261,6 +261,14 @@ def wrapper():
# Re-raise the exception so that IOLoop.handle_callback_exception
# can see it and log the error
raise
if self._state is None:
# If we got here with no self._state, this callback must
# have been triggered by a fast-path read or write
# (and the callback we just executed did not start a
# slow-path operation). If we've never done a slow-path
# op, we can't tell if the connection is closed out from
# under us, so we must add the IOLoop callback here.
self._add_io_state(0)
# We schedule callbacks to be run on the next IOLoop iteration
# rather than running them directly for several reasons:
# * Prevents unbounded stack growth when a callback calls an
Expand Down Expand Up @@ -435,7 +443,12 @@ def _add_io_state(self, state):
if self.socket is None:
# connection has been closed, so there can be no future events
return
if not self._state & state:
if self._state is None:
self._state = ioloop.IOLoop.ERROR | state
with stack_context.NullContext():
self.io_loop.add_handler(
self.socket.fileno(), self._handle_events, self._state)
elif not self._state & state:
self._state = self._state | state
self.io_loop.update_handler(self.socket.fileno(), self._state)

Expand Down

0 comments on commit 2603b3c

Please sign in to comment.