From 0b5179a231c3f639f2fe3f2cafffa8b2cfd02482 Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Mon, 20 Oct 2025 22:50:30 -0700 Subject: [PATCH] std.Io.Threaded: implement netAccept for Windows --- lib/std/Io/Threaded.zig | 34 +++++++++++++++++++++++++++++++++- lib/std/os/windows.zig | 5 ----- lib/std/posix.zig | 41 +---------------------------------------- 3 files changed, 34 insertions(+), 46 deletions(-) diff --git a/lib/std/Io/Threaded.zig b/lib/std/Io/Threaded.zig index e03440e9d3..b96136d151 100644 --- a/lib/std/Io/Threaded.zig +++ b/lib/std/Io/Threaded.zig @@ -244,7 +244,7 @@ pub fn io(t: *Threaded) Io { }, .netListenUnix = netListenUnix, .netAccept = switch (builtin.os.tag) { - .windows => @panic("TODO"), + .windows => netAcceptWindows, else => netAcceptPosix, }, .netBindIp = switch (builtin.os.tag) { @@ -3288,6 +3288,38 @@ fn netAcceptPosix(userdata: ?*anyopaque, listen_fd: net.Socket.Handle) net.Serve } }; } +fn netAcceptWindows(userdata: ?*anyopaque, listen_handle: net.Socket.Handle) net.Server.AcceptError!net.Stream { + if (!have_networking) return error.NetworkDown; + const t: *Threaded = @ptrCast(@alignCast(userdata)); + var storage: WsaAddress = undefined; + var addr_len: i32 = @sizeOf(WsaAddress); + while (true) { + try t.checkCancel(); + const rc = ws2_32.accept(listen_handle, &storage.any, &addr_len); + if (rc != windows.ws2_32.INVALID_SOCKET) return .{ .socket = .{ + .handle = rc, + .address = addressFromWsa(&storage), + } }; + switch (windows.ws2_32.WSAGetLastError()) { + .EINTR => continue, + .ECANCELLED, .E_CANCELLED => return error.Canceled, + .NOTINITIALISED => { + try initializeWsa(t); + continue; + }, + .ECONNRESET => return error.ConnectionAborted, + .EFAULT => |err| return wsaErrorBug(err), + .ENOTSOCK => |err| return wsaErrorBug(err), + .EINVAL => |err| return wsaErrorBug(err), + .EMFILE => return error.ProcessFdQuotaExceeded, + .ENETDOWN => return error.NetworkDown, + .ENOBUFS => return error.SystemResources, + .EOPNOTSUPP => |err| return wsaErrorBug(err), + else => |err| return windows.unexpectedWSAError(err), + } + } +} + fn netReadPosix(userdata: ?*anyopaque, fd: net.Socket.Handle, data: [][]u8) net.Stream.Reader.Error!usize { const t: *Threaded = @ptrCast(@alignCast(userdata)); diff --git a/lib/std/os/windows.zig b/lib/std/os/windows.zig index 7609612c26..ad02698354 100644 --- a/lib/std/os/windows.zig +++ b/lib/std/os/windows.zig @@ -1574,11 +1574,6 @@ pub fn GetFileAttributesW(lpFileName: [*:0]const u16) GetFileAttributesError!DWO return rc; } -pub fn accept(s: ws2_32.SOCKET, name: ?*ws2_32.sockaddr, namelen: ?*ws2_32.socklen_t) ws2_32.SOCKET { - assert((name == null) == (namelen == null)); - return ws2_32.accept(s, name, @as(?*i32, @ptrCast(namelen))); -} - pub fn getpeername(s: ws2_32.SOCKET, name: *ws2_32.sockaddr, namelen: *ws2_32.socklen_t) i32 { return ws2_32.getpeername(s, name, @as(*i32, @ptrCast(namelen))); } diff --git a/lib/std/posix.zig b/lib/std/posix.zig index 04e949036e..dd0ad16695 100644 --- a/lib/std/posix.zig +++ b/lib/std/posix.zig @@ -3471,31 +3471,10 @@ pub fn listen(sock: socket_t, backlog: u31) ListenError!void { pub const AcceptError = std.Io.net.Server.AcceptError; -/// Accept a connection on a socket. -/// If `sockfd` is opened in non blocking mode, the function will -/// return error.WouldBlock when EAGAIN is received. pub fn accept( - /// This argument is a socket that has been created with `socket`, bound to a local address - /// with `bind`, and is listening for connections after a `listen`. sock: socket_t, - /// This argument is a pointer to a sockaddr structure. This structure is filled in with the - /// address of the peer socket, as known to the communications layer. The exact format of the - /// address returned addr is determined by the socket's address family (see `socket` and the - /// respective protocol man pages). addr: ?*sockaddr, - /// This argument is a value-result argument: the caller must initialize it to contain the - /// size (in bytes) of the structure pointed to by addr; on return it will contain the actual size - /// of the peer address. - /// - /// The returned address is truncated if the buffer provided is too small; in this case, `addr_size` - /// will return a value greater than was supplied to the call. addr_size: ?*socklen_t, - /// The following values can be bitwise ORed in flags to obtain different behavior: - /// * `SOCK.NONBLOCK` - Set the `NONBLOCK` file status flag on the open file description (see `open`) - /// referred to by the new file descriptor. Using this flag saves extra calls to `fcntl` to achieve - /// the same result. - /// * `SOCK.CLOEXEC` - Set the close-on-exec (`FD_CLOEXEC`) flag on the new file descriptor. See the - /// description of the `CLOEXEC` flag in `open` for reasons why this may be useful. flags: u32, ) AcceptError!socket_t { const have_accept4 = !(builtin.target.os.tag.isDarwin() or native_os == .windows or native_os == .haiku); @@ -3504,29 +3483,11 @@ pub fn accept( const accepted_sock: socket_t = while (true) { const rc = if (have_accept4) system.accept4(sock, addr, addr_size, flags) - else if (native_os == .windows) - windows.accept(sock, addr, addr_size) else system.accept(sock, addr, addr_size); if (native_os == .windows) { - if (rc == windows.ws2_32.INVALID_SOCKET) { - switch (windows.ws2_32.WSAGetLastError()) { - .NOTINITIALISED => unreachable, // not initialized WSA - .ECONNRESET => return error.ConnectionResetByPeer, - .EFAULT => unreachable, - .ENOTSOCK => return error.FileDescriptorNotASocket, - .EINVAL => return error.SocketNotListening, - .EMFILE => return error.ProcessFdQuotaExceeded, - .ENETDOWN => return error.NetworkDown, - .ENOBUFS => return error.FileDescriptorNotASocket, - .EOPNOTSUPP => return error.OperationNotSupported, - .EWOULDBLOCK => return error.WouldBlock, - else => |err| return windows.unexpectedWSAError(err), - } - } else { - break rc; - } + @compileError("use std.Io instead"); } else { switch (errno(rc)) { .SUCCESS => break @intCast(rc),