authorgravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2025-11-24 11:18:35-08:00
committergravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2025-11-24 14:34:18-08:00
log3f34f5e43349214c862882a83bd951701a6c735f
tree031d1ab910775458fb6066f5379b54e2e44e1baf
parenta242292644a12b8ca0485759ba45c550265da5bd

build runner: update Mutex and Condition usage to std.Io


6 files changed, 149 insertions(+), 112 deletions(-)

lib/compiler/build_runner.zig+9-9
...@@ -494,7 +494,7 @@ pub fn main() !void {...@@ -494,7 +494,7 @@ pub fn main() !void {
494494
495 .max_rss = max_rss,495 .max_rss = max_rss,
496 .max_rss_is_default = false,496 .max_rss_is_default = false,
497 .max_rss_mutex = .{},497 .max_rss_mutex = .init,
498 .skip_oom_steps = skip_oom_steps,498 .skip_oom_steps = skip_oom_steps,
499 .unit_test_timeout_ns = test_timeout_ns,499 .unit_test_timeout_ns = test_timeout_ns,
500500
...@@ -583,7 +583,7 @@ pub fn main() !void {...@@ -583,7 +583,7 @@ pub fn main() !void {
583583
584 if (run.web_server) |*ws| {584 if (run.web_server) |*ws| {
585 assert(!watch); // fatal error after CLI parsing585 assert(!watch); // fatal error after CLI parsing
586 while (true) switch (ws.wait()) {586 while (true) switch (try ws.wait()) {
587 .rebuild => {587 .rebuild => {
588 for (run.step_stack.keys()) |step| {588 for (run.step_stack.keys()) |step| {
589 step.state = .precheck_done;589 step.state = .precheck_done;
...@@ -652,7 +652,7 @@ const Run = struct {...@@ -652,7 +652,7 @@ const Run = struct {
652 gpa: Allocator,652 gpa: Allocator,
653 max_rss: u64,653 max_rss: u64,
654 max_rss_is_default: bool,654 max_rss_is_default: bool,
655 max_rss_mutex: std.Thread.Mutex,655 max_rss_mutex: Io.Mutex,
656 skip_oom_steps: bool,656 skip_oom_steps: bool,
657 unit_test_timeout_ns: ?u64,657 unit_test_timeout_ns: ?u64,
658 watch: bool,658 watch: bool,
...@@ -1305,6 +1305,8 @@ fn workerMakeOneStep(...@@ -1305,6 +1305,8 @@ fn workerMakeOneStep(
1305 prog_node: std.Progress.Node,1305 prog_node: std.Progress.Node,
1306 run: *Run,1306 run: *Run,
1307) void {1307) void {
1308 const io = b.graph.io;
1309
1308 // First, check the conditions for running this step. If they are not met,1310 // First, check the conditions for running this step. If they are not met,
1309 // then we return without doing the step, relying on another worker to1311 // then we return without doing the step, relying on another worker to
1310 // queue this step up again when dependencies are met.1312 // queue this step up again when dependencies are met.
...@@ -1326,8 +1328,8 @@ fn workerMakeOneStep(...@@ -1326,8 +1328,8 @@ fn workerMakeOneStep(
1326 }1328 }
13271329
1328 if (s.max_rss != 0) {1330 if (s.max_rss != 0) {
1329 run.max_rss_mutex.lock();1331 run.max_rss_mutex.lockUncancelable(io);
1330 defer run.max_rss_mutex.unlock();1332 defer run.max_rss_mutex.unlock(io);
13311333
1332 // Avoid running steps twice.1334 // Avoid running steps twice.
1333 if (s.state != .precheck_done) {1335 if (s.state != .precheck_done) {
...@@ -1378,8 +1380,6 @@ fn workerMakeOneStep(...@@ -1378,8 +1380,6 @@ fn workerMakeOneStep(
1378 printErrorMessages(run.gpa, s, .{}, bw, ttyconf, run.error_style, run.multiline_errors) catch {};1380 printErrorMessages(run.gpa, s, .{}, bw, ttyconf, run.error_style, run.multiline_errors) catch {};
1379 }1381 }
13801382
1381 const io = b.graph.io;
1382
1383 handle_result: {1383 handle_result: {
1384 if (make_result) |_| {1384 if (make_result) |_| {
1385 @atomicStore(Step.State, &s.state, .success, .seq_cst);1385 @atomicStore(Step.State, &s.state, .success, .seq_cst);
...@@ -1406,8 +1406,8 @@ fn workerMakeOneStep(...@@ -1406,8 +1406,8 @@ fn workerMakeOneStep(
1406 // If this is a step that claims resources, we must now queue up other1406 // If this is a step that claims resources, we must now queue up other
1407 // steps that are waiting for resources.1407 // steps that are waiting for resources.
1408 if (s.max_rss != 0) {1408 if (s.max_rss != 0) {
1409 run.max_rss_mutex.lock();1409 run.max_rss_mutex.lockUncancelable(io);
1410 defer run.max_rss_mutex.unlock();1410 defer run.max_rss_mutex.unlock(io);
14111411
1412 // Give the memory back to the scheduler.1412 // Give the memory back to the scheduler.
1413 run.claimed_rss -= s.max_rss;1413 run.claimed_rss -= s.max_rss;
lib/std/Build/Cache.zig+22-9
...@@ -22,7 +22,7 @@ manifest_dir: fs.Dir,...@@ -22,7 +22,7 @@ manifest_dir: fs.Dir,
22hash: HashHelper = .{},22hash: HashHelper = .{},
23/// This value is accessed from multiple threads, protected by mutex.23/// This value is accessed from multiple threads, protected by mutex.
24recent_problematic_timestamp: Io.Timestamp = .zero,24recent_problematic_timestamp: Io.Timestamp = .zero,
25mutex: std.Thread.Mutex = .{},25mutex: Io.Mutex = .init,
2626
27/// A set of strings such as the zig library directory or project source root, which27/// A set of strings such as the zig library directory or project source root, which
28/// are stripped from the file paths before putting into the cache. They28/// are stripped from the file paths before putting into the cache. They
...@@ -474,6 +474,7 @@ pub const Manifest = struct {...@@ -474,6 +474,7 @@ pub const Manifest = struct {
474 /// A cache manifest file exists however it could not be parsed.474 /// A cache manifest file exists however it could not be parsed.
475 InvalidFormat,475 InvalidFormat,
476 OutOfMemory,476 OutOfMemory,
477 Canceled,
477 };478 };
478479
479 /// Check the cache to see if the input exists in it. If it exists, returns `true`.480 /// Check the cache to see if the input exists in it. If it exists, returns `true`.
...@@ -559,12 +560,14 @@ pub const Manifest = struct {...@@ -559,12 +560,14 @@ pub const Manifest = struct {
559 self.diagnostic = .{ .manifest_create = error.FileNotFound };560 self.diagnostic = .{ .manifest_create = error.FileNotFound };
560 return error.CacheCheckFailed;561 return error.CacheCheckFailed;
561 },562 },
563 error.Canceled => return error.Canceled,
562 else => |e| {564 else => |e| {
563 self.diagnostic = .{ .manifest_create = e };565 self.diagnostic = .{ .manifest_create = e };
564 return error.CacheCheckFailed;566 return error.CacheCheckFailed;
565 },567 },
566 }568 }
567 },569 },
570 error.Canceled => return error.Canceled,
568 else => |e| {571 else => |e| {
569 self.diagnostic = .{ .manifest_create = e };572 self.diagnostic = .{ .manifest_create = e };
570 return error.CacheCheckFailed;573 return error.CacheCheckFailed;
...@@ -762,6 +765,7 @@ pub const Manifest = struct {...@@ -762,6 +765,7 @@ pub const Manifest = struct {
762 // Every digest before this one has been populated successfully.765 // Every digest before this one has been populated successfully.
763 return .{ .miss = .{ .file_digests_populated = idx } };766 return .{ .miss = .{ .file_digests_populated = idx } };
764 },767 },
768 error.Canceled => return error.Canceled,
765 else => |e| {769 else => |e| {
766 self.diagnostic = .{ .file_open = .{770 self.diagnostic = .{ .file_open = .{
767 .file_index = idx,771 .file_index = idx,
...@@ -790,7 +794,7 @@ pub const Manifest = struct {...@@ -790,7 +794,7 @@ pub const Manifest = struct {
790 .inode = actual_stat.inode,794 .inode = actual_stat.inode,
791 };795 };
792796
793 if (self.isProblematicTimestamp(cache_hash_file.stat.mtime)) {797 if (try self.isProblematicTimestamp(cache_hash_file.stat.mtime)) {
794 // The actual file has an unreliable timestamp, force it to be hashed798 // The actual file has an unreliable timestamp, force it to be hashed
795 cache_hash_file.stat.mtime = .zero;799 cache_hash_file.stat.mtime = .zero;
796 cache_hash_file.stat.inode = 0;800 cache_hash_file.stat.inode = 0;
...@@ -848,7 +852,9 @@ pub const Manifest = struct {...@@ -848,7 +852,9 @@ pub const Manifest = struct {
848 }852 }
849 }853 }
850854
851 fn isProblematicTimestamp(man: *Manifest, timestamp: Io.Timestamp) bool {855 fn isProblematicTimestamp(man: *Manifest, timestamp: Io.Timestamp) error{Canceled}!bool {
856 const io = man.cache.io;
857
852 // If the file_time is prior to the most recent problematic timestamp858 // If the file_time is prior to the most recent problematic timestamp
853 // then we don't need to access the filesystem.859 // then we don't need to access the filesystem.
854 if (timestamp.nanoseconds < man.recent_problematic_timestamp.nanoseconds)860 if (timestamp.nanoseconds < man.recent_problematic_timestamp.nanoseconds)
...@@ -856,8 +862,8 @@ pub const Manifest = struct {...@@ -856,8 +862,8 @@ pub const Manifest = struct {
856862
857 // Next we will check the globally shared Cache timestamp, which is accessed863 // Next we will check the globally shared Cache timestamp, which is accessed
858 // from multiple threads.864 // from multiple threads.
859 man.cache.mutex.lock();865 try man.cache.mutex.lock(io);
860 defer man.cache.mutex.unlock();866 defer man.cache.mutex.unlock(io);
861867
862 // Save the global one to our local one to avoid locking next time.868 // Save the global one to our local one to avoid locking next time.
863 man.recent_problematic_timestamp = man.cache.recent_problematic_timestamp;869 man.recent_problematic_timestamp = man.cache.recent_problematic_timestamp;
...@@ -871,11 +877,18 @@ pub const Manifest = struct {...@@ -871,11 +877,18 @@ pub const Manifest = struct {
871 var file = man.cache.manifest_dir.createFile("timestamp", .{877 var file = man.cache.manifest_dir.createFile("timestamp", .{
872 .read = true,878 .read = true,
873 .truncate = true,879 .truncate = true,
874 }) catch return true;880 }) catch |err| switch (err) {
881 error.Canceled => return error.Canceled,
882 else => return true,
883 };
875 defer file.close();884 defer file.close();
876885
877 // Save locally and also save globally (we still hold the global lock).886 // Save locally and also save globally (we still hold the global lock).
878 man.recent_problematic_timestamp = (file.stat() catch return true).mtime;887 const stat = file.stat() catch |err| switch (err) {
888 error.Canceled => return error.Canceled,
889 else => return true,
890 };
891 man.recent_problematic_timestamp = stat.mtime;
879 man.cache.recent_problematic_timestamp = man.recent_problematic_timestamp;892 man.cache.recent_problematic_timestamp = man.recent_problematic_timestamp;
880 }893 }
881894
...@@ -902,7 +915,7 @@ pub const Manifest = struct {...@@ -902,7 +915,7 @@ pub const Manifest = struct {
902 .inode = actual_stat.inode,915 .inode = actual_stat.inode,
903 };916 };
904917
905 if (self.isProblematicTimestamp(ch_file.stat.mtime)) {918 if (try self.isProblematicTimestamp(ch_file.stat.mtime)) {
906 // The actual file has an unreliable timestamp, force it to be hashed919 // The actual file has an unreliable timestamp, force it to be hashed
907 ch_file.stat.mtime = .zero;920 ch_file.stat.mtime = .zero;
908 ch_file.stat.inode = 0;921 ch_file.stat.inode = 0;
...@@ -1038,7 +1051,7 @@ pub const Manifest = struct {...@@ -1038,7 +1051,7 @@ pub const Manifest = struct {
1038 .contents = null,1051 .contents = null,
1039 };1052 };
10401053
1041 if (self.isProblematicTimestamp(new_file.stat.mtime)) {1054 if (try self.isProblematicTimestamp(new_file.stat.mtime)) {
1042 // The actual file has an unreliable timestamp, force it to be hashed1055 // The actual file has an unreliable timestamp, force it to be hashed
1043 new_file.stat.mtime = .zero;1056 new_file.stat.mtime = .zero;
1044 new_file.stat.inode = 0;1057 new_file.stat.inode = 0;
lib/std/Build/Fuzz.zig+48-37
...@@ -27,11 +27,11 @@ root_prog_node: std.Progress.Node,...@@ -27,11 +27,11 @@ root_prog_node: std.Progress.Node,
27prog_node: std.Progress.Node,27prog_node: std.Progress.Node,
2828
29/// Protects `coverage_files`.29/// Protects `coverage_files`.
30coverage_mutex: std.Thread.Mutex,30coverage_mutex: Io.Mutex,
31coverage_files: std.AutoArrayHashMapUnmanaged(u64, CoverageMap),31coverage_files: std.AutoArrayHashMapUnmanaged(u64, CoverageMap),
3232
33queue_mutex: std.Thread.Mutex,33queue_mutex: Io.Mutex,
34queue_cond: std.Thread.Condition,34queue_cond: Io.Condition,
35msg_queue: std.ArrayList(Msg),35msg_queue: std.ArrayList(Msg),
3636
37pub const Mode = union(enum) {37pub const Mode = union(enum) {
...@@ -122,8 +122,8 @@ pub fn init(...@@ -122,8 +122,8 @@ pub fn init(
122 .root_prog_node = root_prog_node,122 .root_prog_node = root_prog_node,
123 .prog_node = .none,123 .prog_node = .none,
124 .coverage_files = .empty,124 .coverage_files = .empty,
125 .coverage_mutex = .{},125 .coverage_mutex = .init,
126 .queue_mutex = .{},126 .queue_mutex = .init,
127 .queue_cond = .{},127 .queue_cond = .{},
128 .msg_queue = .empty,128 .msg_queue = .empty,
129 };129 };
...@@ -157,9 +157,7 @@ pub fn deinit(fuzz: *Fuzz) void {...@@ -157,9 +157,7 @@ pub fn deinit(fuzz: *Fuzz) void {
157fn rebuildTestsWorkerRun(run: *Step.Run, gpa: Allocator, ttyconf: tty.Config, parent_prog_node: std.Progress.Node) void {157fn rebuildTestsWorkerRun(run: *Step.Run, gpa: Allocator, ttyconf: tty.Config, parent_prog_node: std.Progress.Node) void {
158 rebuildTestsWorkerRunFallible(run, gpa, ttyconf, parent_prog_node) catch |err| {158 rebuildTestsWorkerRunFallible(run, gpa, ttyconf, parent_prog_node) catch |err| {
159 const compile = run.producer.?;159 const compile = run.producer.?;
160 log.err("step '{s}': failed to rebuild in fuzz mode: {s}", .{160 log.err("step '{s}': failed to rebuild in fuzz mode: {t}", .{ compile.step.name, err });
161 compile.step.name, @errorName(err),
162 });
163 };161 };
164}162}
165163
...@@ -208,9 +206,7 @@ fn fuzzWorkerRun(...@@ -208,9 +206,7 @@ fn fuzzWorkerRun(
208 return;206 return;
209 },207 },
210 else => {208 else => {
211 log.err("step '{s}': failed to rerun '{s}' in fuzz mode: {s}", .{209 log.err("step '{s}': failed to rerun '{s}' in fuzz mode: {t}", .{ run.step.name, test_name, err });
212 run.step.name, test_name, @errorName(err),
213 });
214 return;210 return;
215 },211 },
216 };212 };
...@@ -269,8 +265,10 @@ pub fn sendUpdate(...@@ -269,8 +265,10 @@ pub fn sendUpdate(
269 socket: *std.http.Server.WebSocket,265 socket: *std.http.Server.WebSocket,
270 prev: *Previous,266 prev: *Previous,
271) !void {267) !void {
272 fuzz.coverage_mutex.lock();268 const io = fuzz.io;
273 defer fuzz.coverage_mutex.unlock();269
270 try fuzz.coverage_mutex.lock(io);
271 defer fuzz.coverage_mutex.unlock(io);
274272
275 const coverage_maps = fuzz.coverage_files.values();273 const coverage_maps = fuzz.coverage_files.values();
276 if (coverage_maps.len == 0) return;274 if (coverage_maps.len == 0) return;
...@@ -331,30 +329,41 @@ pub fn sendUpdate(...@@ -331,30 +329,41 @@ pub fn sendUpdate(
331}329}
332330
333fn coverageRun(fuzz: *Fuzz) void {331fn coverageRun(fuzz: *Fuzz) void {
334 fuzz.queue_mutex.lock();332 coverageRunCancelable(fuzz) catch |err| switch (err) {
335 defer fuzz.queue_mutex.unlock();333 error.Canceled => return,
334 };
335}
336
337fn coverageRunCancelable(fuzz: *Fuzz) Io.Cancelable!void {
338 const io = fuzz.io;
339
340 try fuzz.queue_mutex.lock(io);
341 defer fuzz.queue_mutex.unlock(io);
336342
337 while (true) {343 while (true) {
338 fuzz.queue_cond.wait(&fuzz.queue_mutex);344 try fuzz.queue_cond.wait(io, &fuzz.queue_mutex);
339 for (fuzz.msg_queue.items) |msg| switch (msg) {345 for (fuzz.msg_queue.items) |msg| switch (msg) {
340 .coverage => |coverage| prepareTables(fuzz, coverage.run, coverage.id) catch |err| switch (err) {346 .coverage => |coverage| prepareTables(fuzz, coverage.run, coverage.id) catch |err| switch (err) {
341 error.AlreadyReported => continue,347 error.AlreadyReported => continue,
342 else => |e| log.err("failed to prepare code coverage tables: {s}", .{@errorName(e)}),348 error.Canceled => return,
349 else => |e| log.err("failed to prepare code coverage tables: {t}", .{e}),
343 },350 },
344 .entry_point => |entry_point| addEntryPoint(fuzz, entry_point.coverage_id, entry_point.addr) catch |err| switch (err) {351 .entry_point => |entry_point| addEntryPoint(fuzz, entry_point.coverage_id, entry_point.addr) catch |err| switch (err) {
345 error.AlreadyReported => continue,352 error.AlreadyReported => continue,
346 else => |e| log.err("failed to prepare code coverage tables: {s}", .{@errorName(e)}),353 error.Canceled => return,
354 else => |e| log.err("failed to prepare code coverage tables: {t}", .{e}),
347 },355 },
348 };356 };
349 fuzz.msg_queue.clearRetainingCapacity();357 fuzz.msg_queue.clearRetainingCapacity();
350 }358 }
351}359}
352fn prepareTables(fuzz: *Fuzz, run_step: *Step.Run, coverage_id: u64) error{ OutOfMemory, AlreadyReported }!void {360fn prepareTables(fuzz: *Fuzz, run_step: *Step.Run, coverage_id: u64) error{ OutOfMemory, AlreadyReported, Canceled }!void {
353 assert(fuzz.mode == .forever);361 assert(fuzz.mode == .forever);
354 const ws = fuzz.mode.forever.ws;362 const ws = fuzz.mode.forever.ws;
363 const io = fuzz.io;
355364
356 fuzz.coverage_mutex.lock();365 try fuzz.coverage_mutex.lock(io);
357 defer fuzz.coverage_mutex.unlock();366 defer fuzz.coverage_mutex.unlock(io);
358367
359 const gop = try fuzz.coverage_files.getOrPut(fuzz.gpa, coverage_id);368 const gop = try fuzz.coverage_files.getOrPut(fuzz.gpa, coverage_id);
360 if (gop.found_existing) {369 if (gop.found_existing) {
...@@ -385,8 +394,8 @@ fn prepareTables(fuzz: *Fuzz, run_step: *Step.Run, coverage_id: u64) error{ OutO...@@ -385,8 +394,8 @@ fn prepareTables(fuzz: *Fuzz, run_step: *Step.Run, coverage_id: u64) error{ OutO
385 target.ofmt,394 target.ofmt,
386 target.cpu.arch,395 target.cpu.arch,
387 ) catch |err| {396 ) catch |err| {
388 log.err("step '{s}': failed to load debug information for '{f}': {s}", .{397 log.err("step '{s}': failed to load debug information for '{f}': {t}", .{
389 run_step.step.name, rebuilt_exe_path, @errorName(err),398 run_step.step.name, rebuilt_exe_path, err,
390 });399 });
391 return error.AlreadyReported;400 return error.AlreadyReported;
392 };401 };
...@@ -397,15 +406,15 @@ fn prepareTables(fuzz: *Fuzz, run_step: *Step.Run, coverage_id: u64) error{ OutO...@@ -397,15 +406,15 @@ fn prepareTables(fuzz: *Fuzz, run_step: *Step.Run, coverage_id: u64) error{ OutO
397 .sub_path = "v/" ++ std.fmt.hex(coverage_id),406 .sub_path = "v/" ++ std.fmt.hex(coverage_id),
398 };407 };
399 var coverage_file = coverage_file_path.root_dir.handle.openFile(coverage_file_path.sub_path, .{}) catch |err| {408 var coverage_file = coverage_file_path.root_dir.handle.openFile(coverage_file_path.sub_path, .{}) catch |err| {
400 log.err("step '{s}': failed to load coverage file '{f}': {s}", .{409 log.err("step '{s}': failed to load coverage file '{f}': {t}", .{
401 run_step.step.name, coverage_file_path, @errorName(err),410 run_step.step.name, coverage_file_path, err,
402 });411 });
403 return error.AlreadyReported;412 return error.AlreadyReported;
404 };413 };
405 defer coverage_file.close();414 defer coverage_file.close();
406415
407 const file_size = coverage_file.getEndPos() catch |err| {416 const file_size = coverage_file.getEndPos() catch |err| {
408 log.err("unable to check len of coverage file '{f}': {s}", .{ coverage_file_path, @errorName(err) });417 log.err("unable to check len of coverage file '{f}': {t}", .{ coverage_file_path, err });
409 return error.AlreadyReported;418 return error.AlreadyReported;
410 };419 };
411420
...@@ -417,7 +426,7 @@ fn prepareTables(fuzz: *Fuzz, run_step: *Step.Run, coverage_id: u64) error{ OutO...@@ -417,7 +426,7 @@ fn prepareTables(fuzz: *Fuzz, run_step: *Step.Run, coverage_id: u64) error{ OutO
417 coverage_file.handle,426 coverage_file.handle,
418 0,427 0,
419 ) catch |err| {428 ) catch |err| {
420 log.err("failed to map coverage file '{f}': {s}", .{ coverage_file_path, @errorName(err) });429 log.err("failed to map coverage file '{f}': {t}", .{ coverage_file_path, err });
421 return error.AlreadyReported;430 return error.AlreadyReported;
422 };431 };
423 gop.value_ptr.mapped_memory = mapped_memory;432 gop.value_ptr.mapped_memory = mapped_memory;
...@@ -443,7 +452,7 @@ fn prepareTables(fuzz: *Fuzz, run_step: *Step.Run, coverage_id: u64) error{ OutO...@@ -443,7 +452,7 @@ fn prepareTables(fuzz: *Fuzz, run_step: *Step.Run, coverage_id: u64) error{ OutO
443 }{ .addrs = sorted_pcs.items(.pc) });452 }{ .addrs = sorted_pcs.items(.pc) });
444453
445 debug_info.resolveAddresses(fuzz.gpa, sorted_pcs.items(.pc), sorted_pcs.items(.sl)) catch |err| {454 debug_info.resolveAddresses(fuzz.gpa, sorted_pcs.items(.pc), sorted_pcs.items(.sl)) catch |err| {
446 log.err("failed to resolve addresses to source locations: {s}", .{@errorName(err)});455 log.err("failed to resolve addresses to source locations: {t}", .{err});
447 return error.AlreadyReported;456 return error.AlreadyReported;
448 };457 };
449458
...@@ -453,9 +462,11 @@ fn prepareTables(fuzz: *Fuzz, run_step: *Step.Run, coverage_id: u64) error{ OutO...@@ -453,9 +462,11 @@ fn prepareTables(fuzz: *Fuzz, run_step: *Step.Run, coverage_id: u64) error{ OutO
453 ws.notifyUpdate();462 ws.notifyUpdate();
454}463}
455464
456fn addEntryPoint(fuzz: *Fuzz, coverage_id: u64, addr: u64) error{ AlreadyReported, OutOfMemory }!void {465fn addEntryPoint(fuzz: *Fuzz, coverage_id: u64, addr: u64) error{ AlreadyReported, OutOfMemory, Canceled }!void {
457 fuzz.coverage_mutex.lock();466 const io = fuzz.io;
458 defer fuzz.coverage_mutex.unlock();467
468 try fuzz.coverage_mutex.lock(io);
469 defer fuzz.coverage_mutex.unlock(io);
459470
460 const coverage_map = fuzz.coverage_files.getPtr(coverage_id).?;471 const coverage_map = fuzz.coverage_files.getPtr(coverage_id).?;
461 const header: *const abi.SeenPcsHeader = @ptrCast(coverage_map.mapped_memory[0..@sizeOf(abi.SeenPcsHeader)]);472 const header: *const abi.SeenPcsHeader = @ptrCast(coverage_map.mapped_memory[0..@sizeOf(abi.SeenPcsHeader)]);
...@@ -518,8 +529,8 @@ pub fn waitAndPrintReport(fuzz: *Fuzz) void {...@@ -518,8 +529,8 @@ pub fn waitAndPrintReport(fuzz: *Fuzz) void {
518 .sub_path = "v/" ++ std.fmt.hex(cov.id),529 .sub_path = "v/" ++ std.fmt.hex(cov.id),
519 };530 };
520 var coverage_file = coverage_file_path.root_dir.handle.openFile(coverage_file_path.sub_path, .{}) catch |err| {531 var coverage_file = coverage_file_path.root_dir.handle.openFile(coverage_file_path.sub_path, .{}) catch |err| {
521 fatal("step '{s}': failed to load coverage file '{f}': {s}", .{532 fatal("step '{s}': failed to load coverage file '{f}': {t}", .{
522 cov.run.step.name, coverage_file_path, @errorName(err),533 cov.run.step.name, coverage_file_path, err,
523 });534 });
524 };535 };
525 defer coverage_file.close();536 defer coverage_file.close();
...@@ -530,8 +541,8 @@ pub fn waitAndPrintReport(fuzz: *Fuzz) void {...@@ -530,8 +541,8 @@ pub fn waitAndPrintReport(fuzz: *Fuzz) void {
530541
531 var header: fuzz_abi.SeenPcsHeader = undefined;542 var header: fuzz_abi.SeenPcsHeader = undefined;
532 r.interface.readSliceAll(std.mem.asBytes(&header)) catch |err| {543 r.interface.readSliceAll(std.mem.asBytes(&header)) catch |err| {
533 fatal("step '{s}': failed to read from coverage file '{f}': {s}", .{544 fatal("step '{s}': failed to read from coverage file '{f}': {t}", .{
534 cov.run.step.name, coverage_file_path, @errorName(err),545 cov.run.step.name, coverage_file_path, err,
535 });546 });
536 };547 };
537548
...@@ -545,8 +556,8 @@ pub fn waitAndPrintReport(fuzz: *Fuzz) void {...@@ -545,8 +556,8 @@ pub fn waitAndPrintReport(fuzz: *Fuzz) void {
545 const chunk_count = fuzz_abi.SeenPcsHeader.seenElemsLen(header.pcs_len);556 const chunk_count = fuzz_abi.SeenPcsHeader.seenElemsLen(header.pcs_len);
546 for (0..chunk_count) |_| {557 for (0..chunk_count) |_| {
547 const seen = r.interface.takeInt(usize, .little) catch |err| {558 const seen = r.interface.takeInt(usize, .little) catch |err| {
548 fatal("step '{s}': failed to read from coverage file '{f}': {s}", .{559 fatal("step '{s}': failed to read from coverage file '{f}': {t}", .{
549 cov.run.step.name, coverage_file_path, @errorName(err),560 cov.run.step.name, coverage_file_path, err,
550 });561 });
551 };562 };
552 seen_count += @popCount(seen);563 seen_count += @popCount(seen);
lib/std/Build/Step.zig+19-18
...@@ -362,7 +362,7 @@ pub fn captureChildProcess(...@@ -362,7 +362,7 @@ pub fn captureChildProcess(
362 .allocator = arena,362 .allocator = arena,
363 .argv = argv,363 .argv = argv,
364 .progress_node = progress_node,364 .progress_node = progress_node,
365 }) catch |err| return s.fail("failed to run {s}: {s}", .{ argv[0], @errorName(err) });365 }) catch |err| return s.fail("failed to run {s}: {t}", .{ argv[0], err });
366366
367 if (result.stderr.len > 0) {367 if (result.stderr.len > 0) {
368 try s.result_error_msgs.append(arena, result.stderr);368 try s.result_error_msgs.append(arena, result.stderr);
...@@ -412,7 +412,7 @@ pub fn evalZigProcess(...@@ -412,7 +412,7 @@ pub fn evalZigProcess(
412 error.BrokenPipe => {412 error.BrokenPipe => {
413 // Process restart required.413 // Process restart required.
414 const term = zp.child.wait() catch |e| {414 const term = zp.child.wait() catch |e| {
415 return s.fail("unable to wait for {s}: {s}", .{ argv[0], @errorName(e) });415 return s.fail("unable to wait for {s}: {t}", .{ argv[0], e });
416 };416 };
417 _ = term;417 _ = term;
418 s.clearZigProcess(gpa);418 s.clearZigProcess(gpa);
...@@ -428,7 +428,7 @@ pub fn evalZigProcess(...@@ -428,7 +428,7 @@ pub fn evalZigProcess(
428 if (s.result_error_msgs.items.len > 0 and result == null) {428 if (s.result_error_msgs.items.len > 0 and result == null) {
429 // Crash detected.429 // Crash detected.
430 const term = zp.child.wait() catch |e| {430 const term = zp.child.wait() catch |e| {
431 return s.fail("unable to wait for {s}: {s}", .{ argv[0], @errorName(e) });431 return s.fail("unable to wait for {s}: {t}", .{ argv[0], e });
432 };432 };
433 s.result_peak_rss = zp.child.resource_usage_statistics.getMaxRss() orelse 0;433 s.result_peak_rss = zp.child.resource_usage_statistics.getMaxRss() orelse 0;
434 s.clearZigProcess(gpa);434 s.clearZigProcess(gpa);
...@@ -453,9 +453,7 @@ pub fn evalZigProcess(...@@ -453,9 +453,7 @@ pub fn evalZigProcess(
453 child.request_resource_usage_statistics = true;453 child.request_resource_usage_statistics = true;
454 child.progress_node = prog_node;454 child.progress_node = prog_node;
455455
456 child.spawn() catch |err| return s.fail("failed to spawn zig compiler {s}: {s}", .{456 child.spawn() catch |err| return s.fail("failed to spawn zig compiler {s}: {t}", .{ argv[0], err });
457 argv[0], @errorName(err),
458 });
459457
460 const zp = try gpa.create(ZigProcess);458 const zp = try gpa.create(ZigProcess);
461 zp.* = .{459 zp.* = .{
...@@ -480,7 +478,7 @@ pub fn evalZigProcess(...@@ -480,7 +478,7 @@ pub fn evalZigProcess(
480 zp.child.stdin = null;478 zp.child.stdin = null;
481479
482 const term = zp.child.wait() catch |err| {480 const term = zp.child.wait() catch |err| {
483 return s.fail("unable to wait for {s}: {s}", .{ argv[0], @errorName(err) });481 return s.fail("unable to wait for {s}: {t}", .{ argv[0], err });
484 };482 };
485 s.result_peak_rss = zp.child.resource_usage_statistics.getMaxRss() orelse 0;483 s.result_peak_rss = zp.child.resource_usage_statistics.getMaxRss() orelse 0;
486484
...@@ -513,8 +511,8 @@ pub fn installFile(s: *Step, src_lazy_path: Build.LazyPath, dest_path: []const u...@@ -513,8 +511,8 @@ pub fn installFile(s: *Step, src_lazy_path: Build.LazyPath, dest_path: []const u
513 const src_path = src_lazy_path.getPath3(b, s);511 const src_path = src_lazy_path.getPath3(b, s);
514 try handleVerbose(b, null, &.{ "install", "-C", b.fmt("{f}", .{src_path}), dest_path });512 try handleVerbose(b, null, &.{ "install", "-C", b.fmt("{f}", .{src_path}), dest_path });
515 return Io.Dir.updateFile(src_path.root_dir.handle.adaptToNewApi(), io, src_path.sub_path, .cwd(), dest_path, .{}) catch |err| {513 return Io.Dir.updateFile(src_path.root_dir.handle.adaptToNewApi(), io, src_path.sub_path, .cwd(), dest_path, .{}) catch |err| {
516 return s.fail("unable to update file from '{f}' to '{s}': {s}", .{514 return s.fail("unable to update file from '{f}' to '{s}': {t}", .{
517 src_path, dest_path, @errorName(err),515 src_path, dest_path, err,
518 });516 });
519 };517 };
520}518}
...@@ -524,9 +522,7 @@ pub fn installDir(s: *Step, dest_path: []const u8) !std.fs.Dir.MakePathStatus {...@@ -524,9 +522,7 @@ pub fn installDir(s: *Step, dest_path: []const u8) !std.fs.Dir.MakePathStatus {
524 const b = s.owner;522 const b = s.owner;
525 try handleVerbose(b, null, &.{ "install", "-d", dest_path });523 try handleVerbose(b, null, &.{ "install", "-d", dest_path });
526 return std.fs.cwd().makePathStatus(dest_path) catch |err| {524 return std.fs.cwd().makePathStatus(dest_path) catch |err| {
527 return s.fail("unable to create dir '{s}': {s}", .{525 return s.fail("unable to create dir '{s}': {t}", .{ dest_path, err });
528 dest_path, @errorName(err),
529 });
530 };526 };
531}527}
532528
...@@ -825,22 +821,27 @@ pub fn cacheHitAndWatch(s: *Step, man: *Build.Cache.Manifest) !bool {...@@ -825,22 +821,27 @@ pub fn cacheHitAndWatch(s: *Step, man: *Build.Cache.Manifest) !bool {
825 return is_hit;821 return is_hit;
826}822}
827823
828fn failWithCacheError(s: *Step, man: *const Build.Cache.Manifest, err: Build.Cache.Manifest.HitError) error{ OutOfMemory, MakeFailed } {824fn failWithCacheError(
825 s: *Step,
826 man: *const Build.Cache.Manifest,
827 err: Build.Cache.Manifest.HitError,
828) error{ OutOfMemory, Canceled, MakeFailed } {
829 switch (err) {829 switch (err) {
830 error.CacheCheckFailed => switch (man.diagnostic) {830 error.CacheCheckFailed => switch (man.diagnostic) {
831 .none => unreachable,831 .none => unreachable,
832 .manifest_create, .manifest_read, .manifest_lock => |e| return s.fail("failed to check cache: {s} {s}", .{832 .manifest_create, .manifest_read, .manifest_lock => |e| return s.fail("failed to check cache: {t} {t}", .{
833 @tagName(man.diagnostic), @errorName(e),833 man.diagnostic, e,
834 }),834 }),
835 .file_open, .file_stat, .file_read, .file_hash => |op| {835 .file_open, .file_stat, .file_read, .file_hash => |op| {
836 const pp = man.files.keys()[op.file_index].prefixed_path;836 const pp = man.files.keys()[op.file_index].prefixed_path;
837 const prefix = man.cache.prefixes()[pp.prefix].path orelse "";837 const prefix = man.cache.prefixes()[pp.prefix].path orelse "";
838 return s.fail("failed to check cache: '{s}{c}{s}' {s} {s}", .{838 return s.fail("failed to check cache: '{s}{c}{s}' {t} {t}", .{
839 prefix, std.fs.path.sep, pp.sub_path, @tagName(man.diagnostic), @errorName(op.err),839 prefix, std.fs.path.sep, pp.sub_path, man.diagnostic, op.err,
840 });840 });
841 },841 },
842 },842 },
843 error.OutOfMemory => return error.OutOfMemory,843 error.OutOfMemory => return error.OutOfMemory,
844 error.Canceled => return error.Canceled,
844 error.InvalidFormat => return s.fail("failed to check cache: invalid manifest file format", .{}),845 error.InvalidFormat => return s.fail("failed to check cache: invalid manifest file format", .{}),
845 }846 }
846}847}
...@@ -850,7 +851,7 @@ fn failWithCacheError(s: *Step, man: *const Build.Cache.Manifest, err: Build.Cac...@@ -850,7 +851,7 @@ fn failWithCacheError(s: *Step, man: *const Build.Cache.Manifest, err: Build.Cac
850pub fn writeManifest(s: *Step, man: *Build.Cache.Manifest) !void {851pub fn writeManifest(s: *Step, man: *Build.Cache.Manifest) !void {
851 if (s.test_results.isSuccess()) {852 if (s.test_results.isSuccess()) {
852 man.writeManifest() catch |err| {853 man.writeManifest() catch |err| {
853 try s.addError("unable to write cache manifest: {s}", .{@errorName(err)});854 try s.addError("unable to write cache manifest: {t}", .{err});
854 };855 };
855 }856 }
856}857}
lib/std/Build/Step/Run.zig+7-6
...@@ -1830,6 +1830,7 @@ fn pollZigTest(...@@ -1830,6 +1830,7 @@ fn pollZigTest(
1830} {1830} {
1831 const gpa = run.step.owner.allocator;1831 const gpa = run.step.owner.allocator;
1832 const arena = run.step.owner.allocator;1832 const arena = run.step.owner.allocator;
1833 const io = run.step.owner.graph.io;
18331834
1834 var sub_prog_node: ?std.Progress.Node = null;1835 var sub_prog_node: ?std.Progress.Node = null;
1835 defer if (sub_prog_node) |n| n.end();1836 defer if (sub_prog_node) |n| n.end();
...@@ -2035,8 +2036,8 @@ fn pollZigTest(...@@ -2035,8 +2036,8 @@ fn pollZigTest(
20352036
2036 {2037 {
2037 const fuzz = fuzz_context.?.fuzz;2038 const fuzz = fuzz_context.?.fuzz;
2038 fuzz.queue_mutex.lock();2039 fuzz.queue_mutex.lockUncancelable(io);
2039 defer fuzz.queue_mutex.unlock();2040 defer fuzz.queue_mutex.unlock(io);
2040 try fuzz.msg_queue.append(fuzz.gpa, .{ .coverage = .{2041 try fuzz.msg_queue.append(fuzz.gpa, .{ .coverage = .{
2041 .id = coverage_id.?,2042 .id = coverage_id.?,
2042 .cumulative = .{2043 .cumulative = .{
...@@ -2046,20 +2047,20 @@ fn pollZigTest(...@@ -2046,20 +2047,20 @@ fn pollZigTest(
2046 },2047 },
2047 .run = run,2048 .run = run,
2048 } });2049 } });
2049 fuzz.queue_cond.signal();2050 fuzz.queue_cond.signal(io);
2050 }2051 }
2051 },2052 },
2052 .fuzz_start_addr => {2053 .fuzz_start_addr => {
2053 const fuzz = fuzz_context.?.fuzz;2054 const fuzz = fuzz_context.?.fuzz;
2054 const addr = body_r.takeInt(u64, .little) catch unreachable;2055 const addr = body_r.takeInt(u64, .little) catch unreachable;
2055 {2056 {
2056 fuzz.queue_mutex.lock();2057 fuzz.queue_mutex.lockUncancelable(io);
2057 defer fuzz.queue_mutex.unlock();2058 defer fuzz.queue_mutex.unlock(io);
2058 try fuzz.msg_queue.append(fuzz.gpa, .{ .entry_point = .{2059 try fuzz.msg_queue.append(fuzz.gpa, .{ .entry_point = .{
2059 .addr = addr,2060 .addr = addr,
2060 .coverage_id = coverage_id.?,2061 .coverage_id = coverage_id.?,
2061 } });2062 } });
2062 fuzz.queue_cond.signal();2063 fuzz.queue_cond.signal(io);
2063 }2064 }
2064 },2065 },
2065 else => {}, // ignore other messages2066 else => {}, // ignore other messages
lib/std/Build/WebServer.zig+44-33
...@@ -19,7 +19,7 @@ step_names_trailing: []u8,...@@ -19,7 +19,7 @@ step_names_trailing: []u8,
19step_status_bits: []u8,19step_status_bits: []u8,
2020
21fuzz: ?Fuzz,21fuzz: ?Fuzz,
22time_report_mutex: std.Thread.Mutex,22time_report_mutex: Io.Mutex,
23time_report_msgs: [][]u8,23time_report_msgs: [][]u8,
24time_report_update_times: []i64,24time_report_update_times: []i64,
2525
...@@ -33,9 +33,9 @@ build_status: std.atomic.Value(abi.BuildStatus),...@@ -33,9 +33,9 @@ build_status: std.atomic.Value(abi.BuildStatus),
33/// an unreasonable number of packets.33/// an unreasonable number of packets.
34update_id: std.atomic.Value(u32),34update_id: std.atomic.Value(u32),
3535
36runner_request_mutex: std.Thread.Mutex,36runner_request_mutex: Io.Mutex,
37runner_request_ready_cond: std.Thread.Condition,37runner_request_ready_cond: Io.Condition,
38runner_request_empty_cond: std.Thread.Condition,38runner_request_empty_cond: Io.Condition,
39runner_request: ?RunnerRequest,39runner_request: ?RunnerRequest,
4040
41/// If a client is not explicitly notified of changes with `notifyUpdate`, it will be sent updates41/// If a client is not explicitly notified of changes with `notifyUpdate`, it will be sent updates
...@@ -114,14 +114,14 @@ pub fn init(opts: Options) WebServer {...@@ -114,14 +114,14 @@ pub fn init(opts: Options) WebServer {
114 .step_status_bits = step_status_bits,114 .step_status_bits = step_status_bits,
115115
116 .fuzz = null,116 .fuzz = null,
117 .time_report_mutex = .{},117 .time_report_mutex = .init,
118 .time_report_msgs = time_report_msgs,118 .time_report_msgs = time_report_msgs,
119 .time_report_update_times = time_report_update_times,119 .time_report_update_times = time_report_update_times,
120120
121 .build_status = .init(.idle),121 .build_status = .init(.idle),
122 .update_id = .init(0),122 .update_id = .init(0),
123123
124 .runner_request_mutex = .{},124 .runner_request_mutex = .init,
125 .runner_request_ready_cond = .{},125 .runner_request_ready_cond = .{},
126 .runner_request_empty_cond = .{},126 .runner_request_empty_cond = .{},
127 .runner_request = null,127 .runner_request = null,
...@@ -296,6 +296,8 @@ fn accept(ws: *WebServer, stream: net.Stream) void {...@@ -296,6 +296,8 @@ fn accept(ws: *WebServer, stream: net.Stream) void {
296}296}
297297
298fn serveWebSocket(ws: *WebServer, sock: *http.Server.WebSocket) !noreturn {298fn serveWebSocket(ws: *WebServer, sock: *http.Server.WebSocket) !noreturn {
299 const io = ws.graph.io;
300
299 var prev_build_status = ws.build_status.load(.monotonic);301 var prev_build_status = ws.build_status.load(.monotonic);
300302
301 const prev_step_status_bits = try ws.gpa.alloc(u8, ws.step_status_bits.len);303 const prev_step_status_bits = try ws.gpa.alloc(u8, ws.step_status_bits.len);
...@@ -331,8 +333,8 @@ fn serveWebSocket(ws: *WebServer, sock: *http.Server.WebSocket) !noreturn {...@@ -331,8 +333,8 @@ fn serveWebSocket(ws: *WebServer, sock: *http.Server.WebSocket) !noreturn {
331 }333 }
332334
333 {335 {
334 ws.time_report_mutex.lock();336 try ws.time_report_mutex.lock(io);
335 defer ws.time_report_mutex.unlock();337 defer ws.time_report_mutex.unlock(io);
336 for (ws.time_report_msgs, ws.time_report_update_times) |msg, update_time| {338 for (ws.time_report_msgs, ws.time_report_update_times) |msg, update_time| {
337 if (update_time <= prev_time) continue;339 if (update_time <= prev_time) continue;
338 // We want to send `msg`, but shouldn't block `ws.time_report_mutex` while we do, so340 // We want to send `msg`, but shouldn't block `ws.time_report_mutex` while we do, so
...@@ -340,8 +342,8 @@ fn serveWebSocket(ws: *WebServer, sock: *http.Server.WebSocket) !noreturn {...@@ -340,8 +342,8 @@ fn serveWebSocket(ws: *WebServer, sock: *http.Server.WebSocket) !noreturn {
340 const owned_msg = try ws.gpa.dupe(u8, msg);342 const owned_msg = try ws.gpa.dupe(u8, msg);
341 defer ws.gpa.free(owned_msg);343 defer ws.gpa.free(owned_msg);
342 // Temporarily unlock, then re-lock after the message is sent.344 // Temporarily unlock, then re-lock after the message is sent.
343 ws.time_report_mutex.unlock();345 ws.time_report_mutex.unlock(io);
344 defer ws.time_report_mutex.lock();346 defer ws.time_report_mutex.lockUncancelable(io);
345 try sock.writeMessage(owned_msg, .binary);347 try sock.writeMessage(owned_msg, .binary);
346 }348 }
347 }349 }
...@@ -382,6 +384,8 @@ fn serveWebSocket(ws: *WebServer, sock: *http.Server.WebSocket) !noreturn {...@@ -382,6 +384,8 @@ fn serveWebSocket(ws: *WebServer, sock: *http.Server.WebSocket) !noreturn {
382 }384 }
383}385}
384fn recvWebSocketMessages(ws: *WebServer, sock: *http.Server.WebSocket) void {386fn recvWebSocketMessages(ws: *WebServer, sock: *http.Server.WebSocket) void {
387 const io = ws.graph.io;
388
385 while (true) {389 while (true) {
386 const msg = sock.readSmallMessage() catch return;390 const msg = sock.readSmallMessage() catch return;
387 if (msg.opcode != .binary) continue;391 if (msg.opcode != .binary) continue;
...@@ -390,14 +394,16 @@ fn recvWebSocketMessages(ws: *WebServer, sock: *http.Server.WebSocket) void {...@@ -390,14 +394,16 @@ fn recvWebSocketMessages(ws: *WebServer, sock: *http.Server.WebSocket) void {
390 switch (tag) {394 switch (tag) {
391 _ => continue,395 _ => continue,
392 .rebuild => while (true) {396 .rebuild => while (true) {
393 ws.runner_request_mutex.lock();397 ws.runner_request_mutex.lock(io) catch |err| switch (err) {
394 defer ws.runner_request_mutex.unlock();398 error.Canceled => return,
399 };
400 defer ws.runner_request_mutex.unlock(io);
395 if (ws.runner_request == null) {401 if (ws.runner_request == null) {
396 ws.runner_request = .rebuild;402 ws.runner_request = .rebuild;
397 ws.runner_request_ready_cond.signal();403 ws.runner_request_ready_cond.signal(io);
398 break;404 break;
399 }405 }
400 ws.runner_request_empty_cond.wait(&ws.runner_request_mutex);406 ws.runner_request_empty_cond.wait(io, &ws.runner_request_mutex) catch return;
401 },407 },
402 }408 }
403 }409 }
...@@ -691,14 +697,15 @@ pub fn updateTimeReportCompile(ws: *WebServer, opts: struct {...@@ -691,14 +697,15 @@ pub fn updateTimeReportCompile(ws: *WebServer, opts: struct {
691 trailing: []const u8,697 trailing: []const u8,
692}) void {698}) void {
693 const gpa = ws.gpa;699 const gpa = ws.gpa;
700 const io = ws.graph.io;
694701
695 const step_idx: u32 = for (ws.all_steps, 0..) |s, i| {702 const step_idx: u32 = for (ws.all_steps, 0..) |s, i| {
696 if (s == &opts.compile.step) break @intCast(i);703 if (s == &opts.compile.step) break @intCast(i);
697 } else unreachable;704 } else unreachable;
698705
699 const old_buf = old: {706 const old_buf = old: {
700 ws.time_report_mutex.lock();707 ws.time_report_mutex.lock(io) catch return;
701 defer ws.time_report_mutex.unlock();708 defer ws.time_report_mutex.unlock(io);
702 const old = ws.time_report_msgs[step_idx];709 const old = ws.time_report_msgs[step_idx];
703 ws.time_report_msgs[step_idx] = &.{};710 ws.time_report_msgs[step_idx] = &.{};
704 break :old old;711 break :old old;
...@@ -720,8 +727,8 @@ pub fn updateTimeReportCompile(ws: *WebServer, opts: struct {...@@ -720,8 +727,8 @@ pub fn updateTimeReportCompile(ws: *WebServer, opts: struct {
720 @memcpy(buf[@sizeOf(abi.time_report.CompileResult)..], opts.trailing);727 @memcpy(buf[@sizeOf(abi.time_report.CompileResult)..], opts.trailing);
721728
722 {729 {
723 ws.time_report_mutex.lock();730 ws.time_report_mutex.lock(io) catch return;
724 defer ws.time_report_mutex.unlock();731 defer ws.time_report_mutex.unlock(io);
725 assert(ws.time_report_msgs[step_idx].len == 0);732 assert(ws.time_report_msgs[step_idx].len == 0);
726 ws.time_report_msgs[step_idx] = buf;733 ws.time_report_msgs[step_idx] = buf;
727 ws.time_report_update_times[step_idx] = ws.now();734 ws.time_report_update_times[step_idx] = ws.now();
...@@ -731,14 +738,15 @@ pub fn updateTimeReportCompile(ws: *WebServer, opts: struct {...@@ -731,14 +738,15 @@ pub fn updateTimeReportCompile(ws: *WebServer, opts: struct {
731738
732pub fn updateTimeReportGeneric(ws: *WebServer, step: *Build.Step, ns_total: u64) void {739pub fn updateTimeReportGeneric(ws: *WebServer, step: *Build.Step, ns_total: u64) void {
733 const gpa = ws.gpa;740 const gpa = ws.gpa;
741 const io = ws.graph.io;
734742
735 const step_idx: u32 = for (ws.all_steps, 0..) |s, i| {743 const step_idx: u32 = for (ws.all_steps, 0..) |s, i| {
736 if (s == step) break @intCast(i);744 if (s == step) break @intCast(i);
737 } else unreachable;745 } else unreachable;
738746
739 const old_buf = old: {747 const old_buf = old: {
740 ws.time_report_mutex.lock();748 ws.time_report_mutex.lock(io) catch return;
741 defer ws.time_report_mutex.unlock();749 defer ws.time_report_mutex.unlock(io);
742 const old = ws.time_report_msgs[step_idx];750 const old = ws.time_report_msgs[step_idx];
743 ws.time_report_msgs[step_idx] = &.{};751 ws.time_report_msgs[step_idx] = &.{};
744 break :old old;752 break :old old;
...@@ -750,8 +758,8 @@ pub fn updateTimeReportGeneric(ws: *WebServer, step: *Build.Step, ns_total: u64)...@@ -750,8 +758,8 @@ pub fn updateTimeReportGeneric(ws: *WebServer, step: *Build.Step, ns_total: u64)
750 .ns_total = ns_total,758 .ns_total = ns_total,
751 };759 };
752 {760 {
753 ws.time_report_mutex.lock();761 ws.time_report_mutex.lock(io) catch return;
754 defer ws.time_report_mutex.unlock();762 defer ws.time_report_mutex.unlock(io);
755 assert(ws.time_report_msgs[step_idx].len == 0);763 assert(ws.time_report_msgs[step_idx].len == 0);
756 ws.time_report_msgs[step_idx] = buf;764 ws.time_report_msgs[step_idx] = buf;
757 ws.time_report_update_times[step_idx] = ws.now();765 ws.time_report_update_times[step_idx] = ws.now();
...@@ -766,6 +774,7 @@ pub fn updateTimeReportRunTest(...@@ -766,6 +774,7 @@ pub fn updateTimeReportRunTest(
766 ns_per_test: []const u64,774 ns_per_test: []const u64,
767) void {775) void {
768 const gpa = ws.gpa;776 const gpa = ws.gpa;
777 const io = ws.graph.io;
769778
770 const step_idx: u32 = for (ws.all_steps, 0..) |s, i| {779 const step_idx: u32 = for (ws.all_steps, 0..) |s, i| {
771 if (s == &run.step) break @intCast(i);780 if (s == &run.step) break @intCast(i);
...@@ -782,8 +791,8 @@ pub fn updateTimeReportRunTest(...@@ -782,8 +791,8 @@ pub fn updateTimeReportRunTest(
782 break :len @sizeOf(abi.time_report.RunTestResult) + names_len + 8 * tests_len;791 break :len @sizeOf(abi.time_report.RunTestResult) + names_len + 8 * tests_len;
783 };792 };
784 const old_buf = old: {793 const old_buf = old: {
785 ws.time_report_mutex.lock();794 ws.time_report_mutex.lock(io) catch return;
786 defer ws.time_report_mutex.unlock();795 defer ws.time_report_mutex.unlock(io);
787 const old = ws.time_report_msgs[step_idx];796 const old = ws.time_report_msgs[step_idx];
788 ws.time_report_msgs[step_idx] = &.{};797 ws.time_report_msgs[step_idx] = &.{};
789 break :old old;798 break :old old;
...@@ -808,8 +817,8 @@ pub fn updateTimeReportRunTest(...@@ -808,8 +817,8 @@ pub fn updateTimeReportRunTest(
808 assert(offset == buf.len);817 assert(offset == buf.len);
809818
810 {819 {
811 ws.time_report_mutex.lock();820 ws.time_report_mutex.lock(io) catch return;
812 defer ws.time_report_mutex.unlock();821 defer ws.time_report_mutex.unlock(io);
813 assert(ws.time_report_msgs[step_idx].len == 0);822 assert(ws.time_report_msgs[step_idx].len == 0);
814 ws.time_report_msgs[step_idx] = buf;823 ws.time_report_msgs[step_idx] = buf;
815 ws.time_report_update_times[step_idx] = ws.now();824 ws.time_report_update_times[step_idx] = ws.now();
...@@ -821,8 +830,9 @@ const RunnerRequest = union(enum) {...@@ -821,8 +830,9 @@ const RunnerRequest = union(enum) {
821 rebuild,830 rebuild,
822};831};
823pub fn getRunnerRequest(ws: *WebServer) ?RunnerRequest {832pub fn getRunnerRequest(ws: *WebServer) ?RunnerRequest {
824 ws.runner_request_mutex.lock();833 const io = ws.graph.io;
825 defer ws.runner_request_mutex.unlock();834 ws.runner_request_mutex.lock(io) catch return;
835 defer ws.runner_request_mutex.unlock(io);
826 if (ws.runner_request) |req| {836 if (ws.runner_request) |req| {
827 ws.runner_request = null;837 ws.runner_request = null;
828 ws.runner_request_empty_cond.signal();838 ws.runner_request_empty_cond.signal();
...@@ -830,16 +840,17 @@ pub fn getRunnerRequest(ws: *WebServer) ?RunnerRequest {...@@ -830,16 +840,17 @@ pub fn getRunnerRequest(ws: *WebServer) ?RunnerRequest {
830 }840 }
831 return null;841 return null;
832}842}
833pub fn wait(ws: *WebServer) RunnerRequest {843pub fn wait(ws: *WebServer) Io.Cancelable!RunnerRequest {
834 ws.runner_request_mutex.lock();844 const io = ws.graph.io;
835 defer ws.runner_request_mutex.unlock();845 try ws.runner_request_mutex.lock(io);
846 defer ws.runner_request_mutex.unlock(io);
836 while (true) {847 while (true) {
837 if (ws.runner_request) |req| {848 if (ws.runner_request) |req| {
838 ws.runner_request = null;849 ws.runner_request = null;
839 ws.runner_request_empty_cond.signal();850 ws.runner_request_empty_cond.signal(io);
840 return req;851 return req;
841 }852 }
842 ws.runner_request_ready_cond.wait(&ws.runner_request_mutex);853 try ws.runner_request_ready_cond.wait(io, &ws.runner_request_mutex);
843 }854 }
844}855}
845856