Runs the "make" function of the single step s, updates its state, and then spawns newly-ready
dependant steps in group. If s makes an RSS claim (i.e. s.max_rss != 0), the caller must
have already subtracted this value from maker.available_rss. This function will release the RSS
claim (i.e. add s.max_rss back into maker.available_rss) and queue any viable memory-blocked
steps after "make" completes for s.
fn makeStep(
maker: *Maker,
group: *Io.Group,
step_index: Configuration.Step.Index,
root_prog_node: std.Progress.Node,
) Io.Cancelable!void
fn makeStep(
maker: *Maker,
group: *Io.Group,
step_index: Configuration.Step.Index,
root_prog_node: std.Progress.Node,
) Io.Cancelable!void {
const graph = maker.graph;
const io = graph.io;
const gpa = maker.gpa;
const c = &maker.scanned_config.configuration;
const conf_step = step_index.ptr(c);
const step_name = conf_step.name.slice(c);
const deps = conf_step.deps.slice(c);
const make_step = maker.stepByIndex(step_index);
{
const step_prog_node = root_prog_node.start(step_name, 0);
defer step_prog_node.end();
if (maker.web_server) |ws| ws.updateStepStatus(step_index, .wip);
const new_state: Step.State = for (deps) |dep_index| {
const dep_make_step = maker.stepByIndex(dep_index);
switch (@atomicLoad(Step.State, &dep_make_step.state, .monotonic)) {
.precheck_unstarted => unreachable,
.precheck_started => unreachable,
.precheck_done => unreachable,
.failure,
.dependency_failure,
.skipped_oom,
=> break .dependency_failure,
.success, .skipped => {},
}
} else if (Step.make(step_index, maker, step_prog_node)) state: {
break :state .success;
} else |err| switch (err) {
error.MakeFailed => .failure,
error.MakeSkipped => .skipped,
error.Canceled => |e| return e,
};
@atomicStore(Step.State, &make_step.state, new_state, .monotonic);
switch (new_state) {
.precheck_unstarted => unreachable,
.precheck_started => unreachable,
.precheck_done => unreachable,
.failure,
.dependency_failure,
.skipped_oom,
=> {
if (maker.web_server) |ws| ws.updateStepStatus(step_index, .failure);
std.Progress.setStatus(.failure_working);
},
.success,
.skipped,
=> {
if (maker.web_server) |ws| ws.updateStepStatus(step_index, .success);
},
}
}
// No matter the result, we want to display error/warning messages.
if (make_step.result_error_bundle.errorMessageCount() > 0 or
make_step.result_error_msgs.items.len > 0 or
make_step.result_stderr.len > 0)
{
const stderr = try io.lockStderr(&stdio_buffer_allocation, graph.stderr_mode);
defer io.unlockStderr();
printErrorMessages(maker, step_index, .{}, stderr.terminal(), maker.error_style, maker.multiline_errors) catch |err| switch (err) {
error.Canceled => |e| return e,
error.WriteFailed => switch (stderr.file_writer.err.?) {
error.Canceled => |e| return e,
else => {},
},
else => {},
};
}
const max_rss = conf_step.max_rss.toBytes();
if (max_rss != 0) {
var dispatch_set: std.ArrayList(Configuration.Step.Index) = .empty;
defer dispatch_set.deinit(gpa);
// Release our RSS claim and kick off some blocked steps if possible. We use `dispatch_set`
// as a staging buffer to avoid recursing into `makeStep` while `maker.max_rss_mutex` is held.
{
try maker.max_rss_mutex.lock(io);
defer maker.max_rss_mutex.unlock(io);
maker.available_rss += max_rss;
dispatch_set.ensureUnusedCapacity(gpa, maker.memory_blocked_steps.items.len) catch
@panic("TODO eliminate memory allocation here");
while (maker.memory_blocked_steps.getLast()) |candidate_index| {
const candidate_max_rss = candidate_index.ptr(c).max_rss.toBytes();
if (maker.available_rss < candidate_max_rss) break;
assert(maker.memory_blocked_steps.pop() == candidate_index);
dispatch_set.appendAssumeCapacity(candidate_index);
}
}
for (dispatch_set.items) |candidate| {
group.async(io, makeStep, .{ maker, group, candidate, root_prog_node });
}
}
for (make_step.dependants.items) |dependant_index| {
const dependant = maker.stepByIndex(dependant_index);
// `.acq_rel` synchronizes with itself to ensure all dependencies' final states are visible when this hits 0.
if (@atomicRmw(u32, &dependant.pending_deps, .Sub, 1, .acq_rel) == 1) {
try stepReady(maker, group, dependant_index, root_prog_node);
}
}
}