def run_until_complete(task): tasks = [(task, None)] selector = DefaultSelector() while tasks or selector.get_map(): timeout = 0 if tasks else None if selector.get_map(): for key, events in selector.select(timeout): tasks.append((key.data, None)) selector.unregister(key.fileobj) queue, tasks = tasks, [] for task, data in queue: try: data = task.send(data) except StopIteration: pass else: if data and data[0] == EVENT_READ: selector.register(data[1], EVENT_READ, task) elif data and data[0] == EVENT_WRITE: selector.register(data[1], EVENT_WRITE, task) elif data and data[0] == 'spawn': tasks.append((data[1], None)) tasks.append((task, data[1])) else: tasks.append((task, None))
def run_until_complete(task): tasks = [(task, None)] # NEW: prepare for I/O multiplexing. selector = DefaultSelector() # NEW: watch out, all tasks might be suspended at the same time. while tasks or selector.get_map(): # NEW: poll I/O operation status and resume tasks when ready. timeout = 0.0 if tasks else None for key, events in selector.select(timeout): tasks.append((key.data, None)) selector.unregister(key.fileobj) queue, tasks = tasks, [] for task, data in queue: try: data = task.send(data) except StopIteration: pass else: # NEW: register for I/O and suspend the task. if data and data[0] == EVENT_READ: selector.register(data[1], EVENT_READ, task) elif data and data[0] == EVENT_WRITE: selector.register(data[1], EVENT_WRITE, task) else: tasks.append((task, None))
def run_until_complete(task): tasks = [(task, None)] selector = DefaultSelector() while tasks or selector.get_map(): print("___________________________________") # poll I/O and resume when ready timeout = 0.0 if tasks else None for key, events in selector.select(timeout): tasks.append((key.data, None)) selector.unregister(key.fileobj) queue, tasks = tasks, [] for task, data in queue: try: print(f"loop: send {data} into {task.__name__}") data = task.send(data) print(f"loop: received {data} from {task.__name__}") except StopIteration as res: pass except Exception as exp: print(repr(exp)) else: if data: req, _ = data if req == EVENT_READ: stream = data[1] selector.register(stream, EVENT_READ, task) elif req == EVENT_WRITE: stream = data[1] selector.register(data[1], EVENT_WRITE, task) else: tasks.append((task, None))
class TestBase(object): AF: int = AF_INET aa: Type[SocketReader] = SocketReader dd: Type[SocketReader] = SocketReader E: BaseSelector a: SocketReader b: EndPoint c: EndPoint d: SocketReader acceptor: socket listener: FlowAcceptor @classmethod def config(self, ahost: str, aport: int): return {'flow': {ahost + ':0': ahost + ':' + str(aport)}} def setup_method(self) -> None: self.acceptor = socket(self.AF, SOCK_STREAM) self.acceptor.bind(('localhost', 0)) self.acceptor.listen(1) ahost, aport = self.acceptor.getsockname()[:2] self.E = DefaultSelector() load_config(self.E, self.config(ahost, aport)) # There should be a single item in the map... (_, _, _, self.listener), = self.E.get_map().values() self.a = self.aa(self.AF, SOCK_STREAM) self.a.selector = self.E try: self.a.connect(self.listener.getsockname()) except BlockingIOError: pass Dispatchable.dispatch(self.E, 1) for _, _, _, s in self.E.get_map().values(): if s == self.listener: pass elif s.connected: self.b = s else: self.c = s assert hasattr(self, 'b') assert hasattr(self, 'c') serverfd, _ = self.acceptor._accept() # type: ignore self.d = self.dd(fileno=serverfd) self.d.selector = self.E for X in self.a, self.b, self.c, self.d: X.setsockopt(IPPROTO_TCP, TCP_NODELAY, 1) self.a.reregister() self.d.reregister() def teardown_method(self) -> None: for _, _, _, X in self.E.get_map().values(): X.close() for X in self.a, self.d, self.E, self.acceptor, self.listener: X.close() def recv_wait(self, s: socket, n: int) -> bytes: for _ in range(100): assert Dispatchable.dispatch(self.E, 1) try: return s.recv(n) except BlockingIOError: pass assert False def raises(self, C): import pytest # type: ignore return pytest.raises(C)
class EventLoop: def __init__(self): self.tasks = deque() self.selector = DefaultSelector() def run_until_complete(self, task): self.tasks.append(task) self.run() def pause(self): return "pause", None def schedule(self, target): return "schedule", target def sock_accept(self, sock): yield ("read", sock) return sock.accept() def sock_recv(self, sock): yield ("read", sock) return sock.recv(1024) def sock_sendall(self, sock, data): yield ("write", sock) return sock.sendall(data) def start_server(self, handler, host, port, backlog=0): handler = partial(handler, self) with socket.socket() as sock: sock.bind((host, port)) sock.listen(backlog) print("Listening on {}:{}".format(host, port)) while True: conn, addr = yield from self.sock_accept(sock) print("Accepted client from", addr) yield self.schedule(handler(conn)) def run(self): while self.tasks or self.selector.get_map(): for _ in range(len(self.tasks)): try: task = self.tasks.popleft() tag, value = next(task) if tag == "schedule": self.tasks.append(value) self.tasks.append(task) elif tag == "read": self.selector.register(value, EVENT_READ, data=task) elif tag == "write": self.selector.register(value, EVENT_WRITE, data=task) elif tag == "pause": self.tasks.append(task) else: raise ValueError("Incorrect tag") except StopIteration: continue if self.selector.get_map(): for key, event in self.selector.select(): if event & EVENT_READ or event & EVENT_WRITE: self.tasks.append(key.data) self.selector.unregister(key.fileobj)