| author | |
| committer | |
| log | 125c4a265aa7cfe82b3fc0c1ac7de75a07315411 |
| tree | 42d85f7fdee5e5efc406b474a14df423ad561bc9 |
| parent | 5f7a0bbabfde4eeb0ff4f40f0942ef710b6104a1 |
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 tables7 files changed, 32 insertions(+), 17 deletions(-)
lib/build-web/index.html+2| ... | @@ -105,6 +105,7 @@ | ... | @@ -105,6 +105,7 @@ |
| 105 | <th scope="col">Semantic Analysis</th> | 105 | <th scope="col">Semantic Analysis</th> |
| 106 | <th scope="col">Code Generation</th> | 106 | <th scope="col">Code Generation</th> |
| 107 | <th scope="col">Linking</th> | 107 | <th scope="col">Linking</th> |
| 108 | <th scope="col">Total</th> | ||
| 108 | </tr> | 109 | </tr> |
| 109 | </thead> | 110 | </thead> |
| 110 | <!-- HTML does not allow placing a 'slot' inside of a 'tbody' for backwards-compatibility | 111 | <!-- HTML does not allow placing a 'slot' inside of a 'tbody' for backwards-compatibility |
| ... | @@ -125,6 +126,7 @@ | ... | @@ -125,6 +126,7 @@ |
| 125 | <th scope="col">Semantic Analysis</th> | 126 | <th scope="col">Semantic Analysis</th> |
| 126 | <th scope="col">Code Generation</th> | 127 | <th scope="col">Code Generation</th> |
| 127 | <th scope="col">Linking</th> | 128 | <th scope="col">Linking</th> |
| 129 | <th scope="col">Total</th> | ||
| 128 | </tr> | 130 | </tr> |
| 129 | </thead> | 131 | </thead> |
| 130 | <!-- HTML does not allow placing a 'slot' inside of a 'tbody' for backwards-compatibility | 132 | <!-- HTML does not allow placing a 'slot' inside of a 'tbody' for backwards-compatibility |
lib/build-web/main.js+13-3| ... | @@ -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 | }; |
| 9 | const domButtonRebuild = document.getElementById("buttonRebuild"); | 9 | let domButtonRebuild = document.getElementById("buttonRebuild"); |
| 10 | const domStepList = document.getElementById("stepList"); | 10 | const domStepList = document.getElementById("stepList"); |
| 11 | let domSteps = []; | 11 | let domSteps = []; |
| 12 | 12 | ||
| ... | @@ -114,7 +114,13 @@ function hello( | ... | @@ -114,7 +114,13 @@ function hello( |
| 114 | steps_len, | 114 | steps_len, |
| 115 | build_status, | 115 | build_status, |
| 116 | time_report, | 116 | time_report, |
| 117 | supports_recv, | ||
| 117 | ) { | 118 | ) { |
| 119 | if (!supports_recv && domButtonRebuild) { | ||
| 120 | domButtonRebuild.remove(); | ||
| 121 | domButtonRebuild = null; | ||
| 122 | } | ||
| 123 | |||
| 118 | domSummary.stepCount.textContent = steps_len; | 124 | domSummary.stepCount.textContent = steps_len; |
| 119 | updateBuildStatus(build_status); | 125 | updateBuildStatus(build_status); |
| 120 | setConnectionStatus("", false); | 126 | setConnectionStatus("", false); |
| ... | @@ -161,11 +167,15 @@ function updateBuildStatus(s) { | ... | @@ -161,11 +167,15 @@ function updateBuildStatus(s) { |
| 161 | if (active) { | 167 | if (active) { |
| 162 | domSummary.status.classList.add("status-running"); | 168 | domSummary.status.classList.add("status-running"); |
| 163 | domSummary.status.classList.remove("status-idle"); | 169 | domSummary.status.classList.remove("status-idle"); |
| 164 | domButtonRebuild.disabled = true; | 170 | if (domButtonRebuild) { |
| 171 | domButtonRebuild.disabled = true; | ||
| 172 | } | ||
| 165 | } else { | 173 | } else { |
| 166 | domSummary.status.classList.remove("status-running"); | 174 | domSummary.status.classList.remove("status-running"); |
| 167 | domSummary.status.classList.add("status-idle"); | 175 | domSummary.status.classList.add("status-idle"); |
| 168 | domButtonRebuild.disabled = false; | 176 | if (domButtonRebuild) { |
| 177 | domButtonRebuild.disabled = false; | ||
| 178 | } | ||
| 169 | } | 179 | } |
| 170 | if (reset_time_reports) { | 180 | if (reset_time_reports) { |
| 171 | // Grey out and collapse all the time reports | 181 | // Grey out and collapse all the time reports |
lib/build-web/main.zig+2-1| ... | @@ -30,6 +30,7 @@ const js = struct { | ... | @@ -30,6 +30,7 @@ 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, | ||
| 33 | ) void; | 34 | ) void; |
| 34 | extern "core" fn updateBuildStatus(status: abi.BuildStatus) void; | 35 | extern "core" fn updateBuildStatus(status: abi.BuildStatus) void; |
| 35 | extern "core" fn updateStepStatus(step_idx: u32) void; | 36 | extern "core" fn updateStepStatus(step_idx: u32) void; |
| ... | @@ -160,7 +161,7 @@ fn helloMessage(msg_bytes: []align(4) u8) Allocator.Error!void { | ... | @@ -160,7 +161,7 @@ fn helloMessage(msg_bytes: []align(4) u8) Allocator.Error!void { |
| 160 | step_list = steps; | 161 | step_list = steps; |
| 161 | step_list_data = duped_step_name_data; | 162 | step_list_data = duped_step_name_data; |
| 162 | 163 | ||
| 163 | js.hello(step_list.len, hdr.status, hdr.flags.time_report); | 164 | js.hello(step_list.len, hdr.status, hdr.flags.time_report, hdr.flags.supports_recv); |
| 164 | } | 165 | } |
| 165 | fn statusUpdateMessage(msg_bytes: []u8) Allocator.Error!void { | 166 | fn statusUpdateMessage(msg_bytes: []u8) Allocator.Error!void { |
| 166 | if (msg_bytes.len < @sizeOf(abi.StatusUpdate)) @panic("malformed StatusUpdate message"); | 167 | if (msg_bytes.len < @sizeOf(abi.StatusUpdate)) @panic("malformed StatusUpdate message"); |
lib/build-web/time_report.zig+4| ... | @@ -175,6 +175,7 @@ pub fn compileResultMessage(msg_bytes: []u8) error{OutOfMemory}!void { | ... | @@ -175,6 +175,7 @@ pub fn compileResultMessage(msg_bytes: []u8) error{OutOfMemory}!void { |
| 175 | \\ <td>{D}</td> | 175 | \\ <td>{D}</td> |
| 176 | \\ <td>{D}</td> | 176 | \\ <td>{D}</td> |
| 177 | \\ <td>{D}</td> | 177 | \\ <td>{D}</td> |
| 178 | \\ <td>{D}</td> | ||
| 178 | \\</tr> | 179 | \\</tr> |
| 179 | \\ | 180 | \\ |
| 180 | , .{ | 181 | , .{ |
| ... | @@ -182,6 +183,7 @@ pub fn compileResultMessage(msg_bytes: []u8) error{OutOfMemory}!void { | ... | @@ -182,6 +183,7 @@ pub fn compileResultMessage(msg_bytes: []u8) error{OutOfMemory}!void { |
| 182 | file.ns_sema, | 183 | file.ns_sema, |
| 183 | file.ns_codegen, | 184 | file.ns_codegen, |
| 184 | file.ns_link, | 185 | file.ns_link, |
| 186 | file.ns_sema + file.ns_codegen + file.ns_link, | ||
| 185 | }); | 187 | }); |
| 186 | } | 188 | } |
| 187 | if (slowest_files.len > max_table_rows) { | 189 | if (slowest_files.len > max_table_rows) { |
| ... | @@ -203,6 +205,7 @@ pub fn compileResultMessage(msg_bytes: []u8) error{OutOfMemory}!void { | ... | @@ -203,6 +205,7 @@ pub fn compileResultMessage(msg_bytes: []u8) error{OutOfMemory}!void { |
| 203 | \\ <td>{D}</td> | 205 | \\ <td>{D}</td> |
| 204 | \\ <td>{D}</td> | 206 | \\ <td>{D}</td> |
| 205 | \\ <td>{D}</td> | 207 | \\ <td>{D}</td> |
| 208 | \\ <td>{D}</td> | ||
| 206 | \\</tr> | 209 | \\</tr> |
| 207 | \\ | 210 | \\ |
| 208 | , .{ | 211 | , .{ |
| ... | @@ -212,6 +215,7 @@ pub fn compileResultMessage(msg_bytes: []u8) error{OutOfMemory}!void { | ... | @@ -212,6 +215,7 @@ pub fn compileResultMessage(msg_bytes: []u8) error{OutOfMemory}!void { |
| 212 | decl.ns_sema, | 215 | decl.ns_sema, |
| 213 | decl.ns_codegen, | 216 | decl.ns_codegen, |
| 214 | decl.ns_link, | 217 | decl.ns_link, |
| 218 | decl.ns_sema + decl.ns_codegen + decl.ns_link, | ||
| 215 | }); | 219 | }); |
| 216 | } | 220 | } |
| 217 | if (slowest_decls.len > max_table_rows) { | 221 | if (slowest_decls.len > max_table_rows) { |
lib/std/Build/WebServer.zig+8-11| ... | @@ -65,16 +65,6 @@ pub fn init(opts: Options) WebServer { | ... | @@ -65,16 +65,6 @@ pub fn init(opts: Options) WebServer { |
| 65 | std.process.fatal("--webui not yet implemented for single-threaded builds", .{}); | 65 | std.process.fatal("--webui not yet implemented for single-threaded builds", .{}); |
| 66 | } | 66 | } |
| 67 | 67 | ||
| 68 | if (builtin.os.tag == .windows) { | ||
| 69 | // At the time of writing, there are two bugs in the standard library which break this feature on Windows: | ||
| 70 | // * Reading from a socket on one thread while writing to it on another seems to deadlock. | ||
| 71 | // * Vectored writes to sockets currently trigger an infinite loop when a buffer has length 0. | ||
| 72 | // | ||
| 73 | // Both of these bugs are expected to be solved by changes which are currently in the unmerged | ||
| 74 | // 'wrangle-writer-buffering' branch. Until that makes it in, this must remain disabled. | ||
| 75 | std.process.fatal("--webui is currently disabled on Windows due to bugs", .{}); | ||
| 76 | } | ||
| 77 | |||
| 78 | const all_steps = opts.all_steps; | 68 | const all_steps = opts.all_steps; |
| 79 | 69 | ||
| 80 | const step_names_trailing = opts.gpa.alloc(u8, len: { | 70 | const step_names_trailing = opts.gpa.alloc(u8, len: { |
| ... | @@ -297,13 +287,20 @@ fn serveWebSocket(ws: *WebServer, sock: *http.Server.WebSocket) !noreturn { | ... | @@ -297,13 +287,20 @@ fn serveWebSocket(ws: *WebServer, sock: *http.Server.WebSocket) !noreturn { |
| 297 | copy.* = @atomicLoad(u8, shared, .monotonic); | 287 | copy.* = @atomicLoad(u8, shared, .monotonic); |
| 298 | } | 288 | } |
| 299 | 289 | ||
| 300 | _ = try std.Thread.spawn(.{}, recvWebSocketMessages, .{ ws, sock }); | 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(); | ||
| 301 | 297 | ||
| 302 | { | 298 | { |
| 303 | const hello_header: abi.Hello = .{ | 299 | const hello_header: abi.Hello = .{ |
| 304 | .status = prev_build_status, | 300 | .status = prev_build_status, |
| 305 | .flags = .{ | 301 | .flags = .{ |
| 306 | .time_report = ws.graph.time_report, | 302 | .time_report = ws.graph.time_report, |
| 303 | .supports_recv = supports_recv, | ||
| 307 | }, | 304 | }, |
| 308 | .timestamp = ws.now(), | 305 | .timestamp = ws.now(), |
| 309 | .steps_len = @intCast(ws.all_steps.len), | 306 | .steps_len = @intCast(ws.all_steps.len), |
lib/std/Build/abi.zig+3-1| ... | @@ -103,7 +103,9 @@ pub const Hello = extern struct { | ... | @@ -103,7 +103,9 @@ 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 | _: u15 = 0, | 106 | /// If this platform supports receiving messages from the client |
| 107 | supports_recv: bool, | ||
| 108 | _: u14 = 0, | ||
| 107 | }; | 109 | }; |
| 108 | }; | 110 | }; |
| 109 | /// WebSocket server->client. | 111 | /// WebSocket server->client. |
lib/std/http/Server.zig-1| ... | @@ -546,7 +546,6 @@ pub const Request = struct { | ... | @@ -546,7 +546,6 @@ pub const Request = struct { |
| 546 | try out.writeAll("connection: upgrade\r\nupgrade: websocket\r\nsec-websocket-accept: "); | 546 | try out.writeAll("connection: upgrade\r\nupgrade: websocket\r\nsec-websocket-accept: "); |
| 547 | const base64_digest = try out.writableArray(28); | 547 | const base64_digest = try out.writableArray(28); |
| 548 | assert(std.base64.standard.Encoder.encode(base64_digest, &digest).len == base64_digest.len); | 548 | assert(std.base64.standard.Encoder.encode(base64_digest, &digest).len == base64_digest.len); |
| 549 | out.advance(base64_digest.len); | ||
| 550 | try out.writeAll("\r\n"); | 549 | try out.writeAll("\r\n"); |
| 551 | 550 | ||
| 552 | for (options.extra_headers) |header| { | 551 | for (options.extra_headers) |header| { |