| ... | ... | @@ -503,7 +503,7 @@ pub fn main(init: process.Init.Minimal) !void { |
| 503 | 503 | var run: Run = .{ |
| 504 | 504 | .gpa = gpa, |
| 505 | 505 | |
| 506 | | .max_rss = max_rss, |
| 506 | .available_rss = max_rss, |
| 507 | 507 | .max_rss_is_default = false, |
| 508 | 508 | .max_rss_mutex = .init, |
| 509 | 509 | .skip_oom_steps = skip_oom_steps, |
| ... | ... | @@ -514,7 +514,6 @@ pub fn main(init: process.Init.Minimal) !void { |
| 514 | 514 | .memory_blocked_steps = .empty, |
| 515 | 515 | .step_stack = .empty, |
| 516 | 516 | |
| 517 | | .claimed_rss = 0, |
| 518 | 517 | .error_style = error_style, |
| 519 | 518 | .multiline_errors = multiline_errors, |
| 520 | 519 | .summary = summary orelse if (watch or webui_listen != null) .line else .failures, |
| ... | ... | @@ -524,8 +523,8 @@ pub fn main(init: process.Init.Minimal) !void { |
| 524 | 523 | run.step_stack.deinit(gpa); |
| 525 | 524 | } |
| 526 | 525 | |
| 527 | | if (run.max_rss == 0) { |
| 528 | | run.max_rss = process.totalSystemMemory() catch std.math.maxInt(u64); |
| 526 | if (run.available_rss == 0) { |
| 527 | run.available_rss = process.totalSystemMemory() catch std.math.maxInt(u64); |
| 529 | 528 | run.max_rss_is_default = true; |
| 530 | 529 | } |
| 531 | 530 | |
| ... | ... | @@ -595,6 +594,7 @@ pub fn main(init: process.Init.Minimal) !void { |
| 595 | 594 | .rebuild => { |
| 596 | 595 | for (run.step_stack.keys()) |step| { |
| 597 | 596 | step.state = .precheck_done; |
| 597 | step.pending_deps = @intCast(step.dependencies.items.len); |
| 598 | 598 | step.reset(gpa); |
| 599 | 599 | } |
| 600 | 600 | continue :rebuild; |
| ... | ... | @@ -637,7 +637,7 @@ pub fn main(init: process.Init.Minimal) !void { |
| 637 | 637 | |
| 638 | 638 | fn markFailedStepsDirty(gpa: Allocator, all_steps: []const *Step) void { |
| 639 | 639 | for (all_steps) |step| switch (step.state) { |
| 640 | | .dependency_failure, .failure, .skipped => step.recursiveReset(gpa), |
| 640 | .dependency_failure, .failure, .skipped => _ = step.invalidateResult(gpa), |
| 641 | 641 | else => continue, |
| 642 | 642 | }; |
| 643 | 643 | // Now that all dirty steps have been found, the remaining steps that |
| ... | ... | @@ -658,7 +658,8 @@ fn countSubProcesses(all_steps: []const *Step) usize { |
| 658 | 658 | |
| 659 | 659 | const Run = struct { |
| 660 | 660 | gpa: Allocator, |
| 661 | | max_rss: u64, |
| 661 | |
| 662 | available_rss: usize, |
| 662 | 663 | max_rss_is_default: bool, |
| 663 | 664 | max_rss_mutex: Io.Mutex, |
| 664 | 665 | skip_oom_steps: bool, |
| ... | ... | @@ -670,7 +671,6 @@ const Run = struct { |
| 670 | 671 | /// Allocated into `gpa`. |
| 671 | 672 | step_stack: std.AutoArrayHashMapUnmanaged(*Step, void), |
| 672 | 673 | |
| 673 | | claimed_rss: usize, |
| 674 | 674 | error_style: ErrorStyle, |
| 675 | 675 | multiline_errors: MultilineErrors, |
| 676 | 676 | summary: Summary, |
| ... | ... | @@ -715,12 +715,15 @@ fn prepare( |
| 715 | 715 | var any_problems = false; |
| 716 | 716 | for (step_stack.keys()) |s| { |
| 717 | 717 | if (s.max_rss == 0) continue; |
| 718 | | if (s.max_rss > run.max_rss) { |
| 718 | if (s.max_rss > run.available_rss) { |
| 719 | 719 | if (run.skip_oom_steps) { |
| 720 | 720 | s.state = .skipped_oom; |
| 721 | for (s.dependants.items) |dependant| { |
| 722 | dependant.pending_deps -= 1; |
| 723 | } |
| 721 | 724 | } else { |
| 722 | 725 | std.debug.print("{s}{s}: this step declares an upper bound of {d} bytes of memory, exceeding the available {d} bytes of memory\n", .{ |
| 723 | | s.owner.dep_prefix, s.name, s.max_rss, run.max_rss, |
| 726 | s.owner.dep_prefix, s.name, s.max_rss, run.available_rss, |
| 724 | 727 | }); |
| 725 | 728 | any_problems = true; |
| 726 | 729 | } |
| ... | ... | @@ -747,23 +750,26 @@ fn runStepNames( |
| 747 | 750 | const step_stack = &run.step_stack; |
| 748 | 751 | |
| 749 | 752 | { |
| 753 | // Collect the initial set of tasks (those with no outstanding dependencies) into a buffer, |
| 754 | // then spawn them. The buffer is so that we don't race with `makeStep` and end up thinking |
| 755 | // a step is initial when it actually became ready due to an earlier initial step. |
| 756 | var initial_set: std.ArrayList(*Step) = .empty; |
| 757 | defer initial_set.deinit(gpa); |
| 758 | try initial_set.ensureUnusedCapacity(gpa, step_stack.count()); |
| 759 | for (step_stack.keys()) |s| { |
| 760 | if (s.state == .precheck_done and s.pending_deps == 0) { |
| 761 | initial_set.appendAssumeCapacity(s); |
| 762 | } |
| 763 | } |
| 764 | |
| 750 | 765 | const step_prog = parent_prog_node.start("steps", step_stack.count()); |
| 751 | 766 | defer step_prog.end(); |
| 752 | 767 | |
| 753 | 768 | var group: Io.Group = .init; |
| 754 | 769 | defer group.cancel(io); |
| 755 | | |
| 756 | | // Here we spawn the initial set of tasks with a nice heuristic - |
| 757 | | // dependency order. Each worker when it finishes a step will then |
| 758 | | // check whether it should run any dependants. |
| 759 | | const steps_slice = step_stack.keys(); |
| 760 | | for (0..steps_slice.len) |i| { |
| 761 | | const step = steps_slice[steps_slice.len - i - 1]; |
| 762 | | if (step.state == .skipped_oom) continue; |
| 763 | | |
| 764 | | group.async(io, workerMakeOneStep, .{ &group, b, step, step_prog, run }); |
| 765 | | } |
| 766 | | |
| 770 | // Start working on all of the initial steps... |
| 771 | for (initial_set.items) |s| try stepReady(&group, b, s, step_prog, run); |
| 772 | // ...and `makeStep` will trigger every other step when their last dependency finishes. |
| 767 | 773 | try group.await(io); |
| 768 | 774 | } |
| 769 | 775 | |
| ... | ... | @@ -798,17 +804,7 @@ fn runStepNames( |
| 798 | 804 | switch (s.state) { |
| 799 | 805 | .precheck_unstarted => unreachable, |
| 800 | 806 | .precheck_started => unreachable, |
| 801 | | .running => unreachable, |
| 802 | | .precheck_done => { |
| 803 | | // precheck_done is equivalent to dependency_failure in the case of |
| 804 | | // transitive dependencies. For example: |
| 805 | | // A -> B -> C (failure) |
| 806 | | // B will be marked as dependency_failure, while A may never be queued, and thus |
| 807 | | // remain in the initial state of precheck_done. |
| 808 | | s.state = .dependency_failure; |
| 809 | | if (run.web_server) |*ws| ws.updateStepStatus(s, .failure); |
| 810 | | pending_count += 1; |
| 811 | | }, |
| 807 | .precheck_done => unreachable, |
| 812 | 808 | .dependency_failure => pending_count += 1, |
| 813 | 809 | .success => success_count += 1, |
| 814 | 810 | .skipped, .skipped_oom => skipped_count += 1, |
| ... | ... | @@ -1008,7 +1004,6 @@ fn printStepStatus(s: *Step, stderr: Io.Terminal, run: *const Run) !void { |
| 1008 | 1004 | .precheck_unstarted => unreachable, |
| 1009 | 1005 | .precheck_started => unreachable, |
| 1010 | 1006 | .precheck_done => unreachable, |
| 1011 | | .running => unreachable, |
| 1012 | 1007 | |
| 1013 | 1008 | .dependency_failure => { |
| 1014 | 1009 | try stderr.setColor(.dim); |
| ... | ... | @@ -1067,16 +1062,16 @@ fn printStepStatus(s: *Step, stderr: Io.Terminal, run: *const Run) !void { |
| 1067 | 1062 | } |
| 1068 | 1063 | try writer.writeAll("\n"); |
| 1069 | 1064 | }, |
| 1070 | | .skipped, .skipped_oom => |skip| { |
| 1065 | .skipped => { |
| 1071 | 1066 | try stderr.setColor(.yellow); |
| 1072 | | try writer.writeAll(" skipped"); |
| 1073 | | if (skip == .skipped_oom) { |
| 1074 | | try writer.writeAll(" (not enough memory)"); |
| 1075 | | try stderr.setColor(.dim); |
| 1076 | | try writer.print(" upper bound of {d} exceeded runner limit ({d})", .{ s.max_rss, run.max_rss }); |
| 1077 | | try stderr.setColor(.yellow); |
| 1078 | | } |
| 1079 | | try writer.writeAll("\n"); |
| 1067 | try writer.writeAll(" skipped\n"); |
| 1068 | try stderr.setColor(.reset); |
| 1069 | }, |
| 1070 | .skipped_oom => { |
| 1071 | try stderr.setColor(.yellow); |
| 1072 | try writer.writeAll(" skipped (not enough memory)"); |
| 1073 | try stderr.setColor(.dim); |
| 1074 | try writer.print(" upper bound of {d} exceeded runner limit ({d})\n", .{ s.max_rss, run.available_rss }); |
| 1080 | 1075 | try stderr.setColor(.reset); |
| 1081 | 1076 | }, |
| 1082 | 1077 | .failure => { |
| ... | ... | @@ -1250,10 +1245,10 @@ fn printTreeStep( |
| 1250 | 1245 | /// Each step has its dependencies traversed in random order, this accomplishes |
| 1251 | 1246 | /// two things: |
| 1252 | 1247 | /// - `step_stack` will be in randomized-depth-first order, so the build runner |
| 1253 | | /// spawns steps in a random (but optimized) order |
| 1248 | /// spawns initial steps in a random order |
| 1254 | 1249 | /// - each step's `dependants` list is also filled in a random order, so that |
| 1255 | | /// when it finishes executing in `workerMakeOneStep`, it spawns next steps |
| 1256 | | /// to run in random order |
| 1250 | /// when it finishes executing in `makeStep`, it spawns next steps to run in |
| 1251 | /// random order |
| 1257 | 1252 | fn constructGraphAndCheckForDependencyLoop( |
| 1258 | 1253 | gpa: Allocator, |
| 1259 | 1254 | b: *std.Build, |
| ... | ... | @@ -1290,12 +1285,12 @@ fn constructGraphAndCheckForDependencyLoop( |
| 1290 | 1285 | } |
| 1291 | 1286 | |
| 1292 | 1287 | s.state = .precheck_done; |
| 1288 | s.pending_deps = @intCast(s.dependencies.items.len); |
| 1293 | 1289 | }, |
| 1294 | 1290 | .precheck_done => {}, |
| 1295 | 1291 | |
| 1296 | 1292 | // These don't happen until we actually run the step graph. |
| 1297 | 1293 | .dependency_failure => unreachable, |
| 1298 | | .running => unreachable, |
| 1299 | 1294 | .success => unreachable, |
| 1300 | 1295 | .failure => unreachable, |
| 1301 | 1296 | .skipped => unreachable, |
| ... | ... | @@ -1303,148 +1298,136 @@ fn constructGraphAndCheckForDependencyLoop( |
| 1303 | 1298 | } |
| 1304 | 1299 | } |
| 1305 | 1300 | |
| 1306 | | fn workerMakeOneStep( |
| 1301 | /// Runs the "make" function of the single step `s`, updates its state, and then spawns newly-ready |
| 1302 | /// dependant steps in `group`. If `s` makes an RSS claim (i.e. `s.max_rss != 0`), the caller must |
| 1303 | /// have already subtracted this value from `run.available_rss`. This function will release the RSS |
| 1304 | /// claim (i.e. add `s.max_rss` back into `run.available_rss`) and queue any viable memory-blocked |
| 1305 | /// steps after "make" completes for `s`. |
| 1306 | fn makeStep( |
| 1307 | 1307 | group: *Io.Group, |
| 1308 | 1308 | b: *std.Build, |
| 1309 | 1309 | s: *Step, |
| 1310 | | prog_node: std.Progress.Node, |
| 1310 | root_prog_node: std.Progress.Node, |
| 1311 | 1311 | run: *Run, |
| 1312 | | ) void { |
| 1312 | ) Io.Cancelable!void { |
| 1313 | 1313 | const graph = b.graph; |
| 1314 | 1314 | const io = graph.io; |
| 1315 | 1315 | const gpa = run.gpa; |
| 1316 | 1316 | |
| 1317 | | // First, check the conditions for running this step. If they are not met, |
| 1318 | | // then we return without doing the step, relying on another worker to |
| 1319 | | // queue this step up again when dependencies are met. |
| 1320 | | for (s.dependencies.items) |dep| { |
| 1321 | | switch (@atomicLoad(Step.State, &dep.state, .seq_cst)) { |
| 1322 | | .success, .skipped => continue, |
| 1323 | | .failure, .dependency_failure, .skipped_oom => { |
| 1324 | | @atomicStore(Step.State, &s.state, .dependency_failure, .seq_cst); |
| 1325 | | if (run.web_server) |*ws| ws.updateStepStatus(s, .failure); |
| 1326 | | return; |
| 1327 | | }, |
| 1328 | | .precheck_done, .running => { |
| 1329 | | // dependency is not finished yet. |
| 1330 | | return; |
| 1331 | | }, |
| 1332 | | .precheck_unstarted => unreachable, |
| 1333 | | .precheck_started => unreachable, |
| 1334 | | } |
| 1335 | | } |
| 1336 | | |
| 1337 | | if (s.max_rss != 0) { |
| 1338 | | run.max_rss_mutex.lockUncancelable(io); |
| 1339 | | defer run.max_rss_mutex.unlock(io); |
| 1340 | | |
| 1341 | | // Avoid running steps twice. |
| 1342 | | if (s.state != .precheck_done) { |
| 1343 | | // Another worker got the job. |
| 1344 | | return; |
| 1345 | | } |
| 1317 | { |
| 1318 | const step_prog_node = root_prog_node.start(s.name, 0); |
| 1319 | defer step_prog_node.end(); |
| 1346 | 1320 | |
| 1347 | | const new_claimed_rss = run.claimed_rss + s.max_rss; |
| 1348 | | if (new_claimed_rss > run.max_rss) { |
| 1349 | | // Running this step right now could possibly exceed the allotted RSS. |
| 1350 | | // Add this step to the queue of memory-blocked steps. |
| 1351 | | run.memory_blocked_steps.append(gpa, s) catch @panic("OOM"); |
| 1352 | | return; |
| 1353 | | } |
| 1321 | if (run.web_server) |*ws| ws.updateStepStatus(s, .wip); |
| 1354 | 1322 | |
| 1355 | | run.claimed_rss = new_claimed_rss; |
| 1356 | | s.state = .running; |
| 1357 | | } else { |
| 1358 | | // Avoid running steps twice. |
| 1359 | | if (@cmpxchgStrong(Step.State, &s.state, .precheck_done, .running, .seq_cst, .seq_cst) != null) { |
| 1360 | | // Another worker got the job. |
| 1361 | | return; |
| 1362 | | } |
| 1363 | | } |
| 1323 | const new_state: Step.State = for (s.dependencies.items) |dep| { |
| 1324 | switch (@atomicLoad(Step.State, &dep.state, .monotonic)) { |
| 1325 | .precheck_unstarted => unreachable, |
| 1326 | .precheck_started => unreachable, |
| 1327 | .precheck_done => unreachable, |
| 1364 | 1328 | |
| 1365 | | const sub_prog_node = prog_node.start(s.name, 0); |
| 1366 | | defer sub_prog_node.end(); |
| 1329 | .failure, |
| 1330 | .dependency_failure, |
| 1331 | .skipped_oom, |
| 1332 | => break .dependency_failure, |
| 1367 | 1333 | |
| 1368 | | if (run.web_server) |*ws| ws.updateStepStatus(s, .wip); |
| 1334 | .success, .skipped => {}, |
| 1335 | } |
| 1336 | } else if (s.make(.{ |
| 1337 | .progress_node = step_prog_node, |
| 1338 | .watch = run.watch, |
| 1339 | .web_server = if (run.web_server) |*ws| ws else null, |
| 1340 | .unit_test_timeout_ns = run.unit_test_timeout_ns, |
| 1341 | .gpa = gpa, |
| 1342 | })) state: { |
| 1343 | break :state .success; |
| 1344 | } else |err| switch (err) { |
| 1345 | error.MakeFailed => .failure, |
| 1346 | error.MakeSkipped => .skipped, |
| 1347 | }; |
| 1369 | 1348 | |
| 1370 | | const make_result = s.make(.{ |
| 1371 | | .progress_node = sub_prog_node, |
| 1372 | | .watch = run.watch, |
| 1373 | | .web_server = if (run.web_server) |*ws| ws else null, |
| 1374 | | .unit_test_timeout_ns = run.unit_test_timeout_ns, |
| 1375 | | .gpa = gpa, |
| 1376 | | }); |
| 1349 | @atomicStore(Step.State, &s.state, new_state, .monotonic); |
| 1377 | 1350 | |
| 1378 | | // No matter the result, we want to display error/warning messages. |
| 1379 | | const show_compile_errors = s.result_error_bundle.errorMessageCount() > 0; |
| 1380 | | const show_error_msgs = s.result_error_msgs.items.len > 0; |
| 1381 | | const show_stderr = s.result_stderr.len > 0; |
| 1382 | | if (show_error_msgs or show_compile_errors or show_stderr) { |
| 1383 | | const stderr = io.lockStderr(&stdio_buffer_allocation, graph.stderr_mode) catch |err| switch (err) { |
| 1384 | | error.Canceled => return, |
| 1385 | | }; |
| 1386 | | defer io.unlockStderr(); |
| 1387 | | printErrorMessages(gpa, s, .{}, stderr.terminal(), run.error_style, run.multiline_errors) catch {}; |
| 1388 | | } |
| 1351 | switch (new_state) { |
| 1352 | .precheck_unstarted => unreachable, |
| 1353 | .precheck_started => unreachable, |
| 1354 | .precheck_done => unreachable, |
| 1389 | 1355 | |
| 1390 | | handle_result: { |
| 1391 | | if (make_result) |_| { |
| 1392 | | @atomicStore(Step.State, &s.state, .success, .seq_cst); |
| 1393 | | if (run.web_server) |*ws| ws.updateStepStatus(s, .success); |
| 1394 | | } else |err| switch (err) { |
| 1395 | | error.MakeFailed => { |
| 1396 | | @atomicStore(Step.State, &s.state, .failure, .seq_cst); |
| 1356 | .failure, |
| 1357 | .dependency_failure, |
| 1358 | .skipped_oom, |
| 1359 | => { |
| 1397 | 1360 | if (run.web_server) |*ws| ws.updateStepStatus(s, .failure); |
| 1398 | 1361 | std.Progress.setStatus(.failure_working); |
| 1399 | | break :handle_result; |
| 1400 | 1362 | }, |
| 1401 | | error.MakeSkipped => { |
| 1402 | | @atomicStore(Step.State, &s.state, .skipped, .seq_cst); |
| 1363 | |
| 1364 | .success, |
| 1365 | .skipped, |
| 1366 | => { |
| 1403 | 1367 | if (run.web_server) |*ws| ws.updateStepStatus(s, .success); |
| 1404 | 1368 | }, |
| 1405 | 1369 | } |
| 1370 | } |
| 1406 | 1371 | |
| 1407 | | // Successful completion of a step, so we queue up its dependants as well. |
| 1408 | | for (s.dependants.items) |dep| { |
| 1409 | | group.async(io, workerMakeOneStep, .{ group, b, dep, prog_node, run }); |
| 1410 | | } |
| 1372 | // No matter the result, we want to display error/warning messages. |
| 1373 | if (s.result_error_bundle.errorMessageCount() > 0 or |
| 1374 | s.result_error_msgs.items.len > 0 or |
| 1375 | s.result_stderr.len > 0) |
| 1376 | { |
| 1377 | const stderr = try io.lockStderr(&stdio_buffer_allocation, graph.stderr_mode); |
| 1378 | defer io.unlockStderr(); |
| 1379 | printErrorMessages(gpa, s, .{}, stderr.terminal(), run.error_style, run.multiline_errors) catch {}; |
| 1411 | 1380 | } |
| 1412 | 1381 | |
| 1413 | | // If this is a step that claims resources, we must now queue up other |
| 1414 | | // steps that are waiting for resources. |
| 1415 | 1382 | if (s.max_rss != 0) { |
| 1416 | | var dispatch_deps: std.ArrayList(*Step) = .empty; |
| 1417 | | defer dispatch_deps.deinit(gpa); |
| 1383 | var dispatch_set: std.ArrayList(*Step) = .empty; |
| 1384 | defer dispatch_set.deinit(gpa); |
| 1418 | 1385 | |
| 1386 | // Release our RSS claim and kick off some blocked steps if possible. We use `dispatch_set` |
| 1387 | // as a staging buffer to avoid recursing into `makeStep` while `run.max_rss_mutex` is held. |
| 1419 | 1388 | { |
| 1420 | | run.max_rss_mutex.lockUncancelable(io); |
| 1389 | try run.max_rss_mutex.lock(io); |
| 1421 | 1390 | defer run.max_rss_mutex.unlock(io); |
| 1422 | | |
| 1423 | | dispatch_deps.ensureUnusedCapacity(gpa, run.memory_blocked_steps.items.len) catch @panic("OOM"); |
| 1424 | | |
| 1425 | | // Give the memory back to the scheduler. |
| 1426 | | run.claimed_rss -= s.max_rss; |
| 1427 | | // Avoid kicking off too many tasks that we already know will not have |
| 1428 | | // enough resources. |
| 1429 | | var remaining = run.max_rss - run.claimed_rss; |
| 1430 | | var i: usize = 0; |
| 1431 | | for (run.memory_blocked_steps.items) |dep| { |
| 1432 | | assert(dep.max_rss != 0); |
| 1433 | | if (dep.max_rss <= remaining) { |
| 1434 | | remaining -= dep.max_rss; |
| 1435 | | dispatch_deps.appendAssumeCapacity(dep); |
| 1436 | | } else { |
| 1437 | | run.memory_blocked_steps.items[i] = dep; |
| 1438 | | i += 1; |
| 1439 | | } |
| 1391 | run.available_rss += s.max_rss; |
| 1392 | dispatch_set.ensureUnusedCapacity(gpa, run.memory_blocked_steps.items.len) catch @panic("OOM"); |
| 1393 | while (run.memory_blocked_steps.getLastOrNull()) |candidate| { |
| 1394 | if (run.available_rss < candidate.max_rss) break; |
| 1395 | assert(run.memory_blocked_steps.pop() == candidate); |
| 1396 | dispatch_set.appendAssumeCapacity(candidate); |
| 1440 | 1397 | } |
| 1441 | | run.memory_blocked_steps.shrinkRetainingCapacity(i); |
| 1442 | 1398 | } |
| 1443 | | for (dispatch_deps.items) |dep| { |
| 1444 | | // Must be called without max_rss_mutex held in case it executes recursively. |
| 1445 | | group.async(io, workerMakeOneStep, .{ group, b, dep, prog_node, run }); |
| 1399 | for (dispatch_set.items) |candidate| { |
| 1400 | group.async(io, makeStep, .{ group, b, candidate, root_prog_node, run }); |
| 1401 | } |
| 1402 | } |
| 1403 | |
| 1404 | for (s.dependants.items) |dependant| { |
| 1405 | // `.acq_rel` synchronizes with itself to ensure all dependencies' final states are visible when this hits 0. |
| 1406 | if (@atomicRmw(u32, &dependant.pending_deps, .Sub, 1, .acq_rel) == 1) { |
| 1407 | try stepReady(group, b, dependant, root_prog_node, run); |
| 1408 | } |
| 1409 | } |
| 1410 | } |
| 1411 | |
| 1412 | fn stepReady( |
| 1413 | group: *Io.Group, |
| 1414 | b: *std.Build, |
| 1415 | s: *Step, |
| 1416 | root_prog_node: std.Progress.Node, |
| 1417 | run: *Run, |
| 1418 | ) !void { |
| 1419 | const io = b.graph.io; |
| 1420 | if (s.max_rss != 0) { |
| 1421 | try run.max_rss_mutex.lock(io); |
| 1422 | defer run.max_rss_mutex.unlock(io); |
| 1423 | if (run.available_rss < s.max_rss) { |
| 1424 | // Running this step right now could possibly exceed the allotted RSS. |
| 1425 | run.memory_blocked_steps.append(run.gpa, s) catch @panic("OOM"); |
| 1426 | return; |
| 1446 | 1427 | } |
| 1428 | run.available_rss -= s.max_rss; |
| 1447 | 1429 | } |
| 1430 | group.async(io, makeStep, .{ group, b, s, root_prog_node, run }); |
| 1448 | 1431 | } |
| 1449 | 1432 | |
| 1450 | 1433 | pub fn printErrorMessages( |