zig

fork of https://codeberg.org/ziglang/zig
Log | Files | Refs | README | LICENSE

commit 125c4a265aa7cfe82b3fc0c1ac7de75a07315411 (tree)
parent 5f7a0bbabfde4eeb0ff4f40f0942ef710b6104a1
Author: kcbanner <kcbanner@gmail.com>
Date:   Sat,  9 Aug 2025 15:57:15 -0400

Fix `respondWebSocket`, enable --webui on Windows

This commit re-enables the --webui functionality on windows, with the caveat that rebuild functionality is still disabled (due to deadlocks caused by reading to / writing from the same non-overlapped socket on multiple threads). I updated the UI to be aware of this, and hide the `Rebuild` button.

http.Server: Remove incorrect advance() call. This was causing browsers to disconnect the websocket, as we were sending undefined bytes.
build.WebServer: Re-enable on windows, but disable functionality that requires receiving messages from the client
build-web: Show total times in tables

Diffstat:
Mlib/build-web/index.html | 2++
Mlib/build-web/main.js | 16+++++++++++++---
Mlib/build-web/main.zig | 3++-
Mlib/build-web/time_report.zig | 4++++
Mlib/std/Build/WebServer.zig | 19++++++++-----------
Mlib/std/Build/abi.zig | 4+++-
Mlib/std/http/Server.zig | 1-
7 files changed, 32 insertions(+), 17 deletions(-)

