authorgravatar for kcbanner@gmail.comCasey Banner <kcbanner@gmail.com> 2025-08-09 18:48:15-04:00
committergravatar for kcbanner@gmail.comCasey Banner <kcbanner@gmail.com> 2025-08-09 18:48:15-04:00
log95f57c3369181123aa7ebd4a9654fc489f8bb4fd
tree4faf083d4cc96f9ce732d91c5f280744ba986d17
parent125c4a265aa7cfe82b3fc0c1ac7de75a07315411

net: Always set WSA_FLAG_OVERLAPPED when creating Windows sockets. Rework send and receive logic to use overlapped I/O.

build-web: Remove the now-redundant supports_recv logic

6 files changed, 74 insertions(+), 43 deletions(-)

lib/build-web/main.js+3-13
...@@ -6,7 +6,7 @@ const domSummary = {...@@ -6,7 +6,7 @@ const domSummary = {
6 stepCount: document.getElementById("summaryStepCount"),6 stepCount: document.getElementById("summaryStepCount"),
7 status: document.getElementById("summaryStatus"),7 status: document.getElementById("summaryStatus"),
8};8};
9let domButtonRebuild = document.getElementById("buttonRebuild");9const domButtonRebuild = document.getElementById("buttonRebuild");
10const domStepList = document.getElementById("stepList");10const domStepList = document.getElementById("stepList");
11let domSteps = [];11let domSteps = [];
1212
...@@ -114,13 +114,7 @@ function hello(...@@ -114,13 +114,7 @@ function hello(
114 steps_len,114 steps_len,
115 build_status,115 build_status,
116 time_report,116 time_report,
117 supports_recv,
118) {117) {
119 if (!supports_recv && domButtonRebuild) {
120 domButtonRebuild.remove();
121 domButtonRebuild = null;
122 }
123
124 domSummary.stepCount.textContent = steps_len;118 domSummary.stepCount.textContent = steps_len;
125 updateBuildStatus(build_status);119 updateBuildStatus(build_status);
126 setConnectionStatus("", false);120 setConnectionStatus("", false);
...@@ -167,15 +161,11 @@ function updateBuildStatus(s) {...@@ -167,15 +161,11 @@ function updateBuildStatus(s) {
167 if (active) {161 if (active) {
168 domSummary.status.classList.add("status-running");162 domSummary.status.classList.add("status-running");
169 domSummary.status.classList.remove("status-idle");163 domSummary.status.classList.remove("status-idle");
170 if (domButtonRebuild) {164 domButtonRebuild.disabled = true;
171 domButtonRebuild.disabled = true;
172 }
173 } else {165 } else {
174 domSummary.status.classList.remove("status-running");166 domSummary.status.classList.remove("status-running");
175 domSummary.status.classList.add("status-idle");167 domSummary.status.classList.add("status-idle");
176 if (domButtonRebuild) {168 domButtonRebuild.disabled = false;
177 domButtonRebuild.disabled = false;
178 }
179 }169 }
180 if (reset_time_reports) {170 if (reset_time_reports) {
181 // Grey out and collapse all the time reports171 // Grey out and collapse all the time reports
lib/build-web/main.zig+1-2
...@@ -30,7 +30,6 @@ const js = struct {...@@ -30,7 +30,6 @@ const js = struct {
30 steps_len: u32,30 steps_len: u32,
31 status: abi.BuildStatus,31 status: abi.BuildStatus,
32 time_report: bool,32 time_report: bool,
33 supports_recv: bool,
34 ) void;33 ) void;
35 extern "core" fn updateBuildStatus(status: abi.BuildStatus) void;34 extern "core" fn updateBuildStatus(status: abi.BuildStatus) void;
36 extern "core" fn updateStepStatus(step_idx: u32) void;35 extern "core" fn updateStepStatus(step_idx: u32) void;
...@@ -161,7 +160,7 @@ fn helloMessage(msg_bytes: []align(4) u8) Allocator.Error!void {...@@ -161,7 +160,7 @@ fn helloMessage(msg_bytes: []align(4) u8) Allocator.Error!void {
161 step_list = steps;160 step_list = steps;
162 step_list_data = duped_step_name_data;161 step_list_data = duped_step_name_data;
163162
164 js.hello(step_list.len, hdr.status, hdr.flags.time_report, hdr.flags.supports_recv);163 js.hello(step_list.len, hdr.status, hdr.flags.time_report);
165}164}
166fn statusUpdateMessage(msg_bytes: []u8) Allocator.Error!void {165fn statusUpdateMessage(msg_bytes: []u8) Allocator.Error!void {
167 if (msg_bytes.len < @sizeOf(abi.StatusUpdate)) @panic("malformed StatusUpdate message");166 if (msg_bytes.len < @sizeOf(abi.StatusUpdate)) @panic("malformed StatusUpdate message");
lib/std/Build/WebServer.zig+2-8
...@@ -287,20 +287,14 @@ fn serveWebSocket(ws: *WebServer, sock: *http.Server.WebSocket) !noreturn {...@@ -287,20 +287,14 @@ fn serveWebSocket(ws: *WebServer, sock: *http.Server.WebSocket) !noreturn {
287 copy.* = @atomicLoad(u8, shared, .monotonic);287 copy.* = @atomicLoad(u8, shared, .monotonic);
288 }288 }
289289
290 // Calling WSARecvFrom on one thread while another calls WSASend deadlocks.290 const recv_thread = try std.Thread.spawn(.{}, recvWebSocketMessages, .{ ws, sock });
291 // This functionality is disabled until std.net uses overlapped sockets on Windows.291 defer recv_thread.join();
292 const supports_recv = builtin.os.tag != .windows;
293 const recv_thread = if (supports_recv)
294 try std.Thread.spawn(.{}, recvWebSocketMessages, .{ ws, sock })
295 else {};
296 defer if (supports_recv) recv_thread.join();
297292
298 {293 {
299 const hello_header: abi.Hello = .{294 const hello_header: abi.Hello = .{
300 .status = prev_build_status,295 .status = prev_build_status,
301 .flags = .{296 .flags = .{
302 .time_report = ws.graph.time_report,297 .time_report = ws.graph.time_report,
303 .supports_recv = supports_recv,
304 },298 },
305 .timestamp = ws.now(),299 .timestamp = ws.now(),
306 .steps_len = @intCast(ws.all_steps.len),300 .steps_len = @intCast(ws.all_steps.len),
lib/std/Build/abi.zig+1-3
...@@ -103,9 +103,7 @@ pub const Hello = extern struct {...@@ -103,9 +103,7 @@ pub const Hello = extern struct {
103 pub const Flags = packed struct(u16) {103 pub const Flags = packed struct(u16) {
104 /// Whether time reporting is enabled.104 /// Whether time reporting is enabled.
105 time_report: bool,105 time_report: bool,
106 /// If this platform supports receiving messages from the client106 _: u15 = 0,
107 supports_recv: bool,
108 _: u14 = 0,
109 };107 };
110};108};
111/// WebSocket server->client.109/// WebSocket server->client.
lib/std/net.zig+63-11
...@@ -259,6 +259,7 @@ pub const Address = extern union {...@@ -259,6 +259,7 @@ pub const Address = extern union {
259 /// Sets SO_REUSEADDR and SO_REUSEPORT on POSIX.259 /// Sets SO_REUSEADDR and SO_REUSEPORT on POSIX.
260 /// Sets SO_REUSEADDR on Windows, which is roughly equivalent.260 /// Sets SO_REUSEADDR on Windows, which is roughly equivalent.
261 reuse_address: bool = false,261 reuse_address: bool = false,
262 /// Sets O_NONBLOCK.
262 force_nonblocking: bool = false,263 force_nonblocking: bool = false,
263 };264 };
264265
...@@ -1998,11 +1999,8 @@ pub const Stream = struct {...@@ -1998,11 +1999,8 @@ pub const Stream = struct {
1998 return n;1999 return n;
1999 }2000 }
20002001
2001 fn streamBufs(r: *Reader, bufs: []windows.ws2_32.WSABUF) Error!u32 {2002 fn handleRecvError(winsock_error: windows.ws2_32.WinsockError) Error!void {
2002 var n: u32 = undefined;2003 switch (winsock_error) {
2003 var flags: u32 = 0;
2004 const rc = windows.ws2_32.WSARecvFrom(r.net_stream.handle, bufs.ptr, @intCast(bufs.len), &n, &flags, null, null, null, null);
2005 if (rc != 0) switch (windows.ws2_32.WSAGetLastError()) {
2006 .WSAECONNRESET => return error.ConnectionResetByPeer,2004 .WSAECONNRESET => return error.ConnectionResetByPeer,
2007 .WSAEFAULT => unreachable, // a pointer is not completely contained in user address space.2005 .WSAEFAULT => unreachable, // a pointer is not completely contained in user address space.
2008 .WSAEINPROGRESS, .WSAEINTR => unreachable, // deprecated and removed in WSA 2.22006 .WSAEINPROGRESS, .WSAEINTR => unreachable, // deprecated and removed in WSA 2.2
...@@ -2013,10 +2011,39 @@ pub const Stream = struct {...@@ -2013,10 +2011,39 @@ pub const Stream = struct {
2013 .WSAENOTCONN => return error.SocketNotConnected,2011 .WSAENOTCONN => return error.SocketNotConnected,
2014 .WSAEWOULDBLOCK => return error.WouldBlock,2012 .WSAEWOULDBLOCK => return error.WouldBlock,
2015 .WSANOTINITIALISED => unreachable, // WSAStartup must be called before this function2013 .WSANOTINITIALISED => unreachable, // WSAStartup must be called before this function
2016 .WSA_IO_PENDING => unreachable, // not using overlapped I/O2014 .WSA_IO_PENDING => unreachable,
2017 .WSA_OPERATION_ABORTED => unreachable, // not using overlapped I/O2015 .WSA_OPERATION_ABORTED => unreachable, // not using overlapped I/O
2018 else => |err| return windows.unexpectedWSAError(err),2016 else => |err| return windows.unexpectedWSAError(err),
2017 }
2018 }
2019
2020 fn streamBufs(r: *Reader, bufs: []windows.ws2_32.WSABUF) Error!u32 {
2021 var flags: u32 = 0;
2022 var overlapped: windows.OVERLAPPED = std.mem.zeroes(windows.OVERLAPPED);
2023
2024 var n: u32 = undefined;
2025 if (windows.ws2_32.WSARecv(
2026 r.net_stream.handle,
2027 bufs.ptr,
2028 @intCast(bufs.len),
2029 &n,
2030 &flags,
2031 &overlapped,
2032 null,
2033 ) == windows.ws2_32.SOCKET_ERROR) switch (windows.ws2_32.WSAGetLastError()) {
2034 .WSA_IO_PENDING => {
2035 var result_flags: u32 = undefined;
2036 if (windows.ws2_32.WSAGetOverlappedResult(
2037 r.net_stream.handle,
2038 &overlapped,
2039 &n,
2040 windows.TRUE,
2041 &result_flags,
2042 ) == windows.FALSE) try handleRecvError(windows.ws2_32.WSAGetLastError());
2043 },
2044 else => |winsock_error| try handleRecvError(winsock_error),
2019 };2045 };
2046
2020 return n;2047 return n;
2021 }2048 }
2022 },2049 },
...@@ -2136,10 +2163,8 @@ pub const Stream = struct {...@@ -2136,10 +2163,8 @@ pub const Stream = struct {
2136 return io_w.consume(n);2163 return io_w.consume(n);
2137 }2164 }
21382165
2139 fn sendBufs(handle: Stream.Handle, bufs: []windows.ws2_32.WSABUF) Error!u32 {2166 fn handleSendError(winsock_error: windows.ws2_32.WinsockError) Error!void {
2140 var n: u32 = undefined;2167 switch (winsock_error) {
2141 const rc = windows.ws2_32.WSASend(handle, bufs.ptr, @intCast(bufs.len), &n, 0, null, null);
2142 if (rc == windows.ws2_32.SOCKET_ERROR) switch (windows.ws2_32.WSAGetLastError()) {
2143 .WSAECONNABORTED => return error.ConnectionResetByPeer,2168 .WSAECONNABORTED => return error.ConnectionResetByPeer,
2144 .WSAECONNRESET => return error.ConnectionResetByPeer,2169 .WSAECONNRESET => return error.ConnectionResetByPeer,
2145 .WSAEFAULT => unreachable, // a pointer is not completely contained in user address space.2170 .WSAEFAULT => unreachable, // a pointer is not completely contained in user address space.
...@@ -2155,10 +2180,37 @@ pub const Stream = struct {...@@ -2155,10 +2180,37 @@ pub const Stream = struct {
2155 .WSAESHUTDOWN => unreachable, // cannot send on a socket after write shutdown2180 .WSAESHUTDOWN => unreachable, // cannot send on a socket after write shutdown
2156 .WSAEWOULDBLOCK => return error.WouldBlock,2181 .WSAEWOULDBLOCK => return error.WouldBlock,
2157 .WSANOTINITIALISED => unreachable, // WSAStartup must be called before this function2182 .WSANOTINITIALISED => unreachable, // WSAStartup must be called before this function
2158 .WSA_IO_PENDING => unreachable, // not using overlapped I/O2183 .WSA_IO_PENDING => unreachable,
2159 .WSA_OPERATION_ABORTED => unreachable, // not using overlapped I/O2184 .WSA_OPERATION_ABORTED => unreachable, // not using overlapped I/O
2160 else => |err| return windows.unexpectedWSAError(err),2185 else => |err| return windows.unexpectedWSAError(err),
2186 }
2187 }
2188
2189 fn sendBufs(handle: Stream.Handle, bufs: []windows.ws2_32.WSABUF) Error!u32 {
2190 var n: u32 = undefined;
2191 var overlapped: windows.OVERLAPPED = std.mem.zeroes(windows.OVERLAPPED);
2192 if (windows.ws2_32.WSASend(
2193 handle,
2194 bufs.ptr,
2195 @intCast(bufs.len),
2196 &n,
2197 0,
2198 &overlapped,
2199 null,
2200 ) == windows.ws2_32.SOCKET_ERROR) switch (windows.ws2_32.WSAGetLastError()) {
2201 .WSA_IO_PENDING => {
2202 var result_flags: u32 = undefined;
2203 if (windows.ws2_32.WSAGetOverlappedResult(
2204 handle,
2205 &overlapped,
2206 &n,
2207 windows.TRUE,
2208 &result_flags,
2209 ) == windows.FALSE) try handleSendError(windows.ws2_32.WSAGetLastError());
2210 },
2211 else => |winsock_error| try handleSendError(winsock_error),
2161 };2212 };
2213
2162 return n;2214 return n;
2163 }2215 }
2164 },2216 },
lib/std/posix.zig+4-6
...@@ -3615,13 +3615,11 @@ pub const SocketError = error{...@@ -3615,13 +3615,11 @@ pub const SocketError = error{
36153615
3616pub fn socket(domain: u32, socket_type: u32, protocol: u32) SocketError!socket_t {3616pub fn socket(domain: u32, socket_type: u32, protocol: u32) SocketError!socket_t {
3617 if (native_os == .windows) {3617 if (native_os == .windows) {
3618 // NOTE: windows translates the SOCK.NONBLOCK/SOCK.CLOEXEC flags into3618 // These flags are not actually part of the Windows API, instead they are converted here for compatibility
3619 // windows-analogous operations
3620 const filtered_sock_type = socket_type & ~@as(u32, SOCK.NONBLOCK | SOCK.CLOEXEC);3619 const filtered_sock_type = socket_type & ~@as(u32, SOCK.NONBLOCK | SOCK.CLOEXEC);
3621 const flags: u32 = if ((socket_type & SOCK.CLOEXEC) != 0)3620 var flags: u32 = windows.ws2_32.WSA_FLAG_OVERLAPPED;
3622 windows.ws2_32.WSA_FLAG_NO_HANDLE_INHERIT3621 if ((socket_type & SOCK.CLOEXEC) != 0) flags |= windows.ws2_32.WSA_FLAG_NO_HANDLE_INHERIT;
3623 else3622
3624 0;
3625 const rc = try windows.WSASocketW(3623 const rc = try windows.WSASocketW(
3626 @bitCast(domain),3624 @bitCast(domain),
3627 @bitCast(filtered_sock_type),3625 @bitCast(filtered_sock_type),