'Adds `state` (IOLoop.{READ,WRITE} flags) to our event handler.
Implementation notes: Reads and writes have a fast path and a
slow path.  The fast path reads synchronously from socket
buffers, while the slow path uses `_add_io_state` to schedule
an IOLoop callback.  Note that in both cases, the callback is
run asynchronously with `_run_callback`.
To detect closed connections, we must have called
`_add_io_state` at some point, but we want to delay this as
much as possible so we don\'t have to set an `IOLoop.ERROR`
listener that will be overwritten by the next slow-path
operation.  As long as there are callbacks scheduled for
fast-path ops, those callbacks may do more reads.
If a sequence of fast-path ops do not end in a slow-path op,
(e.g. for an @asynchronous long-poll request), we must add
the error handler.  This is done in `_run_callback` and `write`
(since the write callback is optional so we can have a
fast-path write with no `_run_callback`)'

def _add_io_state(self, state):

    if self.closed():
        return
    if (self._state is None):
        self._state = (ioloop.IOLoop.ERROR | state)
        with stack_context.NullContext():
            self.io_loop.add_handler(self.fileno(), self._handle_events, self._state)
    elif (not (self._state & state)):
        self._state = (self._state | state)
        self.io_loop.update_handler(self.fileno(), self._state)
