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
This commit is contained in:
@@ -6,7 +6,7 @@ const domSummary = {
|
|||||||
stepCount: document.getElementById("summaryStepCount"),
|
stepCount: document.getElementById("summaryStepCount"),
|
||||||
status: document.getElementById("summaryStatus"),
|
status: document.getElementById("summaryStatus"),
|
||||||
};
|
};
|
||||||
let domButtonRebuild = document.getElementById("buttonRebuild");
|
const domButtonRebuild = document.getElementById("buttonRebuild");
|
||||||
const domStepList = document.getElementById("stepList");
|
const domStepList = document.getElementById("stepList");
|
||||||
let domSteps = [];
|
let domSteps = [];
|
||||||
|
|
||||||
@@ -114,13 +114,7 @@ function hello(
|
|||||||
steps_len,
|
steps_len,
|
||||||
build_status,
|
build_status,
|
||||||
time_report,
|
time_report,
|
||||||
supports_recv,
|
|
||||||
) {
|
) {
|
||||||
if (!supports_recv && domButtonRebuild) {
|
|
||||||
domButtonRebuild.remove();
|
|
||||||
domButtonRebuild = null;
|
|
||||||
}
|
|
||||||
|
|
||||||
domSummary.stepCount.textContent = steps_len;
|
domSummary.stepCount.textContent = steps_len;
|
||||||
updateBuildStatus(build_status);
|
updateBuildStatus(build_status);
|
||||||
setConnectionStatus("", false);
|
setConnectionStatus("", false);
|
||||||
@@ -167,15 +161,11 @@ function updateBuildStatus(s) {
|
|||||||
if (active) {
|
if (active) {
|
||||||
domSummary.status.classList.add("status-running");
|
domSummary.status.classList.add("status-running");
|
||||||
domSummary.status.classList.remove("status-idle");
|
domSummary.status.classList.remove("status-idle");
|
||||||
if (domButtonRebuild) {
|
domButtonRebuild.disabled = true;
|
||||||
domButtonRebuild.disabled = true;
|
|
||||||
}
|
|
||||||
} else {
|
} else {
|
||||||
domSummary.status.classList.remove("status-running");
|
domSummary.status.classList.remove("status-running");
|
||||||
domSummary.status.classList.add("status-idle");
|
domSummary.status.classList.add("status-idle");
|
||||||
if (domButtonRebuild) {
|
domButtonRebuild.disabled = false;
|
||||||
domButtonRebuild.disabled = false;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
if (reset_time_reports) {
|
if (reset_time_reports) {
|
||||||
// Grey out and collapse all the time reports
|
// Grey out and collapse all the time reports
|
||||||
|
|||||||
@@ -30,7 +30,6 @@ const js = struct {
|
|||||||
steps_len: u32,
|
steps_len: u32,
|
||||||
status: abi.BuildStatus,
|
status: abi.BuildStatus,
|
||||||
time_report: bool,
|
time_report: bool,
|
||||||
supports_recv: bool,
|
|
||||||
) void;
|
) void;
|
||||||
extern "core" fn updateBuildStatus(status: abi.BuildStatus) void;
|
extern "core" fn updateBuildStatus(status: abi.BuildStatus) void;
|
||||||
extern "core" fn updateStepStatus(step_idx: u32) void;
|
extern "core" fn updateStepStatus(step_idx: u32) void;
|
||||||
@@ -161,7 +160,7 @@ fn helloMessage(msg_bytes: []align(4) u8) Allocator.Error!void {
|
|||||||
step_list = steps;
|
step_list = steps;
|
||||||
step_list_data = duped_step_name_data;
|
step_list_data = duped_step_name_data;
|
||||||
|
|
||||||
js.hello(step_list.len, hdr.status, hdr.flags.time_report, hdr.flags.supports_recv);
|
js.hello(step_list.len, hdr.status, hdr.flags.time_report);
|
||||||
}
|
}
|
||||||
fn statusUpdateMessage(msg_bytes: []u8) Allocator.Error!void {
|
fn statusUpdateMessage(msg_bytes: []u8) Allocator.Error!void {
|
||||||
if (msg_bytes.len < @sizeOf(abi.StatusUpdate)) @panic("malformed StatusUpdate message");
|
if (msg_bytes.len < @sizeOf(abi.StatusUpdate)) @panic("malformed StatusUpdate message");
|
||||||
|
|||||||
@@ -287,20 +287,14 @@ fn serveWebSocket(ws: *WebServer, sock: *http.Server.WebSocket) !noreturn {
|
|||||||
copy.* = @atomicLoad(u8, shared, .monotonic);
|
copy.* = @atomicLoad(u8, shared, .monotonic);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Calling WSARecvFrom on one thread while another calls WSASend deadlocks.
|
const recv_thread = try std.Thread.spawn(.{}, recvWebSocketMessages, .{ ws, sock });
|
||||||
// This functionality is disabled until std.net uses overlapped sockets on Windows.
|
defer recv_thread.join();
|
||||||
const supports_recv = builtin.os.tag != .windows;
|
|
||||||
const recv_thread = if (supports_recv)
|
|
||||||
try std.Thread.spawn(.{}, recvWebSocketMessages, .{ ws, sock })
|
|
||||||
else {};
|
|
||||||
defer if (supports_recv) recv_thread.join();
|
|
||||||
|
|
||||||
{
|
{
|
||||||
const hello_header: abi.Hello = .{
|
const hello_header: abi.Hello = .{
|
||||||
.status = prev_build_status,
|
.status = prev_build_status,
|
||||||
.flags = .{
|
.flags = .{
|
||||||
.time_report = ws.graph.time_report,
|
.time_report = ws.graph.time_report,
|
||||||
.supports_recv = supports_recv,
|
|
||||||
},
|
},
|
||||||
.timestamp = ws.now(),
|
.timestamp = ws.now(),
|
||||||
.steps_len = @intCast(ws.all_steps.len),
|
.steps_len = @intCast(ws.all_steps.len),
|
||||||
|
|||||||
@@ -103,9 +103,7 @@ pub const Hello = extern struct {
|
|||||||
pub const Flags = packed struct(u16) {
|
pub const Flags = packed struct(u16) {
|
||||||
/// Whether time reporting is enabled.
|
/// Whether time reporting is enabled.
|
||||||
time_report: bool,
|
time_report: bool,
|
||||||
/// If this platform supports receiving messages from the client
|
_: u15 = 0,
|
||||||
supports_recv: bool,
|
|
||||||
_: u14 = 0,
|
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
/// WebSocket server->client.
|
/// WebSocket server->client.
|
||||||
|
|||||||
@@ -259,6 +259,7 @@ pub const Address = extern union {
|
|||||||
/// Sets SO_REUSEADDR and SO_REUSEPORT on POSIX.
|
/// Sets SO_REUSEADDR and SO_REUSEPORT on POSIX.
|
||||||
/// Sets SO_REUSEADDR on Windows, which is roughly equivalent.
|
/// Sets SO_REUSEADDR on Windows, which is roughly equivalent.
|
||||||
reuse_address: bool = false,
|
reuse_address: bool = false,
|
||||||
|
/// Sets O_NONBLOCK.
|
||||||
force_nonblocking: bool = false,
|
force_nonblocking: bool = false,
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -1998,11 +1999,8 @@ pub const Stream = struct {
|
|||||||
return n;
|
return n;
|
||||||
}
|
}
|
||||||
|
|
||||||
fn streamBufs(r: *Reader, bufs: []windows.ws2_32.WSABUF) Error!u32 {
|
fn handleRecvError(winsock_error: windows.ws2_32.WinsockError) Error!void {
|
||||||
var n: u32 = undefined;
|
switch (winsock_error) {
|
||||||
var flags: u32 = 0;
|
|
||||||
const rc = windows.ws2_32.WSARecvFrom(r.net_stream.handle, bufs.ptr, @intCast(bufs.len), &n, &flags, null, null, null, null);
|
|
||||||
if (rc != 0) switch (windows.ws2_32.WSAGetLastError()) {
|
|
||||||
.WSAECONNRESET => return error.ConnectionResetByPeer,
|
.WSAECONNRESET => return error.ConnectionResetByPeer,
|
||||||
.WSAEFAULT => unreachable, // a pointer is not completely contained in user address space.
|
.WSAEFAULT => unreachable, // a pointer is not completely contained in user address space.
|
||||||
.WSAEINPROGRESS, .WSAEINTR => unreachable, // deprecated and removed in WSA 2.2
|
.WSAEINPROGRESS, .WSAEINTR => unreachable, // deprecated and removed in WSA 2.2
|
||||||
@@ -2013,10 +2011,39 @@ pub const Stream = struct {
|
|||||||
.WSAENOTCONN => return error.SocketNotConnected,
|
.WSAENOTCONN => return error.SocketNotConnected,
|
||||||
.WSAEWOULDBLOCK => return error.WouldBlock,
|
.WSAEWOULDBLOCK => return error.WouldBlock,
|
||||||
.WSANOTINITIALISED => unreachable, // WSAStartup must be called before this function
|
.WSANOTINITIALISED => unreachable, // WSAStartup must be called before this function
|
||||||
.WSA_IO_PENDING => unreachable, // not using overlapped I/O
|
.WSA_IO_PENDING => unreachable,
|
||||||
.WSA_OPERATION_ABORTED => unreachable, // not using overlapped I/O
|
.WSA_OPERATION_ABORTED => unreachable, // not using overlapped I/O
|
||||||
else => |err| return windows.unexpectedWSAError(err),
|
else => |err| return windows.unexpectedWSAError(err),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn streamBufs(r: *Reader, bufs: []windows.ws2_32.WSABUF) Error!u32 {
|
||||||
|
var flags: u32 = 0;
|
||||||
|
var overlapped: windows.OVERLAPPED = std.mem.zeroes(windows.OVERLAPPED);
|
||||||
|
|
||||||
|
var n: u32 = undefined;
|
||||||
|
if (windows.ws2_32.WSARecv(
|
||||||
|
r.net_stream.handle,
|
||||||
|
bufs.ptr,
|
||||||
|
@intCast(bufs.len),
|
||||||
|
&n,
|
||||||
|
&flags,
|
||||||
|
&overlapped,
|
||||||
|
null,
|
||||||
|
) == windows.ws2_32.SOCKET_ERROR) switch (windows.ws2_32.WSAGetLastError()) {
|
||||||
|
.WSA_IO_PENDING => {
|
||||||
|
var result_flags: u32 = undefined;
|
||||||
|
if (windows.ws2_32.WSAGetOverlappedResult(
|
||||||
|
r.net_stream.handle,
|
||||||
|
&overlapped,
|
||||||
|
&n,
|
||||||
|
windows.TRUE,
|
||||||
|
&result_flags,
|
||||||
|
) == windows.FALSE) try handleRecvError(windows.ws2_32.WSAGetLastError());
|
||||||
|
},
|
||||||
|
else => |winsock_error| try handleRecvError(winsock_error),
|
||||||
};
|
};
|
||||||
|
|
||||||
return n;
|
return n;
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
@@ -2136,10 +2163,8 @@ pub const Stream = struct {
|
|||||||
return io_w.consume(n);
|
return io_w.consume(n);
|
||||||
}
|
}
|
||||||
|
|
||||||
fn sendBufs(handle: Stream.Handle, bufs: []windows.ws2_32.WSABUF) Error!u32 {
|
fn handleSendError(winsock_error: windows.ws2_32.WinsockError) Error!void {
|
||||||
var n: u32 = undefined;
|
switch (winsock_error) {
|
||||||
const rc = windows.ws2_32.WSASend(handle, bufs.ptr, @intCast(bufs.len), &n, 0, null, null);
|
|
||||||
if (rc == windows.ws2_32.SOCKET_ERROR) switch (windows.ws2_32.WSAGetLastError()) {
|
|
||||||
.WSAECONNABORTED => return error.ConnectionResetByPeer,
|
.WSAECONNABORTED => return error.ConnectionResetByPeer,
|
||||||
.WSAECONNRESET => return error.ConnectionResetByPeer,
|
.WSAECONNRESET => return error.ConnectionResetByPeer,
|
||||||
.WSAEFAULT => unreachable, // a pointer is not completely contained in user address space.
|
.WSAEFAULT => unreachable, // a pointer is not completely contained in user address space.
|
||||||
@@ -2155,10 +2180,37 @@ pub const Stream = struct {
|
|||||||
.WSAESHUTDOWN => unreachable, // cannot send on a socket after write shutdown
|
.WSAESHUTDOWN => unreachable, // cannot send on a socket after write shutdown
|
||||||
.WSAEWOULDBLOCK => return error.WouldBlock,
|
.WSAEWOULDBLOCK => return error.WouldBlock,
|
||||||
.WSANOTINITIALISED => unreachable, // WSAStartup must be called before this function
|
.WSANOTINITIALISED => unreachable, // WSAStartup must be called before this function
|
||||||
.WSA_IO_PENDING => unreachable, // not using overlapped I/O
|
.WSA_IO_PENDING => unreachable,
|
||||||
.WSA_OPERATION_ABORTED => unreachable, // not using overlapped I/O
|
.WSA_OPERATION_ABORTED => unreachable, // not using overlapped I/O
|
||||||
else => |err| return windows.unexpectedWSAError(err),
|
else => |err| return windows.unexpectedWSAError(err),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn sendBufs(handle: Stream.Handle, bufs: []windows.ws2_32.WSABUF) Error!u32 {
|
||||||
|
var n: u32 = undefined;
|
||||||
|
var overlapped: windows.OVERLAPPED = std.mem.zeroes(windows.OVERLAPPED);
|
||||||
|
if (windows.ws2_32.WSASend(
|
||||||
|
handle,
|
||||||
|
bufs.ptr,
|
||||||
|
@intCast(bufs.len),
|
||||||
|
&n,
|
||||||
|
0,
|
||||||
|
&overlapped,
|
||||||
|
null,
|
||||||
|
) == windows.ws2_32.SOCKET_ERROR) switch (windows.ws2_32.WSAGetLastError()) {
|
||||||
|
.WSA_IO_PENDING => {
|
||||||
|
var result_flags: u32 = undefined;
|
||||||
|
if (windows.ws2_32.WSAGetOverlappedResult(
|
||||||
|
handle,
|
||||||
|
&overlapped,
|
||||||
|
&n,
|
||||||
|
windows.TRUE,
|
||||||
|
&result_flags,
|
||||||
|
) == windows.FALSE) try handleSendError(windows.ws2_32.WSAGetLastError());
|
||||||
|
},
|
||||||
|
else => |winsock_error| try handleSendError(winsock_error),
|
||||||
};
|
};
|
||||||
|
|
||||||
return n;
|
return n;
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -3615,13 +3615,11 @@ pub const SocketError = error{
|
|||||||
|
|
||||||
pub fn socket(domain: u32, socket_type: u32, protocol: u32) SocketError!socket_t {
|
pub fn socket(domain: u32, socket_type: u32, protocol: u32) SocketError!socket_t {
|
||||||
if (native_os == .windows) {
|
if (native_os == .windows) {
|
||||||
// NOTE: windows translates the SOCK.NONBLOCK/SOCK.CLOEXEC flags into
|
// These flags are not actually part of the Windows API, instead they are converted here for compatibility
|
||||||
// windows-analogous operations
|
|
||||||
const filtered_sock_type = socket_type & ~@as(u32, SOCK.NONBLOCK | SOCK.CLOEXEC);
|
const filtered_sock_type = socket_type & ~@as(u32, SOCK.NONBLOCK | SOCK.CLOEXEC);
|
||||||
const flags: u32 = if ((socket_type & SOCK.CLOEXEC) != 0)
|
var flags: u32 = windows.ws2_32.WSA_FLAG_OVERLAPPED;
|
||||||
windows.ws2_32.WSA_FLAG_NO_HANDLE_INHERIT
|
if ((socket_type & SOCK.CLOEXEC) != 0) flags |= windows.ws2_32.WSA_FLAG_NO_HANDLE_INHERIT;
|
||||||
else
|
|
||||||
0;
|
|
||||||
const rc = try windows.WSASocketW(
|
const rc = try windows.WSASocketW(
|
||||||
@bitCast(domain),
|
@bitCast(domain),
|
||||||
@bitCast(filtered_sock_type),
|
@bitCast(filtered_sock_type),
|
||||||
|
|||||||
Reference in New Issue
Block a user