def test_popen(self): command = r"""if 1: import sys s = sys.stdin.readline() sys.stdout.write(s.upper()) sys.stderr.write('stderr') """ msg = b"blah\n" p = windows_utils.Popen( [sys.executable, '-c', command], stdin=windows_utils.PIPE, stdout=windows_utils.PIPE, stderr=windows_utils.PIPE ) for f in [p.stdin, p.stdout, p.stderr]: self.assertIsInstance(f, windows_utils.PipeHandle) ovin = _overlapped.Overlapped() ovout = _overlapped.Overlapped() overr = _overlapped.Overlapped() ovin.WriteFile(p.stdin.handle, msg) ovout.ReadFile(p.stdout.handle, 100) overr.ReadFile(p.stderr.handle, 100) events = [ovin.event, ovout.event, overr.event] # Super-long timeout for slow buildbots. res = _winapi.WaitForMultipleObjects(events, True, 10000) self.assertEqual(res, _winapi.WAIT_OBJECT_0) self.assertFalse(ovout.pending) self.assertFalse(overr.pending) self.assertFalse(ovin.pending) self.assertEqual(ovin.getresult(), len(msg)) out = ovout.getresult().rstrip() err = overr.getresult().rstrip() self.assertGreater(len(out), 0) self.assertGreater(len(err), 0) # allow for partial reads... self.assertTrue(msg.upper().rstrip().startswith(out)) self.assertTrue(b"stderr".startswith(err)) # The context manager calls wait() and closes resources with p: pass
def accept(self, listener): self._register_with_iocp(listener) conn = self._get_accept_socket(listener.family) ov = _overlapped.Overlapped(NULL) ov.AcceptEx(listener.fileno(), conn.fileno()) def finish_accept(trans, key, ov): ov.getresult() # Use SO_UPDATE_ACCEPT_CONTEXT so getsockname() etc work. buf = struct.pack('@P', listener.fileno()) conn.setsockopt(socket.SOL_SOCKET, _overlapped.SO_UPDATE_ACCEPT_CONTEXT, buf) conn.settimeout(listener.gettimeout()) return conn, conn.getpeername() async def accept_coro(future, conn): # Coroutine closing the accept socket if the future is cancelled try: await future except exceptions.CancelledError: conn.close() raise future = self._register(ov, listener, finish_accept) coro = accept_coro(future, conn) tasks.ensure_future(coro, loop=self._loop) return future
def _wait_for_handle(self, handle, timeout, _is_cancel): self._check_closed() if timeout is None: ms = _winapi.INFINITE else: # RegisterWaitForSingleObject() has a resolution of 1 millisecond, # round away from zero to wait *at least* timeout seconds. ms = math.ceil(timeout * 1e3) # We only create ov so we can use ov.address as a key for the cache. ov = _overlapped.Overlapped(NULL) wait_handle = _overlapped.RegisterWaitWithQueue( handle, self._iocp, ov.address, ms) if _is_cancel: f = _WaitCancelFuture(ov, handle, wait_handle, loop=self._loop) else: f = _WaitHandleFuture(ov, handle, wait_handle, self, loop=self._loop) if f._source_traceback: del f._source_traceback[-1] def finish_wait_for_handle(trans, key, ov): # Note that this second wait means that we should only use # this with handles types where a successful wait has no # effect. So events or processes are all right, but locks # or semaphores are not. Also note if the handle is # signalled and then quickly reset, then we may return # False even though we have not timed out. return f._poll() self._cache[ov.address] = (f, ov, 0, finish_wait_for_handle) return f
def connect(self, conn, address): if conn.type == socket.SOCK_DGRAM: # WSAConnect will complete immediately for UDP sockets so we don't # need to register any IOCP operation _overlapped.WSAConnect(conn.fileno(), address) fut = self._loop.create_future() fut.set_result(None) return fut self._register_with_iocp(conn) # The socket needs to be locally bound before we call ConnectEx(). try: _overlapped.BindLocal(conn.fileno(), conn.family) except OSError as e: if e.winerror != errno.WSAEINVAL: raise # Probably already locally bound; check using getsockname(). if conn.getsockname()[1] == 0: raise ov = _overlapped.Overlapped(NULL) ov.ConnectEx(conn.fileno(), address) def finish_connect(trans, key, ov): ov.getresult() # Use SO_UPDATE_CONNECT_CONTEXT so getsockname() etc work. conn.setsockopt(socket.SOL_SOCKET, _overlapped.SO_UPDATE_CONNECT_CONTEXT, 0) return conn return self._register(ov, conn, finish_connect)
def _read_data(self, file, type, fut, nbytes, flags=0): ov = _overlapped.Overlapped(NULL) if type: args = () meth = ov.ReadFile else: args = (flags, ) meth = ov.WSARecv meth(file.fileno(), nbytes, *args) self._current_iocp[ov.address] = fut
def _write_data(self, file, type, fut, data, flags=0): ov = _overlapped.Overlapped(NULL) if type: args = () meth = ov.WriteFile else: args = (flags, ) meth = ov.WSASend meth(file.fileno(), data, *args) self._current_iocp[ov.address] = fut
def _drain_stream(self): length, result = 0 if self.closed else 1, b"" while 0 < length <= BUFSIZE: ov = _overlapped.Overlapped(0) buffer = bytes(BUFSIZE) try: ov.ReadFileInto(self.stream.handle, buffer) length = ov.getresult() result += buffer[:length] except BrokenPipeError: break return result
async def send(self, data): # Same send style as usual loop = await get_loop() if isinstance(loop, IOCPLoop): future = Future() ov = _overlapped.Overlapped(0) ov.WSASend(self.socket.fileno(), data, 0) loop._writers[ov.address] = future return await future else: future = Future() self._writequeue.append((future, data)) return await future
def _read_stream(self): ov = None while not self.stop.is_set(): if ov is None: ov = _overlapped.Overlapped(0) try: ov.ReadFile(self.stream.handle, 1) except BrokenPipeError: self.closed = True return data = ov.getresult(10) # wait for 10ms ov = None self.handler(data)
def test_pipe_overlapped(self): h1, h2 = windows_utils.pipe(overlapped=(True, True)) try: ov1 = _overlapped.Overlapped() self.assertFalse(ov1.pending) self.assertEqual(ov1.error, 0) ov1.ReadFile(h1, 100) self.assertTrue(ov1.pending) self.assertEqual(ov1.error, _winapi.ERROR_IO_PENDING) ERROR_IO_INCOMPLETE = 996 try: ov1.getresult() except OSError as e: self.assertEqual(e.winerror, ERROR_IO_INCOMPLETE) else: raise RuntimeError('expected ERROR_IO_INCOMPLETE') ov2 = _overlapped.Overlapped() self.assertFalse(ov2.pending) self.assertEqual(ov2.error, 0) ov2.WriteFile(h2, b"hello") self.assertIn(ov2.error, {0, _winapi.ERROR_IO_PENDING}) res = _winapi.WaitForMultipleObjects([ov2.event], False, 100) self.assertEqual(res, _winapi.WAIT_OBJECT_0) self.assertFalse(ov1.pending) self.assertEqual(ov1.error, ERROR_IO_INCOMPLETE) self.assertFalse(ov2.pending) self.assertIn(ov2.error, {0, _winapi.ERROR_IO_PENDING}) self.assertEqual(ov1.getresult(), b"hello") finally: _winapi.CloseHandle(h1) _winapi.CloseHandle(h2)
def accept_pipe(self, pipe): self._register_with_iocp(pipe) ov = _overlapped.Overlapped(NULL) connected = ov.ConnectNamedPipe(pipe.fileno()) if connected: # ConnectNamePipe() failed with ERROR_PIPE_CONNECTED which means # that the pipe is connected. There is no need to wait for the # completion of the connection. return self._result(pipe) def finish_accept_pipe(trans, key, ov): ov.getresult() return pipe return self._register(ov, pipe, finish_accept_pipe)
def sendto(self, conn, buf, flags=0, addr=None): self._register_with_iocp(conn) ov = _overlapped.Overlapped(NULL) ov.WSASendTo(conn.fileno(), buf, flags, addr) def finish_send(trans, key, ov): try: return ov.getresult() except OSError as exc: if exc.winerror in (_overlapped.ERROR_NETNAME_DELETED, _overlapped.ERROR_OPERATION_ABORTED): raise ConnectionResetError(*exc.args) else: raise return self._register(ov, conn, finish_send)
def send(self, conn, buf, flags=0): self._register_with_iocp(conn) ov = _overlapped.Overlapped(NULL) if isinstance(conn, socket.socket): ov.WSASend(conn.fileno(), buf, flags) else: ov.WriteFile(conn.fileno(), buf) def finish_send(trans, key, ov): try: return ov.getresult() except OSError as exc: if exc.winerror == _overlapped.ERROR_NETNAME_DELETED: raise ConnectionResetError(*exc.args) else: raise return self._register(ov, conn, finish_send)
def sendfile(self, sock, file, offset, count): self._register_with_iocp(sock) ov = _overlapped.Overlapped(NULL) offset_low = offset & 0xffff_ffff offset_high = (offset >> 32) & 0xffff_ffff ov.TransmitFile(sock.fileno(), msvcrt.get_osfhandle(file.fileno()), offset_low, offset_high, count, 0, 0) def finish_sendfile(trans, key, ov): try: return ov.getresult() except OSError as exc: if exc.winerror in (_overlapped.ERROR_NETNAME_DELETED, _overlapped.ERROR_OPERATION_ABORTED): raise ConnectionResetError(*exc.args) else: raise return self._register(ov, sock, finish_sendfile)
def recvfrom(self, conn, nbytes, flags=0): self._register_with_iocp(conn) ov = _overlapped.Overlapped(NULL) try: ov.WSARecvFrom(conn.fileno(), nbytes, flags) except BrokenPipeError: return self._result((b'', None)) def finish_recv(trans, key, ov): try: return ov.getresult() except OSError as exc: if exc.winerror in (_overlapped.ERROR_NETNAME_DELETED, _overlapped.ERROR_OPERATION_ABORTED): raise ConnectionResetError(*exc.args) else: raise return self._register(ov, conn, finish_recv)
def recv_into(self, conn, buf, flags=0): self._register_with_iocp(conn) ov = _overlapped.Overlapped(NULL) try: if isinstance(conn, socket.socket): ov.WSARecvInto(conn.fileno(), buf, flags) else: ov.ReadFileInto(conn.fileno(), buf) except BrokenPipeError: return self._result(b'') def finish_recv(trans, key, ov): try: return ov.getresult() except OSError as exc: if exc.winerror == _overlapped.ERROR_NETNAME_DELETED: raise ConnectionResetError(*exc.args) else: raise return self._register(ov, conn, finish_recv)
def connect(self, conn, address): self._register_with_iocp(conn) # The socket needs to be locally bound before we call ConnectEx(). try: _overlapped.BindLocal(conn.fileno(), conn.family) except OSError as e: if e.winerror != errno.WSAEINVAL: raise # Probably already locally bound; check using getsockname(). if conn.getsockname()[1] == 0: raise ov = _overlapped.Overlapped(NULL) ov.ConnectEx(conn.fileno(), address) def finish_connect(trans, key, ov): ov.getresult() # Use SO_UPDATE_CONNECT_CONTEXT so getsockname() etc work. conn.setsockopt(socket.SOL_SOCKET, _overlapped.SO_UPDATE_CONNECT_CONTEXT, 0) return conn return self._register(ov, conn, finish_connect)