Skip to content

Commit 95f57c3

Browse files
committed
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
1 parent 125c4a2 commit 95f57c3

File tree

6 files changed

+74
-43
lines changed

6 files changed

+74
-43
lines changed

lib/build-web/main.js

Lines changed: 3 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@ const domSummary = {
66
stepCount: document.getElementById("summaryStepCount"),
77
status: document.getElementById("summaryStatus"),
88
};
9-
let domButtonRebuild = document.getElementById("buttonRebuild");
9+
const domButtonRebuild = document.getElementById("buttonRebuild");
1010
const domStepList = document.getElementById("stepList");
1111
let domSteps = [];
1212

@@ -114,13 +114,7 @@ function hello(
114114
steps_len,
115115
build_status,
116116
time_report,
117-
supports_recv,
118117
) {
119-
if (!supports_recv && domButtonRebuild) {
120-
domButtonRebuild.remove();
121-
domButtonRebuild = null;
122-
}
123-
124118
domSummary.stepCount.textContent = steps_len;
125119
updateBuildStatus(build_status);
126120
setConnectionStatus("", false);
@@ -167,15 +161,11 @@ function updateBuildStatus(s) {
167161
if (active) {
168162
domSummary.status.classList.add("status-running");
169163
domSummary.status.classList.remove("status-idle");
170-
if (domButtonRebuild) {
171-
domButtonRebuild.disabled = true;
172-
}
164+
domButtonRebuild.disabled = true;
173165
} else {
174166
domSummary.status.classList.remove("status-running");
175167
domSummary.status.classList.add("status-idle");
176-
if (domButtonRebuild) {
177-
domButtonRebuild.disabled = false;
178-
}
168+
domButtonRebuild.disabled = false;
179169
}
180170
if (reset_time_reports) {
181171
// Grey out and collapse all the time reports

lib/build-web/main.zig

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -30,7 +30,6 @@ const js = struct {
3030
steps_len: u32,
3131
status: abi.BuildStatus,
3232
time_report: bool,
33-
supports_recv: bool,
3433
) void;
3534
extern "core" fn updateBuildStatus(status: abi.BuildStatus) void;
3635
extern "core" fn updateStepStatus(step_idx: u32) void;
@@ -161,7 +160,7 @@ fn helloMessage(msg_bytes: []align(4) u8) Allocator.Error!void {
161160
step_list = steps;
162161
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);
165164
}
166165
fn statusUpdateMessage(msg_bytes: []u8) Allocator.Error!void {
167166
if (msg_bytes.len < @sizeOf(abi.StatusUpdate)) @panic("malformed StatusUpdate message");

lib/std/Build/WebServer.zig

Lines changed: 2 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -287,20 +287,14 @@ fn serveWebSocket(ws: *WebServer, sock: *http.Server.WebSocket) !noreturn {
287287
copy.* = @atomicLoad(u8, shared, .monotonic);
288288
}
289289

290-
// Calling WSARecvFrom on one thread while another calls WSASend deadlocks.
291-
// This functionality is disabled until std.net uses overlapped sockets on Windows.
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();
290+
const recv_thread = try std.Thread.spawn(.{}, recvWebSocketMessages, .{ ws, sock });
291+
defer recv_thread.join();
297292

298293
{
299294
const hello_header: abi.Hello = .{
300295
.status = prev_build_status,
301296
.flags = .{
302297
.time_report = ws.graph.time_report,
303-
.supports_recv = supports_recv,
304298
},
305299
.timestamp = ws.now(),
306300
.steps_len = @intCast(ws.all_steps.len),

lib/std/Build/abi.zig

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -103,9 +103,7 @@ pub const Hello = extern struct {
103103
pub const Flags = packed struct(u16) {
104104
/// Whether time reporting is enabled.
105105
time_report: bool,
106-
/// If this platform supports receiving messages from the client
107-
supports_recv: bool,
108-
_: u14 = 0,
106+
_: u15 = 0,
109107
};
110108
};
111109
/// WebSocket server->client.

lib/std/net.zig

Lines changed: 63 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -259,6 +259,7 @@ pub const Address = extern union {
259259
/// Sets SO_REUSEADDR and SO_REUSEPORT on POSIX.
260260
/// Sets SO_REUSEADDR on Windows, which is roughly equivalent.
261261
reuse_address: bool = false,
262+
/// Sets O_NONBLOCK.
262263
force_nonblocking: bool = false,
263264
};
264265

@@ -1998,11 +1999,8 @@ pub const Stream = struct {
19981999
return n;
19992000
}
20002001

2001-
fn streamBufs(r: *Reader, bufs: []windows.ws2_32.WSABUF) Error!u32 {
2002-
var n: u32 = undefined;
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()) {
2002+
fn handleRecvError(winsock_error: windows.ws2_32.WinsockError) Error!void {
2003+
switch (winsock_error) {
20062004
.WSAECONNRESET => return error.ConnectionResetByPeer,
20072005
.WSAEFAULT => unreachable, // a pointer is not completely contained in user address space.
20082006
.WSAEINPROGRESS, .WSAEINTR => unreachable, // deprecated and removed in WSA 2.2
@@ -2013,10 +2011,39 @@ pub const Stream = struct {
20132011
.WSAENOTCONN => return error.SocketNotConnected,
20142012
.WSAEWOULDBLOCK => return error.WouldBlock,
20152013
.WSANOTINITIALISED => unreachable, // WSAStartup must be called before this function
2016-
.WSA_IO_PENDING => unreachable, // not using overlapped I/O
2014+
.WSA_IO_PENDING => unreachable,
20172015
.WSA_OPERATION_ABORTED => unreachable, // not using overlapped I/O
20182016
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),
20192045
};
2046+
20202047
return n;
20212048
}
20222049
},
@@ -2136,10 +2163,8 @@ pub const Stream = struct {
21362163
return io_w.consume(n);
21372164
}
21382165

2139-
fn sendBufs(handle: Stream.Handle, bufs: []windows.ws2_32.WSABUF) Error!u32 {
2140-
var n: u32 = undefined;
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()) {
2166+
fn handleSendError(winsock_error: windows.ws2_32.WinsockError) Error!void {
2167+
switch (winsock_error) {
21432168
.WSAECONNABORTED => return error.ConnectionResetByPeer,
21442169
.WSAECONNRESET => return error.ConnectionResetByPeer,
21452170
.WSAEFAULT => unreachable, // a pointer is not completely contained in user address space.
@@ -2155,10 +2180,37 @@ pub const Stream = struct {
21552180
.WSAESHUTDOWN => unreachable, // cannot send on a socket after write shutdown
21562181
.WSAEWOULDBLOCK => return error.WouldBlock,
21572182
.WSANOTINITIALISED => unreachable, // WSAStartup must be called before this function
2158-
.WSA_IO_PENDING => unreachable, // not using overlapped I/O
2183+
.WSA_IO_PENDING => unreachable,
21592184
.WSA_OPERATION_ABORTED => unreachable, // not using overlapped I/O
21602185
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),
21612212
};
2213+
21622214
return n;
21632215
}
21642216
},

lib/std/posix.zig

Lines changed: 4 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -3615,13 +3615,11 @@ pub const SocketError = error{
36153615

36163616
pub fn socket(domain: u32, socket_type: u32, protocol: u32) SocketError!socket_t {
36173617
if (native_os == .windows) {
3618-
// NOTE: windows translates the SOCK.NONBLOCK/SOCK.CLOEXEC flags into
3619-
// windows-analogous operations
3618+
// These flags are not actually part of the Windows API, instead they are converted here for compatibility
36203619
const filtered_sock_type = socket_type & ~@as(u32, SOCK.NONBLOCK | SOCK.CLOEXEC);
3621-
const flags: u32 = if ((socket_type & SOCK.CLOEXEC) != 0)
3622-
windows.ws2_32.WSA_FLAG_NO_HANDLE_INHERIT
3623-
else
3624-
0;
3620+
var flags: u32 = windows.ws2_32.WSA_FLAG_OVERLAPPED;
3621+
if ((socket_type & SOCK.CLOEXEC) != 0) flags |= windows.ws2_32.WSA_FLAG_NO_HANDLE_INHERIT;
3622+
36253623
const rc = try windows.WSASocketW(
36263624
@bitCast(domain),
36273625
@bitCast(filtered_sock_type),

0 commit comments

Comments
 (0)