authorgravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2025-11-25 02:49:37-08:00
committergravatar for noreply@github.comGitHub <noreply@github.com> 2025-11-25 02:49:37-08:00
log5816646aa057383d8f87214824a08ae7a9ecd18e
tree94b381603926e3c7a2130556871b41a7fff8263f
parent53e615b920a5c8de19df515c40edc70c82aee392
parent84353183c724298d2341b857bbd69975d315ec8a
signaturebadge-check Signed by PGP key B5690EEEBB952194

Merge pull request #26037 from ziglang/build-runner-std.Io

build runner: update from std.Thread.Pool to std.Io

14 files changed, 269 insertions(+), 232 deletions(-)

lib/compiler/build_runner.zig+48-64
...@@ -107,7 +107,6 @@ pub fn main() !void {...@@ -107,7 +107,6 @@ pub fn main() !void {
107107
108 var targets = std.array_list.Managed([]const u8).init(arena);108 var targets = std.array_list.Managed([]const u8).init(arena);
109 var debug_log_scopes = std.array_list.Managed([]const u8).init(arena);109 var debug_log_scopes = std.array_list.Managed([]const u8).init(arena);
110 var thread_pool_options: std.Thread.Pool.Options = .{ .allocator = arena };
111110
112 var install_prefix: ?[]const u8 = null;111 var install_prefix: ?[]const u8 = null;
113 var dir_list = std.Build.DirList{};112 var dir_list = std.Build.DirList{};
...@@ -413,19 +412,11 @@ pub fn main() !void {...@@ -413,19 +412,11 @@ pub fn main() !void {
413 };412 };
414 } else if (mem.eql(u8, arg, "-fno-reference-trace")) {413 } else if (mem.eql(u8, arg, "-fno-reference-trace")) {
415 builder.reference_trace = null;414 builder.reference_trace = null;
416 } else if (mem.startsWith(u8, arg, "-j")) {415 } else if (mem.cutPrefix(u8, arg, "-j")) |text| {
417 const num = arg["-j".len..];416 const n = std.fmt.parseUnsigned(u32, text, 10) catch |err|
418 const n_jobs = std.fmt.parseUnsigned(u32, num, 10) catch |err| {417 fatal("unable to parse jobs count '{s}': {t}", .{ text, err });
419 std.debug.print("unable to parse jobs count '{s}': {s}", .{418 if (n < 1) fatal("number of jobs must be at least 1", .{});
420 num, @errorName(err),419 threaded.setAsyncLimit(.limited(n));
421 });
422 process.exit(1);
423 };
424 if (n_jobs < 1) {
425 std.debug.print("number of jobs must be at least 1\n", .{});
426 process.exit(1);
427 }
428 thread_pool_options.n_jobs = n_jobs;
429 } else if (mem.eql(u8, arg, "--")) {420 } else if (mem.eql(u8, arg, "--")) {
430 builder.args = argsRest(args, arg_idx);421 builder.args = argsRest(args, arg_idx);
431 break;422 break;
...@@ -503,7 +494,7 @@ pub fn main() !void {...@@ -503,7 +494,7 @@ pub fn main() !void {
503494
504 .max_rss = max_rss,495 .max_rss = max_rss,
505 .max_rss_is_default = false,496 .max_rss_is_default = false,
506 .max_rss_mutex = .{},497 .max_rss_mutex = .init,
507 .skip_oom_steps = skip_oom_steps,498 .skip_oom_steps = skip_oom_steps,
508 .unit_test_timeout_ns = test_timeout_ns,499 .unit_test_timeout_ns = test_timeout_ns,
509500
...@@ -516,7 +507,6 @@ pub fn main() !void {...@@ -516,7 +507,6 @@ pub fn main() !void {
516 .error_style = error_style,507 .error_style = error_style,
517 .multiline_errors = multiline_errors,508 .multiline_errors = multiline_errors,
518 .summary = summary orelse if (watch or webui_listen != null) .line else .failures,509 .summary = summary orelse if (watch or webui_listen != null) .line else .failures,
519 .thread_pool = undefined,
520510
521 .ttyconf = ttyconf,511 .ttyconf = ttyconf,
522 };512 };
...@@ -547,16 +537,12 @@ pub fn main() !void {...@@ -547,16 +537,12 @@ pub fn main() !void {
547 break :w try .init();537 break :w try .init();
548 };538 };
549539
550 try run.thread_pool.init(thread_pool_options);
551 defer run.thread_pool.deinit();
552
553 const now = Io.Clock.Timestamp.now(io, .awake) catch |err| fatal("failed to collect timestamp: {t}", .{err});540 const now = Io.Clock.Timestamp.now(io, .awake) catch |err| fatal("failed to collect timestamp: {t}", .{err});
554541
555 run.web_server = if (webui_listen) |listen_address| ws: {542 run.web_server = if (webui_listen) |listen_address| ws: {
556 if (builtin.single_threaded) unreachable; // `fatal` above543 if (builtin.single_threaded) unreachable; // `fatal` above
557 break :ws .init(.{544 break :ws .init(.{
558 .gpa = gpa,545 .gpa = gpa,
559 .thread_pool = &run.thread_pool,
560 .ttyconf = ttyconf,546 .ttyconf = ttyconf,
561 .graph = &graph,547 .graph = &graph,
562 .all_steps = run.step_stack.keys(),548 .all_steps = run.step_stack.keys(),
...@@ -597,7 +583,7 @@ pub fn main() !void {...@@ -597,7 +583,7 @@ pub fn main() !void {
597583
598 if (run.web_server) |*ws| {584 if (run.web_server) |*ws| {
599 assert(!watch); // fatal error after CLI parsing585 assert(!watch); // fatal error after CLI parsing
600 while (true) switch (ws.wait()) {586 while (true) switch (try ws.wait()) {
601 .rebuild => {587 .rebuild => {
602 for (run.step_stack.keys()) |step| {588 for (run.step_stack.keys()) |step| {
603 step.state = .precheck_done;589 step.state = .precheck_done;
...@@ -666,7 +652,7 @@ const Run = struct {...@@ -666,7 +652,7 @@ const Run = struct {
666 gpa: Allocator,652 gpa: Allocator,
667 max_rss: u64,653 max_rss: u64,
668 max_rss_is_default: bool,654 max_rss_is_default: bool,
669 max_rss_mutex: std.Thread.Mutex,655 max_rss_mutex: Io.Mutex,
670 skip_oom_steps: bool,656 skip_oom_steps: bool,
671 unit_test_timeout_ns: ?u64,657 unit_test_timeout_ns: ?u64,
672 watch: bool,658 watch: bool,
...@@ -675,7 +661,6 @@ const Run = struct {...@@ -675,7 +661,6 @@ const Run = struct {
675 memory_blocked_steps: std.ArrayList(*Step),661 memory_blocked_steps: std.ArrayList(*Step),
676 /// Allocated into `gpa`.662 /// Allocated into `gpa`.
677 step_stack: std.AutoArrayHashMapUnmanaged(*Step, void),663 step_stack: std.AutoArrayHashMapUnmanaged(*Step, void),
678 thread_pool: std.Thread.Pool,
679 /// Similar to the `tty.Config` returned by `std.debug.lockStderrWriter`,664 /// Similar to the `tty.Config` returned by `std.debug.lockStderrWriter`,
680 /// but also respects the '--color' flag.665 /// but also respects the '--color' flag.
681 ttyconf: tty.Config,666 ttyconf: tty.Config,
...@@ -754,14 +739,13 @@ fn runStepNames(...@@ -754,14 +739,13 @@ fn runStepNames(
754 const gpa = run.gpa;739 const gpa = run.gpa;
755 const io = b.graph.io;740 const io = b.graph.io;
756 const step_stack = &run.step_stack;741 const step_stack = &run.step_stack;
757 const thread_pool = &run.thread_pool;
758742
759 {743 {
760 const step_prog = parent_prog_node.start("steps", step_stack.count());744 const step_prog = parent_prog_node.start("steps", step_stack.count());
761 defer step_prog.end();745 defer step_prog.end();
762746
763 var wait_group: std.Thread.WaitGroup = .{};747 var group: Io.Group = .init;
764 defer wait_group.wait();748 defer group.wait(io);
765749
766 // Here we spawn the initial set of tasks with a nice heuristic -750 // Here we spawn the initial set of tasks with a nice heuristic -
767 // dependency order. Each worker when it finishes a step will then751 // dependency order. Each worker when it finishes a step will then
...@@ -771,9 +755,7 @@ fn runStepNames(...@@ -771,9 +755,7 @@ fn runStepNames(
771 const step = steps_slice[steps_slice.len - i - 1];755 const step = steps_slice[steps_slice.len - i - 1];
772 if (step.state == .skipped_oom) continue;756 if (step.state == .skipped_oom) continue;
773757
774 thread_pool.spawnWg(&wait_group, workerMakeOneStep, .{758 group.async(io, workerMakeOneStep, .{ &group, b, step, step_prog, run });
775 &wait_group, b, step, step_prog, run,
776 });
777 }759 }
778 }760 }
779761
...@@ -855,7 +837,6 @@ fn runStepNames(...@@ -855,7 +837,6 @@ fn runStepNames(
855 var f = std.Build.Fuzz.init(837 var f = std.Build.Fuzz.init(
856 gpa,838 gpa,
857 io,839 io,
858 thread_pool,
859 run.ttyconf,840 run.ttyconf,
860 step_stack.keys(),841 step_stack.keys(),
861 parent_prog_node,842 parent_prog_node,
...@@ -1318,13 +1299,14 @@ fn constructGraphAndCheckForDependencyLoop(...@@ -1318,13 +1299,14 @@ fn constructGraphAndCheckForDependencyLoop(
1318}1299}
13191300
1320fn workerMakeOneStep(1301fn workerMakeOneStep(
1321 wg: *std.Thread.WaitGroup,1302 group: *Io.Group,
1322 b: *std.Build,1303 b: *std.Build,
1323 s: *Step,1304 s: *Step,
1324 prog_node: std.Progress.Node,1305 prog_node: std.Progress.Node,
1325 run: *Run,1306 run: *Run,
1326) void {1307) void {
1327 const thread_pool = &run.thread_pool;1308 const io = b.graph.io;
1309 const gpa = run.gpa;
13281310
1329 // First, check the conditions for running this step. If they are not met,1311 // First, check the conditions for running this step. If they are not met,
1330 // then we return without doing the step, relying on another worker to1312 // then we return without doing the step, relying on another worker to
...@@ -1347,8 +1329,8 @@ fn workerMakeOneStep(...@@ -1347,8 +1329,8 @@ fn workerMakeOneStep(
1347 }1329 }
13481330
1349 if (s.max_rss != 0) {1331 if (s.max_rss != 0) {
1350 run.max_rss_mutex.lock();1332 run.max_rss_mutex.lockUncancelable(io);
1351 defer run.max_rss_mutex.unlock();1333 defer run.max_rss_mutex.unlock(io);
13521334
1353 // Avoid running steps twice.1335 // Avoid running steps twice.
1354 if (s.state != .precheck_done) {1336 if (s.state != .precheck_done) {
...@@ -1360,7 +1342,7 @@ fn workerMakeOneStep(...@@ -1360,7 +1342,7 @@ fn workerMakeOneStep(
1360 if (new_claimed_rss > run.max_rss) {1342 if (new_claimed_rss > run.max_rss) {
1361 // Running this step right now could possibly exceed the allotted RSS.1343 // Running this step right now could possibly exceed the allotted RSS.
1362 // Add this step to the queue of memory-blocked steps.1344 // Add this step to the queue of memory-blocked steps.
1363 run.memory_blocked_steps.append(run.gpa, s) catch @panic("OOM");1345 run.memory_blocked_steps.append(gpa, s) catch @panic("OOM");
1364 return;1346 return;
1365 }1347 }
13661348
...@@ -1381,12 +1363,11 @@ fn workerMakeOneStep(...@@ -1381,12 +1363,11 @@ fn workerMakeOneStep(
13811363
1382 const make_result = s.make(.{1364 const make_result = s.make(.{
1383 .progress_node = sub_prog_node,1365 .progress_node = sub_prog_node,
1384 .thread_pool = thread_pool,
1385 .watch = run.watch,1366 .watch = run.watch,
1386 .web_server = if (run.web_server) |*ws| ws else null,1367 .web_server = if (run.web_server) |*ws| ws else null,
1387 .ttyconf = run.ttyconf,1368 .ttyconf = run.ttyconf,
1388 .unit_test_timeout_ns = run.unit_test_timeout_ns,1369 .unit_test_timeout_ns = run.unit_test_timeout_ns,
1389 .gpa = run.gpa,1370 .gpa = gpa,
1390 });1371 });
13911372
1392 // No matter the result, we want to display error/warning messages.1373 // No matter the result, we want to display error/warning messages.
...@@ -1397,7 +1378,7 @@ fn workerMakeOneStep(...@@ -1397,7 +1378,7 @@ fn workerMakeOneStep(
1397 const bw, _ = std.debug.lockStderrWriter(&stdio_buffer_allocation);1378 const bw, _ = std.debug.lockStderrWriter(&stdio_buffer_allocation);
1398 defer std.debug.unlockStderrWriter();1379 defer std.debug.unlockStderrWriter();
1399 const ttyconf = run.ttyconf;1380 const ttyconf = run.ttyconf;
1400 printErrorMessages(run.gpa, s, .{}, bw, ttyconf, run.error_style, run.multiline_errors) catch {};1381 printErrorMessages(gpa, s, .{}, bw, ttyconf, run.error_style, run.multiline_errors) catch {};
1401 }1382 }
14021383
1403 handle_result: {1384 handle_result: {
...@@ -1419,40 +1400,43 @@ fn workerMakeOneStep(...@@ -1419,40 +1400,43 @@ fn workerMakeOneStep(
14191400
1420 // Successful completion of a step, so we queue up its dependants as well.1401 // Successful completion of a step, so we queue up its dependants as well.
1421 for (s.dependants.items) |dep| {1402 for (s.dependants.items) |dep| {
1422 thread_pool.spawnWg(wg, workerMakeOneStep, .{1403 group.async(io, workerMakeOneStep, .{ group, b, dep, prog_node, run });
1423 wg, b, dep, prog_node, run,
1424 });
1425 }1404 }
1426 }1405 }
14271406
1428 // If this is a step that claims resources, we must now queue up other1407 // If this is a step that claims resources, we must now queue up other
1429 // steps that are waiting for resources.1408 // steps that are waiting for resources.
1430 if (s.max_rss != 0) {1409 if (s.max_rss != 0) {
1431 run.max_rss_mutex.lock();1410 var dispatch_deps: std.ArrayList(*Step) = .empty;
1432 defer run.max_rss_mutex.unlock();1411 defer dispatch_deps.deinit(gpa);
14331412 dispatch_deps.ensureUnusedCapacity(gpa, run.memory_blocked_steps.items.len) catch @panic("OOM");
1434 // Give the memory back to the scheduler.1413
1435 run.claimed_rss -= s.max_rss;1414 {
1436 // Avoid kicking off too many tasks that we already know will not have1415 run.max_rss_mutex.lockUncancelable(io);
1437 // enough resources.1416 defer run.max_rss_mutex.unlock(io);
1438 var remaining = run.max_rss - run.claimed_rss;1417
1439 var i: usize = 0;1418 // Give the memory back to the scheduler.
1440 var j: usize = 0;1419 run.claimed_rss -= s.max_rss;
1441 while (j < run.memory_blocked_steps.items.len) : (j += 1) {1420 // Avoid kicking off too many tasks that we already know will not have
1442 const dep = run.memory_blocked_steps.items[j];1421 // enough resources.
1443 assert(dep.max_rss != 0);1422 var remaining = run.max_rss - run.claimed_rss;
1444 if (dep.max_rss <= remaining) {1423 var i: usize = 0;
1445 remaining -= dep.max_rss;1424 for (run.memory_blocked_steps.items) |dep| {
14461425 assert(dep.max_rss != 0);
1447 thread_pool.spawnWg(wg, workerMakeOneStep, .{1426 if (dep.max_rss <= remaining) {
1448 wg, b, dep, prog_node, run,1427 remaining -= dep.max_rss;
1449 });1428 dispatch_deps.appendAssumeCapacity(dep);
1450 } else {1429 } else {
1451 run.memory_blocked_steps.items[i] = dep;1430 run.memory_blocked_steps.items[i] = dep;
1452 i += 1;1431 i += 1;
1432 }
1453 }1433 }
1434 run.memory_blocked_steps.shrinkRetainingCapacity(i);
1435 }
1436 for (dispatch_deps.items) |dep| {
1437 // Must be called without max_rss_mutex held in case it executes recursively.
1438 group.async(io, workerMakeOneStep, .{ group, b, dep, prog_node, run });
1454 }1439 }
1455 run.memory_blocked_steps.shrinkRetainingCapacity(i);
1456 }1440 }
1457}1441}
14581442
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
...@@ -472,6 +472,7 @@ pub const Manifest = struct {...@@ -472,6 +472,7 @@ pub const Manifest = struct {
472 /// A cache manifest file exists however it could not be parsed.472 /// A cache manifest file exists however it could not be parsed.
473 InvalidFormat,473 InvalidFormat,
474 OutOfMemory,474 OutOfMemory,
475 Canceled,
475 };476 };
476477
477 /// Check the cache to see if the input exists in it. If it exists, returns `true`.478 /// Check the cache to see if the input exists in it. If it exists, returns `true`.
...@@ -557,12 +558,14 @@ pub const Manifest = struct {...@@ -557,12 +558,14 @@ pub const Manifest = struct {
557 self.diagnostic = .{ .manifest_create = error.FileNotFound };558 self.diagnostic = .{ .manifest_create = error.FileNotFound };
558 return error.CacheCheckFailed;559 return error.CacheCheckFailed;
559 },560 },
561 error.Canceled => return error.Canceled,
560 else => |e| {562 else => |e| {
561 self.diagnostic = .{ .manifest_create = e };563 self.diagnostic = .{ .manifest_create = e };
562 return error.CacheCheckFailed;564 return error.CacheCheckFailed;
563 },565 },
564 }566 }
565 },567 },
568 error.Canceled => return error.Canceled,
566 else => |e| {569 else => |e| {
567 self.diagnostic = .{ .manifest_create = e };570 self.diagnostic = .{ .manifest_create = e };
568 return error.CacheCheckFailed;571 return error.CacheCheckFailed;
...@@ -760,6 +763,7 @@ pub const Manifest = struct {...@@ -760,6 +763,7 @@ pub const Manifest = struct {
760 // Every digest before this one has been populated successfully.763 // Every digest before this one has been populated successfully.
761 return .{ .miss = .{ .file_digests_populated = idx } };764 return .{ .miss = .{ .file_digests_populated = idx } };
762 },765 },
766 error.Canceled => return error.Canceled,
763 else => |e| {767 else => |e| {
764 self.diagnostic = .{ .file_open = .{768 self.diagnostic = .{ .file_open = .{
765 .file_index = idx,769 .file_index = idx,
...@@ -788,7 +792,7 @@ pub const Manifest = struct {...@@ -788,7 +792,7 @@ pub const Manifest = struct {
788 .inode = actual_stat.inode,792 .inode = actual_stat.inode,
789 };793 };
790794
791 if (self.isProblematicTimestamp(cache_hash_file.stat.mtime)) {795 if (try self.isProblematicTimestamp(cache_hash_file.stat.mtime)) {
792 // The actual file has an unreliable timestamp, force it to be hashed796 // The actual file has an unreliable timestamp, force it to be hashed
793 cache_hash_file.stat.mtime = .zero;797 cache_hash_file.stat.mtime = .zero;
794 cache_hash_file.stat.inode = 0;798 cache_hash_file.stat.inode = 0;
...@@ -846,7 +850,9 @@ pub const Manifest = struct {...@@ -846,7 +850,9 @@ pub const Manifest = struct {
846 }850 }
847 }851 }
848852
849 fn isProblematicTimestamp(man: *Manifest, timestamp: Io.Timestamp) bool {853 fn isProblematicTimestamp(man: *Manifest, timestamp: Io.Timestamp) error{Canceled}!bool {
854 const io = man.cache.io;
855
850 // If the file_time is prior to the most recent problematic timestamp856 // If the file_time is prior to the most recent problematic timestamp
851 // then we don't need to access the filesystem.857 // then we don't need to access the filesystem.
852 if (timestamp.nanoseconds < man.recent_problematic_timestamp.nanoseconds)858 if (timestamp.nanoseconds < man.recent_problematic_timestamp.nanoseconds)
...@@ -854,8 +860,8 @@ pub const Manifest = struct {...@@ -854,8 +860,8 @@ pub const Manifest = struct {
854860
855 // Next we will check the globally shared Cache timestamp, which is accessed861 // Next we will check the globally shared Cache timestamp, which is accessed
856 // from multiple threads.862 // from multiple threads.
857 man.cache.mutex.lock();863 try man.cache.mutex.lock(io);
858 defer man.cache.mutex.unlock();864 defer man.cache.mutex.unlock(io);
859865
860 // Save the global one to our local one to avoid locking next time.866 // Save the global one to our local one to avoid locking next time.
861 man.recent_problematic_timestamp = man.cache.recent_problematic_timestamp;867 man.recent_problematic_timestamp = man.cache.recent_problematic_timestamp;
...@@ -869,11 +875,18 @@ pub const Manifest = struct {...@@ -869,11 +875,18 @@ pub const Manifest = struct {
869 var file = man.cache.manifest_dir.createFile("timestamp", .{875 var file = man.cache.manifest_dir.createFile("timestamp", .{
870 .read = true,876 .read = true,
871 .truncate = true,877 .truncate = true,
872 }) catch return true;878 }) catch |err| switch (err) {
879 error.Canceled => return error.Canceled,
880 else => return true,
881 };
873 defer file.close();882 defer file.close();
874883
875 // Save locally and also save globally (we still hold the global lock).884 // Save locally and also save globally (we still hold the global lock).
876 man.recent_problematic_timestamp = (file.stat() catch return true).mtime;885 const stat = file.stat() catch |err| switch (err) {
886 error.Canceled => return error.Canceled,
887 else => return true,
888 };
889 man.recent_problematic_timestamp = stat.mtime;
877 man.cache.recent_problematic_timestamp = man.recent_problematic_timestamp;890 man.cache.recent_problematic_timestamp = man.recent_problematic_timestamp;
878 }891 }
879892
...@@ -900,7 +913,7 @@ pub const Manifest = struct {...@@ -900,7 +913,7 @@ pub const Manifest = struct {
900 .inode = actual_stat.inode,913 .inode = actual_stat.inode,
901 };914 };
902915
903 if (self.isProblematicTimestamp(ch_file.stat.mtime)) {916 if (try self.isProblematicTimestamp(ch_file.stat.mtime)) {
904 // The actual file has an unreliable timestamp, force it to be hashed917 // The actual file has an unreliable timestamp, force it to be hashed
905 ch_file.stat.mtime = .zero;918 ch_file.stat.mtime = .zero;
906 ch_file.stat.inode = 0;919 ch_file.stat.inode = 0;
...@@ -1036,7 +1049,7 @@ pub const Manifest = struct {...@@ -1036,7 +1049,7 @@ pub const Manifest = struct {
1036 .contents = null,1049 .contents = null,
1037 };1050 };
10381051
1039 if (self.isProblematicTimestamp(new_file.stat.mtime)) {1052 if (try self.isProblematicTimestamp(new_file.stat.mtime)) {
1040 // The actual file has an unreliable timestamp, force it to be hashed1053 // The actual file has an unreliable timestamp, force it to be hashed
1041 new_file.stat.mtime = .zero;1054 new_file.stat.mtime = .zero;
1042 new_file.stat.inode = 0;1055 new_file.stat.inode = 0;
lib/std/Build/Fuzz.zig+63-58
...@@ -22,17 +22,16 @@ mode: Mode,...@@ -22,17 +22,16 @@ mode: Mode,
22/// Allocated into `gpa`.22/// Allocated into `gpa`.
23run_steps: []const *Step.Run,23run_steps: []const *Step.Run,
2424
25wait_group: std.Thread.WaitGroup,25group: Io.Group,
26root_prog_node: std.Progress.Node,26root_prog_node: std.Progress.Node,
27prog_node: std.Progress.Node,27prog_node: std.Progress.Node,
28thread_pool: *std.Thread.Pool,
2928
30/// Protects `coverage_files`.29/// Protects `coverage_files`.
31coverage_mutex: std.Thread.Mutex,30coverage_mutex: Io.Mutex,
32coverage_files: std.AutoArrayHashMapUnmanaged(u64, CoverageMap),31coverage_files: std.AutoArrayHashMapUnmanaged(u64, CoverageMap),
3332
34queue_mutex: std.Thread.Mutex,33queue_mutex: Io.Mutex,
35queue_cond: std.Thread.Condition,34queue_cond: Io.Condition,
36msg_queue: std.ArrayList(Msg),35msg_queue: std.ArrayList(Msg),
3736
38pub const Mode = union(enum) {37pub const Mode = union(enum) {
...@@ -78,7 +77,6 @@ const CoverageMap = struct {...@@ -78,7 +77,6 @@ const CoverageMap = struct {
78pub fn init(77pub fn init(
79 gpa: Allocator,78 gpa: Allocator,
80 io: Io,79 io: Io,
81 thread_pool: *std.Thread.Pool,
82 ttyconf: tty.Config,80 ttyconf: tty.Config,
83 all_steps: []const *Build.Step,81 all_steps: []const *Build.Step,
84 root_prog_node: std.Progress.Node,82 root_prog_node: std.Progress.Node,
...@@ -89,20 +87,22 @@ pub fn init(...@@ -89,20 +87,22 @@ pub fn init(
89 defer steps.deinit(gpa);87 defer steps.deinit(gpa);
90 const rebuild_node = root_prog_node.start("Rebuilding Unit Tests", 0);88 const rebuild_node = root_prog_node.start("Rebuilding Unit Tests", 0);
91 defer rebuild_node.end();89 defer rebuild_node.end();
92 var rebuild_wg: std.Thread.WaitGroup = .{};90 var rebuild_group: Io.Group = .init;
93 defer rebuild_wg.wait();91 defer rebuild_group.cancel(io);
9492
95 for (all_steps) |step| {93 for (all_steps) |step| {
96 const run = step.cast(Step.Run) orelse continue;94 const run = step.cast(Step.Run) orelse continue;
97 if (run.producer == null) continue;95 if (run.producer == null) continue;
98 if (run.fuzz_tests.items.len == 0) continue;96 if (run.fuzz_tests.items.len == 0) continue;
99 try steps.append(gpa, run);97 try steps.append(gpa, run);
100 thread_pool.spawnWg(&rebuild_wg, rebuildTestsWorkerRun, .{ run, gpa, ttyconf, rebuild_node });98 rebuild_group.async(io, rebuildTestsWorkerRun, .{ run, gpa, ttyconf, rebuild_node });
101 }99 }
102100
103 if (steps.items.len == 0) fatal("no fuzz tests found", .{});101 if (steps.items.len == 0) fatal("no fuzz tests found", .{});
104 rebuild_node.setEstimatedTotalItems(steps.items.len);102 rebuild_node.setEstimatedTotalItems(steps.items.len);
105 break :steps try gpa.dupe(*Step.Run, steps.items);103 const run_steps = try gpa.dupe(*Step.Run, steps.items);
104 rebuild_group.wait(io);
105 break :steps run_steps;
106 };106 };
107 errdefer gpa.free(run_steps);107 errdefer gpa.free(run_steps);
108108
...@@ -118,42 +118,38 @@ pub fn init(...@@ -118,42 +118,38 @@ pub fn init(
118 .ttyconf = ttyconf,118 .ttyconf = ttyconf,
119 .mode = mode,119 .mode = mode,
120 .run_steps = run_steps,120 .run_steps = run_steps,
121 .wait_group = .{},121 .group = .init,
122 .thread_pool = thread_pool,
123 .root_prog_node = root_prog_node,122 .root_prog_node = root_prog_node,
124 .prog_node = .none,123 .prog_node = .none,
125 .coverage_files = .empty,124 .coverage_files = .empty,
126 .coverage_mutex = .{},125 .coverage_mutex = .init,
127 .queue_mutex = .{},126 .queue_mutex = .init,
128 .queue_cond = .{},127 .queue_cond = .{},
129 .msg_queue = .empty,128 .msg_queue = .empty,
130 };129 };
131}130}
132131
133pub fn start(fuzz: *Fuzz) void {132pub fn start(fuzz: *Fuzz) void {
133 const io = fuzz.io;
134 fuzz.prog_node = fuzz.root_prog_node.start("Fuzzing", fuzz.run_steps.len);134 fuzz.prog_node = fuzz.root_prog_node.start("Fuzzing", fuzz.run_steps.len);
135135
136 if (fuzz.mode == .forever) {136 if (fuzz.mode == .forever) {
137 // For polling messages and sending updates to subscribers.137 // For polling messages and sending updates to subscribers.
138 fuzz.wait_group.start();138 fuzz.group.concurrent(io, coverageRun, .{fuzz}) catch |err|
139 _ = std.Thread.spawn(.{}, coverageRun, .{fuzz}) catch |err| {139 fatal("unable to spawn coverage task: {t}", .{err});
140 fuzz.wait_group.finish();
141 fatal("unable to spawn coverage thread: {s}", .{@errorName(err)});
142 };
143 }140 }
144141
145 for (fuzz.run_steps) |run| {142 for (fuzz.run_steps) |run| {
146 for (run.fuzz_tests.items) |unit_test_index| {143 for (run.fuzz_tests.items) |unit_test_index| {
147 assert(run.rebuilt_executable != null);144 assert(run.rebuilt_executable != null);
148 fuzz.thread_pool.spawnWg(&fuzz.wait_group, fuzzWorkerRun, .{145 fuzz.group.async(io, fuzzWorkerRun, .{ fuzz, run, unit_test_index });
149 fuzz, run, unit_test_index,
150 });
151 }146 }
152 }147 }
153}148}
154149
155pub fn deinit(fuzz: *Fuzz) void {150pub fn deinit(fuzz: *Fuzz) void {
156 if (!fuzz.wait_group.isDone()) @panic("TODO: terminate the fuzzer processes");151 const io = fuzz.io;
152 fuzz.group.cancel(io);
157 fuzz.prog_node.end();153 fuzz.prog_node.end();
158 fuzz.gpa.free(fuzz.run_steps);154 fuzz.gpa.free(fuzz.run_steps);
159}155}
...@@ -161,9 +157,7 @@ pub fn deinit(fuzz: *Fuzz) void {...@@ -161,9 +157,7 @@ pub fn deinit(fuzz: *Fuzz) void {
161fn 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 {
162 rebuildTestsWorkerRunFallible(run, gpa, ttyconf, parent_prog_node) catch |err| {158 rebuildTestsWorkerRunFallible(run, gpa, ttyconf, parent_prog_node) catch |err| {
163 const compile = run.producer.?;159 const compile = run.producer.?;
164 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 });
165 compile.step.name, @errorName(err),
166 });
167 };161 };
168}162}
169163
...@@ -212,9 +206,7 @@ fn fuzzWorkerRun(...@@ -212,9 +206,7 @@ fn fuzzWorkerRun(
212 return;206 return;
213 },207 },
214 else => {208 else => {
215 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 });
216 run.step.name, test_name, @errorName(err),
217 });
218 return;210 return;
219 },211 },
220 };212 };
...@@ -273,8 +265,10 @@ pub fn sendUpdate(...@@ -273,8 +265,10 @@ pub fn sendUpdate(
273 socket: *std.http.Server.WebSocket,265 socket: *std.http.Server.WebSocket,
274 prev: *Previous,266 prev: *Previous,
275) !void {267) !void {
276 fuzz.coverage_mutex.lock();268 const io = fuzz.io;
277 defer fuzz.coverage_mutex.unlock();269
270 try fuzz.coverage_mutex.lock(io);
271 defer fuzz.coverage_mutex.unlock(io);
278272
279 const coverage_maps = fuzz.coverage_files.values();273 const coverage_maps = fuzz.coverage_files.values();
280 if (coverage_maps.len == 0) return;274 if (coverage_maps.len == 0) return;
...@@ -335,32 +329,41 @@ pub fn sendUpdate(...@@ -335,32 +329,41 @@ pub fn sendUpdate(
335}329}
336330
337fn coverageRun(fuzz: *Fuzz) void {331fn coverageRun(fuzz: *Fuzz) void {
338 defer fuzz.wait_group.finish();332 coverageRunCancelable(fuzz) catch |err| switch (err) {
333 error.Canceled => return,
334 };
335}
336
337fn coverageRunCancelable(fuzz: *Fuzz) Io.Cancelable!void {
338 const io = fuzz.io;
339339
340 fuzz.queue_mutex.lock();340 try fuzz.queue_mutex.lock(io);
341 defer fuzz.queue_mutex.unlock();341 defer fuzz.queue_mutex.unlock(io);
342342
343 while (true) {343 while (true) {
344 fuzz.queue_cond.wait(&fuzz.queue_mutex);344 try fuzz.queue_cond.wait(io, &fuzz.queue_mutex);
345 for (fuzz.msg_queue.items) |msg| switch (msg) {345 for (fuzz.msg_queue.items) |msg| switch (msg) {
346 .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) {
347 error.AlreadyReported => continue,347 error.AlreadyReported => continue,
348 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}),
349 },350 },
350 .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) {
351 error.AlreadyReported => continue,352 error.AlreadyReported => continue,
352 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}),
353 },355 },
354 };356 };
355 fuzz.msg_queue.clearRetainingCapacity();357 fuzz.msg_queue.clearRetainingCapacity();
356 }358 }
357}359}
358fn 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 {
359 assert(fuzz.mode == .forever);361 assert(fuzz.mode == .forever);
360 const ws = fuzz.mode.forever.ws;362 const ws = fuzz.mode.forever.ws;
363 const io = fuzz.io;
361364
362 fuzz.coverage_mutex.lock();365 try fuzz.coverage_mutex.lock(io);
363 defer fuzz.coverage_mutex.unlock();366 defer fuzz.coverage_mutex.unlock(io);
364367
365 const gop = try fuzz.coverage_files.getOrPut(fuzz.gpa, coverage_id);368 const gop = try fuzz.coverage_files.getOrPut(fuzz.gpa, coverage_id);
366 if (gop.found_existing) {369 if (gop.found_existing) {
...@@ -391,8 +394,8 @@ fn prepareTables(fuzz: *Fuzz, run_step: *Step.Run, coverage_id: u64) error{ OutO...@@ -391,8 +394,8 @@ fn prepareTables(fuzz: *Fuzz, run_step: *Step.Run, coverage_id: u64) error{ OutO
391 target.ofmt,394 target.ofmt,
392 target.cpu.arch,395 target.cpu.arch,
393 ) catch |err| {396 ) catch |err| {
394 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}", .{
395 run_step.step.name, rebuilt_exe_path, @errorName(err),398 run_step.step.name, rebuilt_exe_path, err,
396 });399 });
397 return error.AlreadyReported;400 return error.AlreadyReported;
398 };401 };
...@@ -403,15 +406,15 @@ fn prepareTables(fuzz: *Fuzz, run_step: *Step.Run, coverage_id: u64) error{ OutO...@@ -403,15 +406,15 @@ fn prepareTables(fuzz: *Fuzz, run_step: *Step.Run, coverage_id: u64) error{ OutO
403 .sub_path = "v/" ++ std.fmt.hex(coverage_id),406 .sub_path = "v/" ++ std.fmt.hex(coverage_id),
404 };407 };
405 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| {
406 log.err("step '{s}': failed to load coverage file '{f}': {s}", .{409 log.err("step '{s}': failed to load coverage file '{f}': {t}", .{
407 run_step.step.name, coverage_file_path, @errorName(err),410 run_step.step.name, coverage_file_path, err,
408 });411 });
409 return error.AlreadyReported;412 return error.AlreadyReported;
410 };413 };
411 defer coverage_file.close();414 defer coverage_file.close();
412415
413 const file_size = coverage_file.getEndPos() catch |err| {416 const file_size = coverage_file.getEndPos() catch |err| {
414 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 });
415 return error.AlreadyReported;418 return error.AlreadyReported;
416 };419 };
417420
...@@ -423,7 +426,7 @@ fn prepareTables(fuzz: *Fuzz, run_step: *Step.Run, coverage_id: u64) error{ OutO...@@ -423,7 +426,7 @@ fn prepareTables(fuzz: *Fuzz, run_step: *Step.Run, coverage_id: u64) error{ OutO
423 coverage_file.handle,426 coverage_file.handle,
424 0,427 0,
425 ) catch |err| {428 ) catch |err| {
426 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 });
427 return error.AlreadyReported;430 return error.AlreadyReported;
428 };431 };
429 gop.value_ptr.mapped_memory = mapped_memory;432 gop.value_ptr.mapped_memory = mapped_memory;
...@@ -449,7 +452,7 @@ fn prepareTables(fuzz: *Fuzz, run_step: *Step.Run, coverage_id: u64) error{ OutO...@@ -449,7 +452,7 @@ fn prepareTables(fuzz: *Fuzz, run_step: *Step.Run, coverage_id: u64) error{ OutO
449 }{ .addrs = sorted_pcs.items(.pc) });452 }{ .addrs = sorted_pcs.items(.pc) });
450453
451 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| {
452 log.err("failed to resolve addresses to source locations: {s}", .{@errorName(err)});455 log.err("failed to resolve addresses to source locations: {t}", .{err});
453 return error.AlreadyReported;456 return error.AlreadyReported;
454 };457 };
455458
...@@ -459,9 +462,11 @@ fn prepareTables(fuzz: *Fuzz, run_step: *Step.Run, coverage_id: u64) error{ OutO...@@ -459,9 +462,11 @@ fn prepareTables(fuzz: *Fuzz, run_step: *Step.Run, coverage_id: u64) error{ OutO
459 ws.notifyUpdate();462 ws.notifyUpdate();
460}463}
461464
462fn 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 {
463 fuzz.coverage_mutex.lock();466 const io = fuzz.io;
464 defer fuzz.coverage_mutex.unlock();467
468 try fuzz.coverage_mutex.lock(io);
469 defer fuzz.coverage_mutex.unlock(io);
465470
466 const coverage_map = fuzz.coverage_files.getPtr(coverage_id).?;471 const coverage_map = fuzz.coverage_files.getPtr(coverage_id).?;
467 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)]);
...@@ -511,8 +516,8 @@ pub fn waitAndPrintReport(fuzz: *Fuzz) void {...@@ -511,8 +516,8 @@ pub fn waitAndPrintReport(fuzz: *Fuzz) void {
511 assert(fuzz.mode == .limit);516 assert(fuzz.mode == .limit);
512 const io = fuzz.io;517 const io = fuzz.io;
513518
514 fuzz.wait_group.wait();519 fuzz.group.wait(io);
515 fuzz.wait_group.reset();520 fuzz.group = .init;
516521
517 std.debug.print("======= FUZZING REPORT =======\n", .{});522 std.debug.print("======= FUZZING REPORT =======\n", .{});
518 for (fuzz.msg_queue.items) |msg| {523 for (fuzz.msg_queue.items) |msg| {
...@@ -524,8 +529,8 @@ pub fn waitAndPrintReport(fuzz: *Fuzz) void {...@@ -524,8 +529,8 @@ pub fn waitAndPrintReport(fuzz: *Fuzz) void {
524 .sub_path = "v/" ++ std.fmt.hex(cov.id),529 .sub_path = "v/" ++ std.fmt.hex(cov.id),
525 };530 };
526 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| {
527 fatal("step '{s}': failed to load coverage file '{f}': {s}", .{532 fatal("step '{s}': failed to load coverage file '{f}': {t}", .{
528 cov.run.step.name, coverage_file_path, @errorName(err),533 cov.run.step.name, coverage_file_path, err,
529 });534 });
530 };535 };
531 defer coverage_file.close();536 defer coverage_file.close();
...@@ -536,8 +541,8 @@ pub fn waitAndPrintReport(fuzz: *Fuzz) void {...@@ -536,8 +541,8 @@ pub fn waitAndPrintReport(fuzz: *Fuzz) void {
536541
537 var header: fuzz_abi.SeenPcsHeader = undefined;542 var header: fuzz_abi.SeenPcsHeader = undefined;
538 r.interface.readSliceAll(std.mem.asBytes(&header)) catch |err| {543 r.interface.readSliceAll(std.mem.asBytes(&header)) catch |err| {
539 fatal("step '{s}': failed to read from coverage file '{f}': {s}", .{544 fatal("step '{s}': failed to read from coverage file '{f}': {t}", .{
540 cov.run.step.name, coverage_file_path, @errorName(err),545 cov.run.step.name, coverage_file_path, err,
541 });546 });
542 };547 };
543548
...@@ -551,8 +556,8 @@ pub fn waitAndPrintReport(fuzz: *Fuzz) void {...@@ -551,8 +556,8 @@ pub fn waitAndPrintReport(fuzz: *Fuzz) void {
551 const chunk_count = fuzz_abi.SeenPcsHeader.seenElemsLen(header.pcs_len);556 const chunk_count = fuzz_abi.SeenPcsHeader.seenElemsLen(header.pcs_len);
552 for (0..chunk_count) |_| {557 for (0..chunk_count) |_| {
553 const seen = r.interface.takeInt(usize, .little) catch |err| {558 const seen = r.interface.takeInt(usize, .little) catch |err| {
554 fatal("step '{s}': failed to read from coverage file '{f}': {s}", .{559 fatal("step '{s}': failed to read from coverage file '{f}': {t}", .{
555 cov.run.step.name, coverage_file_path, @errorName(err),560 cov.run.step.name, coverage_file_path, err,
556 });561 });
557 };562 };
558 seen_count += @popCount(seen);563 seen_count += @popCount(seen);
lib/std/Build/Step.zig+19-19
...@@ -110,7 +110,6 @@ pub const TestResults = struct {...@@ -110,7 +110,6 @@ pub const TestResults = struct {
110110
111pub const MakeOptions = struct {111pub const MakeOptions = struct {
112 progress_node: std.Progress.Node,112 progress_node: std.Progress.Node,
113 thread_pool: *std.Thread.Pool,
114 watch: bool,113 watch: bool,
115 web_server: switch (builtin.target.cpu.arch) {114 web_server: switch (builtin.target.cpu.arch) {
116 else => ?*Build.WebServer,115 else => ?*Build.WebServer,
...@@ -363,7 +362,7 @@ pub fn captureChildProcess(...@@ -363,7 +362,7 @@ pub fn captureChildProcess(
363 .allocator = arena,362 .allocator = arena,
364 .argv = argv,363 .argv = argv,
365 .progress_node = progress_node,364 .progress_node = progress_node,
366 }) 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 });
367366
368 if (result.stderr.len > 0) {367 if (result.stderr.len > 0) {
369 try s.result_error_msgs.append(arena, result.stderr);368 try s.result_error_msgs.append(arena, result.stderr);
...@@ -413,7 +412,7 @@ pub fn evalZigProcess(...@@ -413,7 +412,7 @@ pub fn evalZigProcess(
413 error.BrokenPipe => {412 error.BrokenPipe => {
414 // Process restart required.413 // Process restart required.
415 const term = zp.child.wait() catch |e| {414 const term = zp.child.wait() catch |e| {
416 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 });
417 };416 };
418 _ = term;417 _ = term;
419 s.clearZigProcess(gpa);418 s.clearZigProcess(gpa);
...@@ -429,7 +428,7 @@ pub fn evalZigProcess(...@@ -429,7 +428,7 @@ pub fn evalZigProcess(
429 if (s.result_error_msgs.items.len > 0 and result == null) {428 if (s.result_error_msgs.items.len > 0 and result == null) {
430 // Crash detected.429 // Crash detected.
431 const term = zp.child.wait() catch |e| {430 const term = zp.child.wait() catch |e| {
432 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 });
433 };432 };
434 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;
435 s.clearZigProcess(gpa);434 s.clearZigProcess(gpa);
...@@ -454,9 +453,7 @@ pub fn evalZigProcess(...@@ -454,9 +453,7 @@ pub fn evalZigProcess(
454 child.request_resource_usage_statistics = true;453 child.request_resource_usage_statistics = true;
455 child.progress_node = prog_node;454 child.progress_node = prog_node;
456455
457 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 });
458 argv[0], @errorName(err),
459 });
460457
461 const zp = try gpa.create(ZigProcess);458 const zp = try gpa.create(ZigProcess);
462 zp.* = .{459 zp.* = .{
...@@ -481,7 +478,7 @@ pub fn evalZigProcess(...@@ -481,7 +478,7 @@ pub fn evalZigProcess(
481 zp.child.stdin = null;478 zp.child.stdin = null;
482479
483 const term = zp.child.wait() catch |err| {480 const term = zp.child.wait() catch |err| {
484 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 });
485 };482 };
486 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;
487484
...@@ -514,8 +511,8 @@ pub fn installFile(s: *Step, src_lazy_path: Build.LazyPath, dest_path: []const u...@@ -514,8 +511,8 @@ pub fn installFile(s: *Step, src_lazy_path: Build.LazyPath, dest_path: []const u
514 const src_path = src_lazy_path.getPath3(b, s);511 const src_path = src_lazy_path.getPath3(b, s);
515 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 });
516 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| {
517 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}", .{
518 src_path, dest_path, @errorName(err),515 src_path, dest_path, err,
519 });516 });
520 };517 };
521}518}
...@@ -525,9 +522,7 @@ pub fn installDir(s: *Step, dest_path: []const u8) !std.fs.Dir.MakePathStatus {...@@ -525,9 +522,7 @@ pub fn installDir(s: *Step, dest_path: []const u8) !std.fs.Dir.MakePathStatus {
525 const b = s.owner;522 const b = s.owner;
526 try handleVerbose(b, null, &.{ "install", "-d", dest_path });523 try handleVerbose(b, null, &.{ "install", "-d", dest_path });
527 return std.fs.cwd().makePathStatus(dest_path) catch |err| {524 return std.fs.cwd().makePathStatus(dest_path) catch |err| {
528 return s.fail("unable to create dir '{s}': {s}", .{525 return s.fail("unable to create dir '{s}': {t}", .{ dest_path, err });
529 dest_path, @errorName(err),
530 });
531 };526 };
532}527}
533528
...@@ -826,22 +821,27 @@ pub fn cacheHitAndWatch(s: *Step, man: *Build.Cache.Manifest) !bool {...@@ -826,22 +821,27 @@ pub fn cacheHitAndWatch(s: *Step, man: *Build.Cache.Manifest) !bool {
826 return is_hit;821 return is_hit;
827}822}
828823
829fn 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 } {
830 switch (err) {829 switch (err) {
831 error.CacheCheckFailed => switch (man.diagnostic) {830 error.CacheCheckFailed => switch (man.diagnostic) {
832 .none => unreachable,831 .none => unreachable,
833 .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}", .{
834 @tagName(man.diagnostic), @errorName(e),833 man.diagnostic, e,
835 }),834 }),
836 .file_open, .file_stat, .file_read, .file_hash => |op| {835 .file_open, .file_stat, .file_read, .file_hash => |op| {
837 const pp = man.files.keys()[op.file_index].prefixed_path;836 const pp = man.files.keys()[op.file_index].prefixed_path;
838 const prefix = man.cache.prefixes()[pp.prefix].path orelse "";837 const prefix = man.cache.prefixes()[pp.prefix].path orelse "";
839 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}", .{
840 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,
841 });840 });
842 },841 },
843 },842 },
844 error.OutOfMemory => return error.OutOfMemory,843 error.OutOfMemory => return error.OutOfMemory,
844 error.Canceled => return error.Canceled,
845 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", .{}),
846 }846 }
847}847}
...@@ -851,7 +851,7 @@ fn failWithCacheError(s: *Step, man: *const Build.Cache.Manifest, err: Build.Cac...@@ -851,7 +851,7 @@ fn failWithCacheError(s: *Step, man: *const Build.Cache.Manifest, err: Build.Cac
851pub fn writeManifest(s: *Step, man: *Build.Cache.Manifest) !void {851pub fn writeManifest(s: *Step, man: *Build.Cache.Manifest) !void {
852 if (s.test_results.isSuccess()) {852 if (s.test_results.isSuccess()) {
853 man.writeManifest() catch |err| {853 man.writeManifest() catch |err| {
854 try s.addError("unable to write cache manifest: {s}", .{@errorName(err)});854 try s.addError("unable to write cache manifest: {t}", .{err});
855 };855 };
856 }856 }
857}857}
lib/std/Build/Step/Run.zig+7-7
...@@ -1151,7 +1151,6 @@ pub fn rerunInFuzzMode(...@@ -1151,7 +1151,6 @@ pub fn rerunInFuzzMode(
1151 const tmp_dir_path = "tmp" ++ fs.path.sep_str ++ std.fmt.hex(rand_int);1151 const tmp_dir_path = "tmp" ++ fs.path.sep_str ++ std.fmt.hex(rand_int);
1152 try runCommand(run, argv_list.items, has_side_effects, tmp_dir_path, .{1152 try runCommand(run, argv_list.items, has_side_effects, tmp_dir_path, .{
1153 .progress_node = prog_node,1153 .progress_node = prog_node,
1154 .thread_pool = undefined, // not used by `runCommand`
1155 .watch = undefined, // not used by `runCommand`1154 .watch = undefined, // not used by `runCommand`
1156 .web_server = null, // only needed for time reports1155 .web_server = null, // only needed for time reports
1157 .ttyconf = fuzz.ttyconf,1156 .ttyconf = fuzz.ttyconf,
...@@ -1831,6 +1830,7 @@ fn pollZigTest(...@@ -1831,6 +1830,7 @@ fn pollZigTest(
1831} {1830} {
1832 const gpa = run.step.owner.allocator;1831 const gpa = run.step.owner.allocator;
1833 const arena = run.step.owner.allocator;1832 const arena = run.step.owner.allocator;
1833 const io = run.step.owner.graph.io;
18341834
1835 var sub_prog_node: ?std.Progress.Node = null;1835 var sub_prog_node: ?std.Progress.Node = null;
1836 defer if (sub_prog_node) |n| n.end();1836 defer if (sub_prog_node) |n| n.end();
...@@ -2036,8 +2036,8 @@ fn pollZigTest(...@@ -2036,8 +2036,8 @@ fn pollZigTest(
20362036
2037 {2037 {
2038 const fuzz = fuzz_context.?.fuzz;2038 const fuzz = fuzz_context.?.fuzz;
2039 fuzz.queue_mutex.lock();2039 fuzz.queue_mutex.lockUncancelable(io);
2040 defer fuzz.queue_mutex.unlock();2040 defer fuzz.queue_mutex.unlock(io);
2041 try fuzz.msg_queue.append(fuzz.gpa, .{ .coverage = .{2041 try fuzz.msg_queue.append(fuzz.gpa, .{ .coverage = .{
2042 .id = coverage_id.?,2042 .id = coverage_id.?,
2043 .cumulative = .{2043 .cumulative = .{
...@@ -2047,20 +2047,20 @@ fn pollZigTest(...@@ -2047,20 +2047,20 @@ fn pollZigTest(
2047 },2047 },
2048 .run = run,2048 .run = run,
2049 } });2049 } });
2050 fuzz.queue_cond.signal();2050 fuzz.queue_cond.signal(io);
2051 }2051 }
2052 },2052 },
2053 .fuzz_start_addr => {2053 .fuzz_start_addr => {
2054 const fuzz = fuzz_context.?.fuzz;2054 const fuzz = fuzz_context.?.fuzz;
2055 const addr = body_r.takeInt(u64, .little) catch unreachable;2055 const addr = body_r.takeInt(u64, .little) catch unreachable;
2056 {2056 {
2057 fuzz.queue_mutex.lock();2057 fuzz.queue_mutex.lockUncancelable(io);
2058 defer fuzz.queue_mutex.unlock();2058 defer fuzz.queue_mutex.unlock(io);
2059 try fuzz.msg_queue.append(fuzz.gpa, .{ .entry_point = .{2059 try fuzz.msg_queue.append(fuzz.gpa, .{ .entry_point = .{
2060 .addr = addr,2060 .addr = addr,
2061 .coverage_id = coverage_id.?,2061 .coverage_id = coverage_id.?,
2062 } });2062 } });
2063 fuzz.queue_cond.signal();2063 fuzz.queue_cond.signal(io);
2064 }2064 }
2065 },2065 },
2066 else => {}, // ignore other messages2066 else => {}, // ignore other messages
lib/std/Build/WebServer.zig+44-37
...@@ -1,5 +1,4 @@...@@ -1,5 +1,4 @@
1gpa: Allocator,1gpa: Allocator,
2thread_pool: *std.Thread.Pool,
3graph: *const Build.Graph,2graph: *const Build.Graph,
4all_steps: []const *Build.Step,3all_steps: []const *Build.Step,
5listen_address: net.IpAddress,4listen_address: net.IpAddress,
...@@ -20,7 +19,7 @@ step_names_trailing: []u8,...@@ -20,7 +19,7 @@ step_names_trailing: []u8,
20step_status_bits: []u8,19step_status_bits: []u8,
2120
22fuzz: ?Fuzz,21fuzz: ?Fuzz,
23time_report_mutex: std.Thread.Mutex,22time_report_mutex: Io.Mutex,
24time_report_msgs: [][]u8,23time_report_msgs: [][]u8,
25time_report_update_times: []i64,24time_report_update_times: []i64,
2625
...@@ -34,9 +33,9 @@ build_status: std.atomic.Value(abi.BuildStatus),...@@ -34,9 +33,9 @@ build_status: std.atomic.Value(abi.BuildStatus),
34/// an unreasonable number of packets.33/// an unreasonable number of packets.
35update_id: std.atomic.Value(u32),34update_id: std.atomic.Value(u32),
3635
37runner_request_mutex: std.Thread.Mutex,36runner_request_mutex: Io.Mutex,
38runner_request_ready_cond: std.Thread.Condition,37runner_request_ready_cond: Io.Condition,
39runner_request_empty_cond: std.Thread.Condition,38runner_request_empty_cond: Io.Condition,
40runner_request: ?RunnerRequest,39runner_request: ?RunnerRequest,
4140
42/// 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
...@@ -53,7 +52,6 @@ pub fn notifyUpdate(ws: *WebServer) void {...@@ -53,7 +52,6 @@ pub fn notifyUpdate(ws: *WebServer) void {
5352
54pub const Options = struct {53pub const Options = struct {
55 gpa: Allocator,54 gpa: Allocator,
56 thread_pool: *std.Thread.Pool,
57 ttyconf: Io.tty.Config,55 ttyconf: Io.tty.Config,
58 graph: *const std.Build.Graph,56 graph: *const std.Build.Graph,
59 all_steps: []const *Build.Step,57 all_steps: []const *Build.Step,
...@@ -100,7 +98,6 @@ pub fn init(opts: Options) WebServer {...@@ -100,7 +98,6 @@ pub fn init(opts: Options) WebServer {
10098
101 return .{99 return .{
102 .gpa = opts.gpa,100 .gpa = opts.gpa,
103 .thread_pool = opts.thread_pool,
104 .ttyconf = opts.ttyconf,101 .ttyconf = opts.ttyconf,
105 .graph = opts.graph,102 .graph = opts.graph,
106 .all_steps = all_steps,103 .all_steps = all_steps,
...@@ -117,14 +114,14 @@ pub fn init(opts: Options) WebServer {...@@ -117,14 +114,14 @@ pub fn init(opts: Options) WebServer {
117 .step_status_bits = step_status_bits,114 .step_status_bits = step_status_bits,
118115
119 .fuzz = null,116 .fuzz = null,
120 .time_report_mutex = .{},117 .time_report_mutex = .init,
121 .time_report_msgs = time_report_msgs,118 .time_report_msgs = time_report_msgs,
122 .time_report_update_times = time_report_update_times,119 .time_report_update_times = time_report_update_times,
123120
124 .build_status = .init(.idle),121 .build_status = .init(.idle),
125 .update_id = .init(0),122 .update_id = .init(0),
126123
127 .runner_request_mutex = .{},124 .runner_request_mutex = .init,
128 .runner_request_ready_cond = .{},125 .runner_request_ready_cond = .{},
129 .runner_request_empty_cond = .{},126 .runner_request_empty_cond = .{},
130 .runner_request = null,127 .runner_request = null,
...@@ -235,7 +232,6 @@ pub fn finishBuild(ws: *WebServer, opts: struct {...@@ -235,7 +232,6 @@ pub fn finishBuild(ws: *WebServer, opts: struct {
235 ws.fuzz = Fuzz.init(232 ws.fuzz = Fuzz.init(
236 ws.gpa,233 ws.gpa,
237 ws.graph.io,234 ws.graph.io,
238 ws.thread_pool,
239 ws.ttyconf,235 ws.ttyconf,
240 ws.all_steps,236 ws.all_steps,
241 ws.root_prog_node,237 ws.root_prog_node,
...@@ -300,6 +296,8 @@ fn accept(ws: *WebServer, stream: net.Stream) void {...@@ -300,6 +296,8 @@ fn accept(ws: *WebServer, stream: net.Stream) void {
300}296}
301297
302fn serveWebSocket(ws: *WebServer, sock: *http.Server.WebSocket) !noreturn {298fn serveWebSocket(ws: *WebServer, sock: *http.Server.WebSocket) !noreturn {
299 const io = ws.graph.io;
300
303 var prev_build_status = ws.build_status.load(.monotonic);301 var prev_build_status = ws.build_status.load(.monotonic);
304302
305 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);
...@@ -335,8 +333,8 @@ fn serveWebSocket(ws: *WebServer, sock: *http.Server.WebSocket) !noreturn {...@@ -335,8 +333,8 @@ fn serveWebSocket(ws: *WebServer, sock: *http.Server.WebSocket) !noreturn {
335 }333 }
336334
337 {335 {
338 ws.time_report_mutex.lock();336 try ws.time_report_mutex.lock(io);
339 defer ws.time_report_mutex.unlock();337 defer ws.time_report_mutex.unlock(io);
340 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| {
341 if (update_time <= prev_time) continue;339 if (update_time <= prev_time) continue;
342 // 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
...@@ -344,8 +342,8 @@ fn serveWebSocket(ws: *WebServer, sock: *http.Server.WebSocket) !noreturn {...@@ -344,8 +342,8 @@ fn serveWebSocket(ws: *WebServer, sock: *http.Server.WebSocket) !noreturn {
344 const owned_msg = try ws.gpa.dupe(u8, msg);342 const owned_msg = try ws.gpa.dupe(u8, msg);
345 defer ws.gpa.free(owned_msg);343 defer ws.gpa.free(owned_msg);
346 // Temporarily unlock, then re-lock after the message is sent.344 // Temporarily unlock, then re-lock after the message is sent.
347 ws.time_report_mutex.unlock();345 ws.time_report_mutex.unlock(io);
348 defer ws.time_report_mutex.lock();346 defer ws.time_report_mutex.lockUncancelable(io);
349 try sock.writeMessage(owned_msg, .binary);347 try sock.writeMessage(owned_msg, .binary);
350 }348 }
351 }349 }
...@@ -386,6 +384,8 @@ fn serveWebSocket(ws: *WebServer, sock: *http.Server.WebSocket) !noreturn {...@@ -386,6 +384,8 @@ fn serveWebSocket(ws: *WebServer, sock: *http.Server.WebSocket) !noreturn {
386 }384 }
387}385}
388fn recvWebSocketMessages(ws: *WebServer, sock: *http.Server.WebSocket) void {386fn recvWebSocketMessages(ws: *WebServer, sock: *http.Server.WebSocket) void {
387 const io = ws.graph.io;
388
389 while (true) {389 while (true) {
390 const msg = sock.readSmallMessage() catch return;390 const msg = sock.readSmallMessage() catch return;
391 if (msg.opcode != .binary) continue;391 if (msg.opcode != .binary) continue;
...@@ -394,14 +394,16 @@ fn recvWebSocketMessages(ws: *WebServer, sock: *http.Server.WebSocket) void {...@@ -394,14 +394,16 @@ fn recvWebSocketMessages(ws: *WebServer, sock: *http.Server.WebSocket) void {
394 switch (tag) {394 switch (tag) {
395 _ => continue,395 _ => continue,
396 .rebuild => while (true) {396 .rebuild => while (true) {
397 ws.runner_request_mutex.lock();397 ws.runner_request_mutex.lock(io) catch |err| switch (err) {
398 defer ws.runner_request_mutex.unlock();398 error.Canceled => return,
399 };
400 defer ws.runner_request_mutex.unlock(io);
399 if (ws.runner_request == null) {401 if (ws.runner_request == null) {
400 ws.runner_request = .rebuild;402 ws.runner_request = .rebuild;
401 ws.runner_request_ready_cond.signal();403 ws.runner_request_ready_cond.signal(io);
402 break;404 break;
403 }405 }
404 ws.runner_request_empty_cond.wait(&ws.runner_request_mutex);406 ws.runner_request_empty_cond.wait(io, &ws.runner_request_mutex) catch return;
405 },407 },
406 }408 }
407 }409 }
...@@ -695,14 +697,15 @@ pub fn updateTimeReportCompile(ws: *WebServer, opts: struct {...@@ -695,14 +697,15 @@ pub fn updateTimeReportCompile(ws: *WebServer, opts: struct {
695 trailing: []const u8,697 trailing: []const u8,
696}) void {698}) void {
697 const gpa = ws.gpa;699 const gpa = ws.gpa;
700 const io = ws.graph.io;
698701
699 const step_idx: u32 = for (ws.all_steps, 0..) |s, i| {702 const step_idx: u32 = for (ws.all_steps, 0..) |s, i| {
700 if (s == &opts.compile.step) break @intCast(i);703 if (s == &opts.compile.step) break @intCast(i);
701 } else unreachable;704 } else unreachable;
702705
703 const old_buf = old: {706 const old_buf = old: {
704 ws.time_report_mutex.lock();707 ws.time_report_mutex.lock(io) catch return;
705 defer ws.time_report_mutex.unlock();708 defer ws.time_report_mutex.unlock(io);
706 const old = ws.time_report_msgs[step_idx];709 const old = ws.time_report_msgs[step_idx];
707 ws.time_report_msgs[step_idx] = &.{};710 ws.time_report_msgs[step_idx] = &.{};
708 break :old old;711 break :old old;
...@@ -724,8 +727,8 @@ pub fn updateTimeReportCompile(ws: *WebServer, opts: struct {...@@ -724,8 +727,8 @@ pub fn updateTimeReportCompile(ws: *WebServer, opts: struct {
724 @memcpy(buf[@sizeOf(abi.time_report.CompileResult)..], opts.trailing);727 @memcpy(buf[@sizeOf(abi.time_report.CompileResult)..], opts.trailing);
725728
726 {729 {
727 ws.time_report_mutex.lock();730 ws.time_report_mutex.lock(io) catch return;
728 defer ws.time_report_mutex.unlock();731 defer ws.time_report_mutex.unlock(io);
729 assert(ws.time_report_msgs[step_idx].len == 0);732 assert(ws.time_report_msgs[step_idx].len == 0);
730 ws.time_report_msgs[step_idx] = buf;733 ws.time_report_msgs[step_idx] = buf;
731 ws.time_report_update_times[step_idx] = ws.now();734 ws.time_report_update_times[step_idx] = ws.now();
...@@ -735,14 +738,15 @@ pub fn updateTimeReportCompile(ws: *WebServer, opts: struct {...@@ -735,14 +738,15 @@ pub fn updateTimeReportCompile(ws: *WebServer, opts: struct {
735738
736pub fn updateTimeReportGeneric(ws: *WebServer, step: *Build.Step, ns_total: u64) void {739pub fn updateTimeReportGeneric(ws: *WebServer, step: *Build.Step, ns_total: u64) void {
737 const gpa = ws.gpa;740 const gpa = ws.gpa;
741 const io = ws.graph.io;
738742
739 const step_idx: u32 = for (ws.all_steps, 0..) |s, i| {743 const step_idx: u32 = for (ws.all_steps, 0..) |s, i| {
740 if (s == step) break @intCast(i);744 if (s == step) break @intCast(i);
741 } else unreachable;745 } else unreachable;
742746
743 const old_buf = old: {747 const old_buf = old: {
744 ws.time_report_mutex.lock();748 ws.time_report_mutex.lock(io) catch return;
745 defer ws.time_report_mutex.unlock();749 defer ws.time_report_mutex.unlock(io);
746 const old = ws.time_report_msgs[step_idx];750 const old = ws.time_report_msgs[step_idx];
747 ws.time_report_msgs[step_idx] = &.{};751 ws.time_report_msgs[step_idx] = &.{};
748 break :old old;752 break :old old;
...@@ -754,8 +758,8 @@ pub fn updateTimeReportGeneric(ws: *WebServer, step: *Build.Step, ns_total: u64)...@@ -754,8 +758,8 @@ pub fn updateTimeReportGeneric(ws: *WebServer, step: *Build.Step, ns_total: u64)
754 .ns_total = ns_total,758 .ns_total = ns_total,
755 };759 };
756 {760 {
757 ws.time_report_mutex.lock();761 ws.time_report_mutex.lock(io) catch return;
758 defer ws.time_report_mutex.unlock();762 defer ws.time_report_mutex.unlock(io);
759 assert(ws.time_report_msgs[step_idx].len == 0);763 assert(ws.time_report_msgs[step_idx].len == 0);
760 ws.time_report_msgs[step_idx] = buf;764 ws.time_report_msgs[step_idx] = buf;
761 ws.time_report_update_times[step_idx] = ws.now();765 ws.time_report_update_times[step_idx] = ws.now();
...@@ -770,6 +774,7 @@ pub fn updateTimeReportRunTest(...@@ -770,6 +774,7 @@ pub fn updateTimeReportRunTest(
770 ns_per_test: []const u64,774 ns_per_test: []const u64,
771) void {775) void {
772 const gpa = ws.gpa;776 const gpa = ws.gpa;
777 const io = ws.graph.io;
773778
774 const step_idx: u32 = for (ws.all_steps, 0..) |s, i| {779 const step_idx: u32 = for (ws.all_steps, 0..) |s, i| {
775 if (s == &run.step) break @intCast(i);780 if (s == &run.step) break @intCast(i);
...@@ -786,8 +791,8 @@ pub fn updateTimeReportRunTest(...@@ -786,8 +791,8 @@ pub fn updateTimeReportRunTest(
786 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;
787 };792 };
788 const old_buf = old: {793 const old_buf = old: {
789 ws.time_report_mutex.lock();794 ws.time_report_mutex.lock(io) catch return;
790 defer ws.time_report_mutex.unlock();795 defer ws.time_report_mutex.unlock(io);
791 const old = ws.time_report_msgs[step_idx];796 const old = ws.time_report_msgs[step_idx];
792 ws.time_report_msgs[step_idx] = &.{};797 ws.time_report_msgs[step_idx] = &.{};
793 break :old old;798 break :old old;
...@@ -812,8 +817,8 @@ pub fn updateTimeReportRunTest(...@@ -812,8 +817,8 @@ pub fn updateTimeReportRunTest(
812 assert(offset == buf.len);817 assert(offset == buf.len);
813818
814 {819 {
815 ws.time_report_mutex.lock();820 ws.time_report_mutex.lock(io) catch return;
816 defer ws.time_report_mutex.unlock();821 defer ws.time_report_mutex.unlock(io);
817 assert(ws.time_report_msgs[step_idx].len == 0);822 assert(ws.time_report_msgs[step_idx].len == 0);
818 ws.time_report_msgs[step_idx] = buf;823 ws.time_report_msgs[step_idx] = buf;
819 ws.time_report_update_times[step_idx] = ws.now();824 ws.time_report_update_times[step_idx] = ws.now();
...@@ -825,8 +830,9 @@ const RunnerRequest = union(enum) {...@@ -825,8 +830,9 @@ const RunnerRequest = union(enum) {
825 rebuild,830 rebuild,
826};831};
827pub fn getRunnerRequest(ws: *WebServer) ?RunnerRequest {832pub fn getRunnerRequest(ws: *WebServer) ?RunnerRequest {
828 ws.runner_request_mutex.lock();833 const io = ws.graph.io;
829 defer ws.runner_request_mutex.unlock();834 ws.runner_request_mutex.lock(io) catch return;
835 defer ws.runner_request_mutex.unlock(io);
830 if (ws.runner_request) |req| {836 if (ws.runner_request) |req| {
831 ws.runner_request = null;837 ws.runner_request = null;
832 ws.runner_request_empty_cond.signal();838 ws.runner_request_empty_cond.signal();
...@@ -834,16 +840,17 @@ pub fn getRunnerRequest(ws: *WebServer) ?RunnerRequest {...@@ -834,16 +840,17 @@ pub fn getRunnerRequest(ws: *WebServer) ?RunnerRequest {
834 }840 }
835 return null;841 return null;
836}842}
837pub fn wait(ws: *WebServer) RunnerRequest {843pub fn wait(ws: *WebServer) Io.Cancelable!RunnerRequest {
838 ws.runner_request_mutex.lock();844 const io = ws.graph.io;
839 defer ws.runner_request_mutex.unlock();845 try ws.runner_request_mutex.lock(io);
846 defer ws.runner_request_mutex.unlock(io);
840 while (true) {847 while (true) {
841 if (ws.runner_request) |req| {848 if (ws.runner_request) |req| {
842 ws.runner_request = null;849 ws.runner_request = null;
843 ws.runner_request_empty_cond.signal();850 ws.runner_request_empty_cond.signal(io);
844 return req;851 return req;
845 }852 }
846 ws.runner_request_ready_cond.wait(&ws.runner_request_mutex);853 try ws.runner_request_ready_cond.wait(io, &ws.runner_request_mutex);
847 }854 }
848}855}
849856
lib/std/Io/Threaded.zig+11-3
...@@ -33,6 +33,9 @@ wait_group: std.Thread.WaitGroup = .{},...@@ -33,6 +33,9 @@ wait_group: std.Thread.WaitGroup = .{},
33/// immediately.33/// immediately.
34///34///
35/// Defaults to a number equal to logical CPU cores.35/// Defaults to a number equal to logical CPU cores.
36///
37/// Protected by `mutex` once the I/O instance is already in use. See
38/// `setAsyncLimit`.
36async_limit: Io.Limit,39async_limit: Io.Limit,
37/// Maximum thread pool size (excluding main thread) for dispatching concurrent40/// Maximum thread pool size (excluding main thread) for dispatching concurrent
38/// tasks. Until this limit, calls to `Io.concurrent` will increase the thread41/// tasks. Until this limit, calls to `Io.concurrent` will increase the thread
...@@ -168,6 +171,12 @@ pub const init_single_threaded: Threaded = .{...@@ -168,6 +171,12 @@ pub const init_single_threaded: Threaded = .{
168 .have_signal_handler = false,171 .have_signal_handler = false,
169};172};
170173
174pub fn setAsyncLimit(t: *Threaded, new_limit: Io.Limit) void {
175 t.mutex.lock();
176 defer t.mutex.unlock();
177 t.async_limit = new_limit;
178}
179
171pub fn deinit(t: *Threaded) void {180pub fn deinit(t: *Threaded) void {
172 t.join();181 t.join();
173 if (is_windows and t.wsa.status == .initialized) {182 if (is_windows and t.wsa.status == .initialized) {
...@@ -507,7 +516,7 @@ fn async(...@@ -507,7 +516,7 @@ fn async(
507 start: *const fn (context: *const anyopaque, result: *anyopaque) void,516 start: *const fn (context: *const anyopaque, result: *anyopaque) void,
508) ?*Io.AnyFuture {517) ?*Io.AnyFuture {
509 const t: *Threaded = @ptrCast(@alignCast(userdata));518 const t: *Threaded = @ptrCast(@alignCast(userdata));
510 if (builtin.single_threaded or t.async_limit == .nothing) {519 if (builtin.single_threaded) {
511 start(context.ptr, result.ptr);520 start(context.ptr, result.ptr);
512 return null;521 return null;
513 }522 }
...@@ -684,8 +693,7 @@ fn groupAsync(...@@ -684,8 +693,7 @@ fn groupAsync(
684 start: *const fn (*Io.Group, context: *const anyopaque) void,693 start: *const fn (*Io.Group, context: *const anyopaque) void,
685) void {694) void {
686 const t: *Threaded = @ptrCast(@alignCast(userdata));695 const t: *Threaded = @ptrCast(@alignCast(userdata));
687 if (builtin.single_threaded or t.async_limit == .nothing)696 if (builtin.single_threaded) return start(group, context.ptr);
688 return start(group, context.ptr);
689697
690 const gpa = t.allocator;698 const gpa = t.allocator;
691 const gc = GroupClosure.init(gpa, t, group, context, context_alignment, start) catch699 const gc = GroupClosure.init(gpa, t, group, context, context_alignment, start) catch
src/Compilation.zig+15-15
...@@ -2851,6 +2851,7 @@ fn cleanupAfterUpdate(comp: *Compilation, tmp_dir_rand_int: u64) void {...@@ -2851,6 +2851,7 @@ fn cleanupAfterUpdate(comp: *Compilation, tmp_dir_rand_int: u64) void {
28512851
2852pub const UpdateError = error{2852pub const UpdateError = error{
2853 OutOfMemory,2853 OutOfMemory,
2854 Canceled,
2854 Unexpected,2855 Unexpected,
2855 CurrentWorkingDirectoryUnlinked,2856 CurrentWorkingDirectoryUnlinked,
2856};2857};
...@@ -2930,6 +2931,7 @@ pub fn update(comp: *Compilation, main_progress_node: std.Progress.Node) UpdateE...@@ -2930,6 +2931,7 @@ pub fn update(comp: *Compilation, main_progress_node: std.Progress.Node) UpdateE
2930 },2931 },
2931 },2932 },
2932 error.OutOfMemory => return error.OutOfMemory,2933 error.OutOfMemory => return error.OutOfMemory,
2934 error.Canceled => return error.Canceled,
2933 error.InvalidFormat => return comp.setMiscFailure(2935 error.InvalidFormat => return comp.setMiscFailure(
2934 .check_whole_cache,2936 .check_whole_cache,
2935 "failed to check cache: invalid manifest file format",2937 "failed to check cache: invalid manifest file format",
...@@ -5010,7 +5012,7 @@ fn performAllTheWork(...@@ -5010,7 +5012,7 @@ fn performAllTheWork(
5010 }5012 }
5011}5013}
50125014
5013const JobError = Allocator.Error;5015const JobError = Allocator.Error || Io.Cancelable;
50145016
5015pub fn queueJob(comp: *Compilation, job: Job) !void {5017pub fn queueJob(comp: *Compilation, job: Job) !void {
5016 try comp.work_queues[Job.stage(job)].pushBack(comp.gpa, job);5018 try comp.work_queues[Job.stage(job)].pushBack(comp.gpa, job);
...@@ -5117,6 +5119,7 @@ fn processOneJob(tid: usize, comp: *Compilation, job: Job) JobError!void {...@@ -5117,6 +5119,7 @@ fn processOneJob(tid: usize, comp: *Compilation, job: Job) JobError!void {
51175119
5118 pt.ensureFuncBodyUpToDate(func) catch |err| switch (err) {5120 pt.ensureFuncBodyUpToDate(func) catch |err| switch (err) {
5119 error.OutOfMemory => |e| return e,5121 error.OutOfMemory => |e| return e,
5122 error.Canceled => |e| return e,
5120 error.AnalysisFail => return,5123 error.AnalysisFail => return,
5121 };5124 };
5122 },5125 },
...@@ -5137,6 +5140,7 @@ fn processOneJob(tid: usize, comp: *Compilation, job: Job) JobError!void {...@@ -5137,6 +5140,7 @@ fn processOneJob(tid: usize, comp: *Compilation, job: Job) JobError!void {
5137 };5140 };
5138 maybe_err catch |err| switch (err) {5141 maybe_err catch |err| switch (err) {
5139 error.OutOfMemory => |e| return e,5142 error.OutOfMemory => |e| return e,
5143 error.Canceled => |e| return e,
5140 error.AnalysisFail => return,5144 error.AnalysisFail => return,
5141 };5145 };
51425146
...@@ -5166,7 +5170,7 @@ fn processOneJob(tid: usize, comp: *Compilation, job: Job) JobError!void {...@@ -5166,7 +5170,7 @@ fn processOneJob(tid: usize, comp: *Compilation, job: Job) JobError!void {
5166 const pt: Zcu.PerThread = .activate(comp.zcu.?, @enumFromInt(tid));5170 const pt: Zcu.PerThread = .activate(comp.zcu.?, @enumFromInt(tid));
5167 defer pt.deactivate();5171 defer pt.deactivate();
5168 Type.fromInterned(ty).resolveFully(pt) catch |err| switch (err) {5172 Type.fromInterned(ty).resolveFully(pt) catch |err| switch (err) {
5169 error.OutOfMemory => return error.OutOfMemory,5173 error.OutOfMemory, error.Canceled => |e| return e,
5170 error.AnalysisFail => return,5174 error.AnalysisFail => return,
5171 };5175 };
5172 },5176 },
...@@ -5177,7 +5181,7 @@ fn processOneJob(tid: usize, comp: *Compilation, job: Job) JobError!void {...@@ -5177,7 +5181,7 @@ fn processOneJob(tid: usize, comp: *Compilation, job: Job) JobError!void {
5177 const pt: Zcu.PerThread = .activate(comp.zcu.?, @enumFromInt(tid));5181 const pt: Zcu.PerThread = .activate(comp.zcu.?, @enumFromInt(tid));
5178 defer pt.deactivate();5182 defer pt.deactivate();
5179 pt.semaMod(mod) catch |err| switch (err) {5183 pt.semaMod(mod) catch |err| switch (err) {
5180 error.OutOfMemory => return error.OutOfMemory,5184 error.OutOfMemory, error.Canceled => |e| return e,
5181 error.AnalysisFail => return,5185 error.AnalysisFail => return,
5182 };5186 };
5183 },5187 },
...@@ -5190,8 +5194,8 @@ fn processOneJob(tid: usize, comp: *Compilation, job: Job) JobError!void {...@@ -5190,8 +5194,8 @@ fn processOneJob(tid: usize, comp: *Compilation, job: Job) JobError!void {
5190 // TODO Surface more error details.5194 // TODO Surface more error details.
5191 comp.lockAndSetMiscFailure(5195 comp.lockAndSetMiscFailure(
5192 .windows_import_lib,5196 .windows_import_lib,
5193 "unable to generate DLL import .lib file for {s}: {s}",5197 "unable to generate DLL import .lib file for {s}: {t}",
5194 .{ link_lib, @errorName(err) },5198 .{ link_lib, err },
5195 );5199 );
5196 };5200 };
5197 },5201 },
...@@ -6066,14 +6070,10 @@ fn buildLibZigC(comp: *Compilation, prog_node: std.Progress.Node) void {...@@ -6066,14 +6070,10 @@ fn buildLibZigC(comp: *Compilation, prog_node: std.Progress.Node) void {
6066 };6070 };
6067}6071}
60686072
6069fn reportRetryableCObjectError(6073fn reportRetryableCObjectError(comp: *Compilation, c_object: *CObject, err: anyerror) error{OutOfMemory}!void {
6070 comp: *Compilation,
6071 c_object: *CObject,
6072 err: anyerror,
6073) error{OutOfMemory}!void {
6074 c_object.status = .failure_retryable;6074 c_object.status = .failure_retryable;
60756075
6076 switch (comp.failCObj(c_object, "{s}", .{@errorName(err)})) {6076 switch (comp.failCObj(c_object, "{t}", .{err})) {
6077 error.AnalysisFail => return,6077 error.AnalysisFail => return,
6078 else => |e| return e,6078 else => |e| return e,
6079 }6079 }
...@@ -7317,7 +7317,7 @@ fn failCObj(...@@ -7317,7 +7317,7 @@ fn failCObj(
7317 c_object: *CObject,7317 c_object: *CObject,
7318 comptime format: []const u8,7318 comptime format: []const u8,
7319 args: anytype,7319 args: anytype,
7320) SemaError {7320) error{ OutOfMemory, AnalysisFail } {
7321 @branchHint(.cold);7321 @branchHint(.cold);
7322 const diag_bundle = blk: {7322 const diag_bundle = blk: {
7323 const diag_bundle = try comp.gpa.create(CObject.Diag.Bundle);7323 const diag_bundle = try comp.gpa.create(CObject.Diag.Bundle);
...@@ -7341,7 +7341,7 @@ fn failCObjWithOwnedDiagBundle(...@@ -7341,7 +7341,7 @@ fn failCObjWithOwnedDiagBundle(
7341 comp: *Compilation,7341 comp: *Compilation,
7342 c_object: *CObject,7342 c_object: *CObject,
7343 diag_bundle: *CObject.Diag.Bundle,7343 diag_bundle: *CObject.Diag.Bundle,
7344) SemaError {7344) error{ OutOfMemory, AnalysisFail } {
7345 @branchHint(.cold);7345 @branchHint(.cold);
7346 assert(diag_bundle.diags.len > 0);7346 assert(diag_bundle.diags.len > 0);
7347 {7347 {
...@@ -7357,7 +7357,7 @@ fn failCObjWithOwnedDiagBundle(...@@ -7357,7 +7357,7 @@ fn failCObjWithOwnedDiagBundle(
7357 return error.AnalysisFail;7357 return error.AnalysisFail;
7358}7358}
73597359
7360fn failWin32Resource(comp: *Compilation, win32_resource: *Win32Resource, comptime format: []const u8, args: anytype) SemaError {7360fn failWin32Resource(comp: *Compilation, win32_resource: *Win32Resource, comptime format: []const u8, args: anytype) error{ OutOfMemory, AnalysisFail } {
7361 @branchHint(.cold);7361 @branchHint(.cold);
7362 var bundle: ErrorBundle.Wip = undefined;7362 var bundle: ErrorBundle.Wip = undefined;
7363 try bundle.init(comp.gpa);7363 try bundle.init(comp.gpa);
...@@ -7384,7 +7384,7 @@ fn failWin32ResourceWithOwnedBundle(...@@ -7384,7 +7384,7 @@ fn failWin32ResourceWithOwnedBundle(
7384 comp: *Compilation,7384 comp: *Compilation,
7385 win32_resource: *Win32Resource,7385 win32_resource: *Win32Resource,
7386 err_bundle: ErrorBundle,7386 err_bundle: ErrorBundle,
7387) SemaError {7387) error{ OutOfMemory, AnalysisFail } {
7388 @branchHint(.cold);7388 @branchHint(.cold);
7389 {7389 {
7390 comp.mutex.lock();7390 comp.mutex.lock();
src/Sema.zig+8-6
...@@ -6696,7 +6696,7 @@ pub fn analyzeSaveErrRetIndex(sema: *Sema, block: *Block) SemaError!Air.Inst.Ref...@@ -6696,7 +6696,7 @@ pub fn analyzeSaveErrRetIndex(sema: *Sema, block: *Block) SemaError!Air.Inst.Ref
6696 const field_index = sema.structFieldIndex(block, stack_trace_ty, field_name, LazySrcLoc.unneeded) catch |err| switch (err) {6696 const field_index = sema.structFieldIndex(block, stack_trace_ty, field_name, LazySrcLoc.unneeded) catch |err| switch (err) {
6697 error.AnalysisFail => @panic("std.builtin.StackTrace is corrupt"),6697 error.AnalysisFail => @panic("std.builtin.StackTrace is corrupt"),
6698 error.ComptimeReturn, error.ComptimeBreak => unreachable,6698 error.ComptimeReturn, error.ComptimeBreak => unreachable,
6699 error.OutOfMemory => |e| return e,6699 error.OutOfMemory, error.Canceled => |e| return e,
6700 };6700 };
67016701
6702 return try block.addInst(.{6702 return try block.addInst(.{
...@@ -13924,6 +13924,7 @@ fn zirEmbedFile(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!A...@@ -13924,6 +13924,7 @@ fn zirEmbedFile(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!A
13924 return sema.fail(block, operand_src, "unable to resolve '{s}': working directory has been unlinked", .{name});13924 return sema.fail(block, operand_src, "unable to resolve '{s}': working directory has been unlinked", .{name});
13925 },13925 },
13926 error.OutOfMemory => |e| return e,13926 error.OutOfMemory => |e| return e,
13927 error.Canceled => |e| return e,
13927 };13928 };
13928 try sema.declareDependency(.{ .embed_file = ef_idx });13929 try sema.declareDependency(.{ .embed_file = ef_idx });
1392913930
...@@ -34345,7 +34346,7 @@ pub fn resolveStructLayout(sema: *Sema, ty: Type) SemaError!void {...@@ -34345,7 +34346,7 @@ pub fn resolveStructLayout(sema: *Sema, ty: Type) SemaError!void {
3434534346
34346 if (struct_type.layout == .@"packed") {34347 if (struct_type.layout == .@"packed") {
34347 sema.backingIntType(struct_type) catch |err| switch (err) {34348 sema.backingIntType(struct_type) catch |err| switch (err) {
34348 error.OutOfMemory, error.AnalysisFail => |e| return e,34349 error.AnalysisFail, error.OutOfMemory, error.Canceled => |e| return e,
34349 error.ComptimeBreak, error.ComptimeReturn => unreachable,34350 error.ComptimeBreak, error.ComptimeReturn => unreachable,
34350 };34351 };
34351 return;34352 return;
...@@ -34893,7 +34894,7 @@ pub fn resolveStructFieldTypes(...@@ -34893,7 +34894,7 @@ pub fn resolveStructFieldTypes(
34893 defer tracked_unit.end(zcu);34894 defer tracked_unit.end(zcu);
3489434895
34895 sema.structFields(struct_type) catch |err| switch (err) {34896 sema.structFields(struct_type) catch |err| switch (err) {
34896 error.AnalysisFail, error.OutOfMemory => |e| return e,34897 error.AnalysisFail, error.OutOfMemory, error.Canceled => |e| return e,
34897 error.ComptimeBreak, error.ComptimeReturn => unreachable,34898 error.ComptimeBreak, error.ComptimeReturn => unreachable,
34898 };34899 };
34899}34900}
...@@ -34926,7 +34927,7 @@ pub fn resolveStructFieldInits(sema: *Sema, ty: Type) SemaError!void {...@@ -34926,7 +34927,7 @@ pub fn resolveStructFieldInits(sema: *Sema, ty: Type) SemaError!void {
34926 defer tracked_unit.end(zcu);34927 defer tracked_unit.end(zcu);
3492734928
34928 sema.structFieldInits(struct_type) catch |err| switch (err) {34929 sema.structFieldInits(struct_type) catch |err| switch (err) {
34929 error.AnalysisFail, error.OutOfMemory => |e| return e,34930 error.AnalysisFail, error.OutOfMemory, error.Canceled => |e| return e,
34930 error.ComptimeBreak, error.ComptimeReturn => unreachable,34931 error.ComptimeBreak, error.ComptimeReturn => unreachable,
34931 };34932 };
34932 struct_type.setHaveFieldInits(ip);34933 struct_type.setHaveFieldInits(ip);
...@@ -34960,7 +34961,7 @@ pub fn resolveUnionFieldTypes(sema: *Sema, ty: Type, union_type: InternPool.Load...@@ -34960,7 +34961,7 @@ pub fn resolveUnionFieldTypes(sema: *Sema, ty: Type, union_type: InternPool.Load
34960 union_type.setStatus(ip, .field_types_wip);34961 union_type.setStatus(ip, .field_types_wip);
34961 errdefer union_type.setStatus(ip, .none);34962 errdefer union_type.setStatus(ip, .none);
34962 sema.unionFields(ty.toIntern(), union_type) catch |err| switch (err) {34963 sema.unionFields(ty.toIntern(), union_type) catch |err| switch (err) {
34963 error.AnalysisFail, error.OutOfMemory => |e| return e,34964 error.AnalysisFail, error.OutOfMemory, error.Canceled => |e| return e,
34964 error.ComptimeBreak, error.ComptimeReturn => unreachable,34965 error.ComptimeBreak, error.ComptimeReturn => unreachable,
34965 };34966 };
34966 union_type.setStatus(ip, .have_field_types);34967 union_type.setStatus(ip, .have_field_types);
...@@ -37027,6 +37028,7 @@ fn notePathToComptimeAllocPtr(...@@ -37027,6 +37028,7 @@ fn notePathToComptimeAllocPtr(
3702737028
37028 const derivation = comptime_ptr.pointerDerivationAdvanced(arena, pt, false, sema) catch |err| switch (err) {37029 const derivation = comptime_ptr.pointerDerivationAdvanced(arena, pt, false, sema) catch |err| switch (err) {
37029 error.OutOfMemory => |e| return e,37030 error.OutOfMemory => |e| return e,
37031 error.Canceled => @panic("TODO"), // pls don't be cancelable mlugg
37030 error.AnalysisFail => unreachable,37032 error.AnalysisFail => unreachable,
37031 };37033 };
3703237034
...@@ -37367,7 +37369,7 @@ pub fn resolveDeclaredEnum(...@@ -37367,7 +37369,7 @@ pub fn resolveDeclaredEnum(
37367 ) catch |err| switch (err) {37369 ) catch |err| switch (err) {
37368 error.ComptimeBreak => unreachable,37370 error.ComptimeBreak => unreachable,
37369 error.ComptimeReturn => unreachable,37371 error.ComptimeReturn => unreachable,
37370 error.OutOfMemory => |e| return e,37372 error.OutOfMemory, error.Canceled => |e| return e,
37371 error.AnalysisFail => {37373 error.AnalysisFail => {
37372 if (!zcu.failed_analysis.contains(sema.owner)) {37374 if (!zcu.failed_analysis.contains(sema.owner)) {
37373 try zcu.transitive_failed_analysis.put(gpa, sema.owner, {});37375 try zcu.transitive_failed_analysis.put(gpa, sema.owner, {});
src/Type.zig+2-1
...@@ -3837,7 +3837,7 @@ fn resolveStructInner(...@@ -3837,7 +3837,7 @@ fn resolveStructInner(
3837 }3837 }
3838 return error.AnalysisFail;3838 return error.AnalysisFail;
3839 },3839 },
3840 error.OutOfMemory => |e| return e,3840 error.OutOfMemory, error.Canceled => |e| return e,
3841 };3841 };
3842}3842}
38433843
...@@ -3896,6 +3896,7 @@ fn resolveUnionInner(...@@ -3896,6 +3896,7 @@ fn resolveUnionInner(
3896 return error.AnalysisFail;3896 return error.AnalysisFail;
3897 },3897 },
3898 error.OutOfMemory => |e| return e,3898 error.OutOfMemory => |e| return e,
3899 error.Canceled => |e| return e,
3899 };3900 };
3900}3901}
39013902
src/Value.zig+7-3
...@@ -1,12 +1,15 @@...@@ -1,12 +1,15 @@
1const std = @import("std");
2const builtin = @import("builtin");
3const build_options = @import("build_options");1const build_options = @import("build_options");
4const Type = @import("Type.zig");2const builtin = @import("builtin");
3
4const std = @import("std");
5const Io = std.Io;
5const assert = std.debug.assert;6const assert = std.debug.assert;
6const BigIntConst = std.math.big.int.Const;7const BigIntConst = std.math.big.int.Const;
7const BigIntMutable = std.math.big.int.Mutable;8const BigIntMutable = std.math.big.int.Mutable;
8const Target = std.Target;9const Target = std.Target;
9const Allocator = std.mem.Allocator;10const Allocator = std.mem.Allocator;
11
12const Type = @import("Type.zig");
10const Zcu = @import("Zcu.zig");13const Zcu = @import("Zcu.zig");
11const Sema = @import("Sema.zig");14const Sema = @import("Sema.zig");
12const InternPool = @import("InternPool.zig");15const InternPool = @import("InternPool.zig");
...@@ -2410,6 +2413,7 @@ pub const PointerDeriveStep = union(enum) {...@@ -2410,6 +2413,7 @@ pub const PointerDeriveStep = union(enum) {
2410pub fn pointerDerivation(ptr_val: Value, arena: Allocator, pt: Zcu.PerThread) Allocator.Error!PointerDeriveStep {2413pub fn pointerDerivation(ptr_val: Value, arena: Allocator, pt: Zcu.PerThread) Allocator.Error!PointerDeriveStep {
2411 return ptr_val.pointerDerivationAdvanced(arena, pt, false, null) catch |err| switch (err) {2414 return ptr_val.pointerDerivationAdvanced(arena, pt, false, null) catch |err| switch (err) {
2412 error.OutOfMemory => |e| return e,2415 error.OutOfMemory => |e| return e,
2416 error.Canceled => @panic("TODO"), // pls remove from error set mlugg
2413 error.AnalysisFail => unreachable,2417 error.AnalysisFail => unreachable,
2414 };2418 };
2415}2419}
src/Zcu.zig+3-1
...@@ -2755,9 +2755,11 @@ pub const LazySrcLoc = struct {...@@ -2755,9 +2755,11 @@ pub const LazySrcLoc = struct {
2755 }2755 }
2756};2756};
27572757
2758pub const SemaError = error{ OutOfMemory, AnalysisFail };2758pub const SemaError = error{ OutOfMemory, Canceled, AnalysisFail };
2759pub const CompileError = error{2759pub const CompileError = error{
2760 OutOfMemory,2760 OutOfMemory,
2761 /// The compilation update is no longer desired.
2762 Canceled,
2761 /// When this is returned, the compile error for the failure has already been recorded.2763 /// When this is returned, the compile error for the failure has already been recorded.
2762 AnalysisFail,2764 AnalysisFail,
2763 /// In a comptime scope, a return instruction was encountered. This error is only seen when2765 /// In a comptime scope, a return instruction was encountered. This error is only seen when
src/Zcu/PerThread.zig+18-9
...@@ -1,26 +1,31 @@...@@ -1,26 +1,31 @@
1//! This type provides a wrapper around a `*Zcu` for uses which require a thread `Id`.1//! This type provides a wrapper around a `*Zcu` for uses which require a thread `Id`.
2//! Any operation which mutates `InternPool` state lives here rather than on `Zcu`.2//! Any operation which mutates `InternPool` state lives here rather than on `Zcu`.
33
4const Air = @import("../Air.zig");4const std = @import("std");
5const Allocator = std.mem.Allocator;5const Allocator = std.mem.Allocator;
6const assert = std.debug.assert;6const assert = std.debug.assert;
7const Ast = std.zig.Ast;7const Ast = std.zig.Ast;
8const AstGen = std.zig.AstGen;8const AstGen = std.zig.AstGen;
9const BigIntConst = std.math.big.int.Const;9const BigIntConst = std.math.big.int.Const;
10const BigIntMutable = std.math.big.int.Mutable;10const BigIntMutable = std.math.big.int.Mutable;
11const Cache = std.Build.Cache;
12const log = std.log.scoped(.zcu);
13const mem = std.mem;
14const Zir = std.zig.Zir;
15const Zoir = std.zig.Zoir;
16const ZonGen = std.zig.ZonGen;
17const Io = std.Io;
18
19const Air = @import("../Air.zig");
11const Builtin = @import("../Builtin.zig");20const Builtin = @import("../Builtin.zig");
12const build_options = @import("build_options");21const build_options = @import("build_options");
13const builtin = @import("builtin");22const builtin = @import("builtin");
14const Cache = std.Build.Cache;
15const dev = @import("../dev.zig");23const dev = @import("../dev.zig");
16const InternPool = @import("../InternPool.zig");24const InternPool = @import("../InternPool.zig");
17const AnalUnit = InternPool.AnalUnit;25const AnalUnit = InternPool.AnalUnit;
18const introspect = @import("../introspect.zig");26const introspect = @import("../introspect.zig");
19const log = std.log.scoped(.zcu);
20const Module = @import("../Package.zig").Module;27const Module = @import("../Package.zig").Module;
21const Sema = @import("../Sema.zig");28const Sema = @import("../Sema.zig");
22const std = @import("std");
23const mem = std.mem;
24const target_util = @import("../target.zig");29const target_util = @import("../target.zig");
25const trace = @import("../tracy.zig").trace;30const trace = @import("../tracy.zig").trace;
26const Type = @import("../Type.zig");31const Type = @import("../Type.zig");
...@@ -29,9 +34,6 @@ const Zcu = @import("../Zcu.zig");...@@ -29,9 +34,6 @@ const Zcu = @import("../Zcu.zig");
29const Compilation = @import("../Compilation.zig");34const Compilation = @import("../Compilation.zig");
30const codegen = @import("../codegen.zig");35const codegen = @import("../codegen.zig");
31const crash_report = @import("../crash_report.zig");36const crash_report = @import("../crash_report.zig");
32const Zir = std.zig.Zir;
33const Zoir = std.zig.Zoir;
34const ZonGen = std.zig.ZonGen;
3537
36zcu: *Zcu,38zcu: *Zcu,
3739
...@@ -678,6 +680,7 @@ pub fn ensureMemoizedStateUpToDate(pt: Zcu.PerThread, stage: InternPool.Memoized...@@ -678,6 +680,7 @@ pub fn ensureMemoizedStateUpToDate(pt: Zcu.PerThread, stage: InternPool.Memoized
678 // TODO: same as for `ensureComptimeUnitUpToDate` etc680 // TODO: same as for `ensureComptimeUnitUpToDate` etc
679 return error.OutOfMemory;681 return error.OutOfMemory;
680 },682 },
683 error.Canceled => |e| return e,
681 error.ComptimeReturn => unreachable,684 error.ComptimeReturn => unreachable,
682 error.ComptimeBreak => unreachable,685 error.ComptimeBreak => unreachable,
683 };686 };
...@@ -842,6 +845,7 @@ pub fn ensureComptimeUnitUpToDate(pt: Zcu.PerThread, cu_id: InternPool.ComptimeU...@@ -842,6 +845,7 @@ pub fn ensureComptimeUnitUpToDate(pt: Zcu.PerThread, cu_id: InternPool.ComptimeU
842 // for reporting OOM errors without allocating.845 // for reporting OOM errors without allocating.
843 return error.OutOfMemory;846 return error.OutOfMemory;
844 },847 },
848 error.Canceled => |e| return e,
845 error.ComptimeReturn => unreachable,849 error.ComptimeReturn => unreachable,
846 error.ComptimeBreak => unreachable,850 error.ComptimeBreak => unreachable,
847 };851 };
...@@ -1030,6 +1034,7 @@ pub fn ensureNavValUpToDate(pt: Zcu.PerThread, nav_id: InternPool.Nav.Index) Zcu...@@ -1030,6 +1034,7 @@ pub fn ensureNavValUpToDate(pt: Zcu.PerThread, nav_id: InternPool.Nav.Index) Zcu
1030 // for reporting OOM errors without allocating.1034 // for reporting OOM errors without allocating.
1031 return error.OutOfMemory;1035 return error.OutOfMemory;
1032 },1036 },
1037 error.Canceled => |e| return e,
1033 error.ComptimeReturn => unreachable,1038 error.ComptimeReturn => unreachable,
1034 error.ComptimeBreak => unreachable,1039 error.ComptimeBreak => unreachable,
1035 };1040 };
...@@ -1443,6 +1448,7 @@ pub fn ensureNavTypeUpToDate(pt: Zcu.PerThread, nav_id: InternPool.Nav.Index) Zc...@@ -1443,6 +1448,7 @@ pub fn ensureNavTypeUpToDate(pt: Zcu.PerThread, nav_id: InternPool.Nav.Index) Zc
1443 // for reporting OOM errors without allocating.1448 // for reporting OOM errors without allocating.
1444 return error.OutOfMemory;1449 return error.OutOfMemory;
1445 },1450 },
1451 error.Canceled => |e| return e,
1446 error.ComptimeReturn => unreachable,1452 error.ComptimeReturn => unreachable,
1447 error.ComptimeBreak => unreachable,1453 error.ComptimeBreak => unreachable,
1448 };1454 };
...@@ -1668,6 +1674,7 @@ pub fn ensureFuncBodyUpToDate(pt: Zcu.PerThread, func_index: InternPool.Index) Z...@@ -1668,6 +1674,7 @@ pub fn ensureFuncBodyUpToDate(pt: Zcu.PerThread, func_index: InternPool.Index) Z
1668 // for reporting OOM errors without allocating.1674 // for reporting OOM errors without allocating.
1669 return error.OutOfMemory;1675 return error.OutOfMemory;
1670 },1676 },
1677 error.Canceled => |e| return e,
1671 };1678 };
16721679
1673 if (was_outdated) {1680 if (was_outdated) {
...@@ -2360,6 +2367,7 @@ pub fn embedFile(...@@ -2360,6 +2367,7 @@ pub fn embedFile(
2360 import_string: []const u8,2367 import_string: []const u8,
2361) error{2368) error{
2362 OutOfMemory,2369 OutOfMemory,
2370 Canceled,
2363 ImportOutsideModulePath,2371 ImportOutsideModulePath,
2364 CurrentWorkingDirectoryUnlinked,2372 CurrentWorkingDirectoryUnlinked,
2365}!Zcu.EmbedFile.Index {2373}!Zcu.EmbedFile.Index {
...@@ -4123,7 +4131,7 @@ fn recreateEnumType(...@@ -4123,7 +4131,7 @@ fn recreateEnumType(
4123 pt: Zcu.PerThread,4131 pt: Zcu.PerThread,
4124 old_ty: InternPool.Index,4132 old_ty: InternPool.Index,
4125 key: InternPool.Key.NamespaceType.Declared,4133 key: InternPool.Key.NamespaceType.Declared,
4126) Allocator.Error!InternPool.Index {4134) (Allocator.Error || Io.Cancelable)!InternPool.Index {
4127 const zcu = pt.zcu;4135 const zcu = pt.zcu;
4128 const gpa = zcu.gpa;4136 const gpa = zcu.gpa;
4129 const ip = &zcu.intern_pool;4137 const ip = &zcu.intern_pool;
...@@ -4234,6 +4242,7 @@ fn recreateEnumType(...@@ -4234,6 +4242,7 @@ fn recreateEnumType(
4234 body_end,4242 body_end,
4235 ) catch |err| switch (err) {4243 ) catch |err| switch (err) {
4236 error.OutOfMemory => |e| return e,4244 error.OutOfMemory => |e| return e,
4245 error.Canceled => |e| return e,
4237 error.AnalysisFail => {}, // call sites are responsible for checking `[transitive_]failed_analysis` to detect this4246 error.AnalysisFail => {}, // call sites are responsible for checking `[transitive_]failed_analysis` to detect this
4238 };4247 };
42394248
src/print_value.zig+2
...@@ -27,6 +27,7 @@ pub fn formatSema(ctx: FormatContext, writer: *Writer) Writer.Error!void {...@@ -27,6 +27,7 @@ pub fn formatSema(ctx: FormatContext, writer: *Writer) Writer.Error!void {
27 error.OutOfMemory => @panic("OOM"), // We're not allowed to return this from a format function27 error.OutOfMemory => @panic("OOM"), // We're not allowed to return this from a format function
28 error.ComptimeBreak, error.ComptimeReturn => unreachable,28 error.ComptimeBreak, error.ComptimeReturn => unreachable,
29 error.AnalysisFail => unreachable, // TODO: re-evaluate when we use `sema` more fully29 error.AnalysisFail => unreachable, // TODO: re-evaluate when we use `sema` more fully
30 error.Canceled => @panic("TODO"), // pls stop returning this error mlugg
30 else => |e| return e,31 else => |e| return e,
31 };32 };
32}33}
...@@ -36,6 +37,7 @@ pub fn format(ctx: FormatContext, writer: *Writer) Writer.Error!void {...@@ -36,6 +37,7 @@ pub fn format(ctx: FormatContext, writer: *Writer) Writer.Error!void {
36 return print(ctx.val, writer, ctx.depth, ctx.pt, null) catch |err| switch (err) {37 return print(ctx.val, writer, ctx.depth, ctx.pt, null) catch |err| switch (err) {
37 error.OutOfMemory => @panic("OOM"), // We're not allowed to return this from a format function38 error.OutOfMemory => @panic("OOM"), // We're not allowed to return this from a format function
38 error.ComptimeBreak, error.ComptimeReturn, error.AnalysisFail => unreachable,39 error.ComptimeBreak, error.ComptimeReturn, error.AnalysisFail => unreachable,
40 error.Canceled => @panic("TODO"), // pls stop returning this error mlugg
39 else => |e| return e,41 else => |e| return e,
40 };42 };
41}43}