diff --git a/lib/build-web/index.html b/lib/build-web/index.html @@ -105,6 +105,7 @@ <th scope="col">Semantic Analysis</th> <th scope="col">Code Generation</th> <th scope="col">Linking</th> + <th scope="col">Total</th> </tr> </thead> <!-- HTML does not allow placing a 'slot' inside of a 'tbody' for backwards-compatibility @@ -125,6 +126,7 @@ <th scope="col">Semantic Analysis</th> <th scope="col">Code Generation</th> <th scope="col">Linking</th> + <th scope="col">Total</th> </tr> </thead> <!-- HTML does not allow placing a 'slot' inside of a 'tbody' for backwards-compatibility diff --git a/lib/build-web/main.js b/lib/build-web/main.js @@ -6,7 +6,7 @@ const domSummary = { stepCount: document.getElementById("summaryStepCount"), status: document.getElementById("summaryStatus"), }; -const domButtonRebuild = document.getElementById("buttonRebuild"); +let domButtonRebuild = document.getElementById("buttonRebuild"); const domStepList = document.getElementById("stepList"); let domSteps = []; @@ -114,7 +114,13 @@ function hello( steps_len, build_status, time_report, + supports_recv, ) { + if (!supports_recv && domButtonRebuild) { + domButtonRebuild.remove(); + domButtonRebuild = null; + } + domSummary.stepCount.textContent = steps_len; updateBuildStatus(build_status); setConnectionStatus("", false); @@ -161,11 +167,15 @@ function updateBuildStatus(s) { if (active) { domSummary.status.classList.add("status-running"); domSummary.status.classList.remove("status-idle"); - domButtonRebuild.disabled = true; + if (domButtonRebuild) { + domButtonRebuild.disabled = true; + } } else { domSummary.status.classList.remove("status-running"); domSummary.status.classList.add("status-idle"); - domButtonRebuild.disabled = false; + if (domButtonRebuild) { + domButtonRebuild.disabled = false; + } } if (reset_time_reports) { // Grey out and collapse all the time reports diff --git a/lib/build-web/main.zig b/lib/build-web/main.zig @@ -30,6 +30,7 @@ const js = struct { steps_len: u32, status: abi.BuildStatus, time_report: bool, + supports_recv: bool, ) void; extern "core" fn updateBuildStatus(status: abi.BuildStatus) void; extern "core" fn updateStepStatus(step_idx: u32) void; @@ -160,7 +161,7 @@ fn helloMessage(msg_bytes: []align(4) u8) Allocator.Error!void { step_list = steps; step_list_data = duped_step_name_data; - js.hello(step_list.len, hdr.status, hdr.flags.time_report); + js.hello(step_list.len, hdr.status, hdr.flags.time_report, hdr.flags.supports_recv); } fn statusUpdateMessage(msg_bytes: []u8) Allocator.Error!void { if (msg_bytes.len < @sizeOf(abi.StatusUpdate)) @panic("malformed StatusUpdate message"); diff --git a/lib/build-web/time_report.zig b/lib/build-web/time_report.zig @@ -175,6 +175,7 @@ pub fn compileResultMessage(msg_bytes: []u8) error{OutOfMemory}!void { \\ <td>{D}</td> \\ <td>{D}</td> \\ <td>{D}</td> + \\ <td>{D}</td> \\</tr> \\ , .{ @@ -182,6 +183,7 @@ pub fn compileResultMessage(msg_bytes: []u8) error{OutOfMemory}!void { file.ns_sema, file.ns_codegen, file.ns_link, + file.ns_sema + file.ns_codegen + file.ns_link, }); } if (slowest_files.len > max_table_rows) { @@ -203,6 +205,7 @@ pub fn compileResultMessage(msg_bytes: []u8) error{OutOfMemory}!void { \\ <td>{D}</td> \\ <td>{D}</td> \\ <td>{D}</td> + \\ <td>{D}</td> \\</tr> \\ , .{ @@ -212,6 +215,7 @@ pub fn compileResultMessage(msg_bytes: []u8) error{OutOfMemory}!void { decl.ns_sema, decl.ns_codegen, decl.ns_link, + decl.ns_sema + decl.ns_codegen + decl.ns_link, }); } if (slowest_decls.len > max_table_rows) { diff --git a/lib/std/Build/WebServer.zig b/lib/std/Build/WebServer.zig @@ -65,16 +65,6 @@ pub fn init(opts: Options) WebServer { std.process.fatal("--webui not yet implemented for single-threaded builds", .{}); } - if (builtin.os.tag == .windows) { - // At the time of writing, there are two bugs in the standard library which break this feature on Windows: - // * Reading from a socket on one thread while writing to it on another seems to deadlock. - // * Vectored writes to sockets currently trigger an infinite loop when a buffer has length 0. - // - // Both of these bugs are expected to be solved by changes which are currently in the unmerged - // 'wrangle-writer-buffering' branch. Until that makes it in, this must remain disabled. - std.process.fatal("--webui is currently disabled on Windows due to bugs", .{}); - } - const all_steps = opts.all_steps; const step_names_trailing = opts.gpa.alloc(u8, len: { @@ -297,13 +287,20 @@ fn serveWebSocket(ws: *WebServer, sock: *http.Server.WebSocket) !noreturn { copy.* = @atomicLoad(u8, shared, .monotonic); } - _ = try std.Thread.spawn(.{}, recvWebSocketMessages, .{ ws, sock }); + // Calling WSARecvFrom on one thread while another calls WSASend deadlocks. + // This functionality is disabled until std.net uses overlapped sockets on Windows. + 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 = .{ .status = prev_build_status, .flags = .{ .time_report = ws.graph.time_report, + .supports_recv = supports_recv, }, .timestamp = ws.now(), .steps_len = @intCast(ws.all_steps.len), diff --git a/lib/std/Build/abi.zig b/lib/std/Build/abi.zig @@ -103,7 +103,9 @@ pub const Hello = extern struct { pub const Flags = packed struct(u16) { /// Whether time reporting is enabled. time_report: bool, - _: u15 = 0, + /// If this platform supports receiving messages from the client + supports_recv: bool, + _: u14 = 0, }; }; /// WebSocket server->client. diff --git a/lib/std/http/Server.zig b/lib/std/http/Server.zig @@ -546,7 +546,6 @@ pub const Request = struct { try out.writeAll("connection: upgrade\r\nupgrade: websocket\r\nsec-websocket-accept: "); const base64_digest = try out.writableArray(28); assert(std.base64.standard.Encoder.encode(base64_digest, &digest).len == base64_digest.len); - out.advance(base64_digest.len); try out.writeAll("\r\n"); for (options.extra_headers) |header| {