Пример #1
0
class EventLoop:
    """event loop, handles IO"""
    def __init__(self):
        self.co_result = {}

        self.selector = DefaultSelector()

    def run_until_complete(self, coroutine):
        self._continue(coroutine, None)
        while coroutine not in self.co_result:
            self._tick()
        result = self.co_result[coroutine]
        del self.co_result[coroutine]
        return result

    def run_forever(self):
        while True:
            self._tick()

    def start_coroutine(self, co):
        self._continue(co, None)

    def _continue(self, co, awaited):
        try:
            cmd = co.send(awaited)
        except StopIteration as e:
            self.co_result[co] = e.value
        else:
            if isinstance(cmd, EventLoopCommand.StreamAccept):
                self._register(EVENT_READ, cmd.sock, (cmd, co))
            elif isinstance(cmd, EventLoopCommand.StreamConnect):
                try:
                    cmd.sock.connect(cmd.addr)
                except BlockingIOError:
                    self._register(EVENT_WRITE, cmd.sock, (cmd, co))
                except:
                    raise
                else:
                    # connect finished immediately
                    self._continue(co, None)
            elif isinstance(cmd, EventLoopCommand.StreamRecv):
                self._register(EVENT_READ, cmd.sock, (cmd, co))
            elif isinstance(cmd, EventLoopCommand.StreamSend):
                self._register(EVENT_WRITE, cmd.sock, (cmd, co))
            else:
                raise RuntimeError('unknown cmd')

    def _register(self, event, sock, data):
        try:
            key = self.selector.get_key(sock)
        except KeyError:
            self.selector.register(sock, event, {event: data})
        else:
            assert key.events & event == 0
            key.data[event] = data
            self.selector.modify(sock, key.events | event, key.data)

    def _unregister(self, event, sock):
        key = self.selector.get_key(sock)
        assert key.events & event != 0
        new_mask = key.events & ~event
        del key.data[event]
        if new_mask == 0:
            self.selector.unregister(sock)
        else:
            self.selector.modify(sock, new_mask, key.data)

    def _tick(self):
        for key, mask in self.selector.select(1.0):
            if mask & EVENT_WRITE != 0:
                cmd, co = key.data[EVENT_WRITE]
                self._unregister(EVENT_WRITE, cmd.sock)
                if isinstance(cmd, EventLoopCommand.StreamConnect):
                    self._continue(co, cmd.sock.connect(cmd.addr))
                elif isinstance(cmd, EventLoopCommand.StreamSend):
                    self._continue(co, cmd.sock.send(cmd.buf))
                else:
                    raise RuntimeError('unknown cmd')
            elif mask & EVENT_READ != 0:
                cmd, co = key.data[EVENT_READ]
                self._unregister(EVENT_READ, cmd.sock)
                if isinstance(cmd, EventLoopCommand.StreamAccept):
                    self._continue(co, cmd.sock.accept())
                elif isinstance(cmd, EventLoopCommand.StreamRecv):
                    self._continue(co, cmd.sock.recv(cmd.buf_len))
                else:
                    raise RuntimeError('unknown cmd')