Skip to content

Commit a242292

Browse files
committed
build runner: update from std.Thread.Pool to std.Io
1 parent 32dc46a commit a242292

File tree

6 files changed

+40
-68
lines changed

6 files changed

+40
-68
lines changed

lib/compiler/build_runner.zig

Lines changed: 13 additions & 37 deletions
Original file line numberDiff line numberDiff line change
@@ -107,7 +107,6 @@ pub fn main() !void {
107107

108108
var targets = std.array_list.Managed([]const u8).init(arena);
109109
var debug_log_scopes = std.array_list.Managed([]const u8).init(arena);
110-
var thread_pool_options: std.Thread.Pool.Options = .{ .allocator = arena };
111110

112111
var install_prefix: ?[]const u8 = null;
113112
var dir_list = std.Build.DirList{};
@@ -413,19 +412,11 @@ pub fn main() !void {
413412
};
414413
} else if (mem.eql(u8, arg, "-fno-reference-trace")) {
415414
builder.reference_trace = null;
416-
} else if (mem.startsWith(u8, arg, "-j")) {
417-
const num = arg["-j".len..];
418-
const n_jobs = std.fmt.parseUnsigned(u32, num, 10) catch |err| {
419-
std.debug.print("unable to parse jobs count '{s}': {s}", .{
420-
num, @errorName(err),
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;
415+
} else if (mem.cutPrefix(u8, arg, "-j")) |text| {
416+
const n = std.fmt.parseUnsigned(u32, text, 10) catch |err|
417+
fatal("unable to parse jobs count '{s}': {t}", .{ text, err });
418+
if (n < 1) fatal("number of jobs must be at least 1", .{});
419+
threaded.setAsyncLimit(.limited(n));
429420
} else if (mem.eql(u8, arg, "--")) {
430421
builder.args = argsRest(args, arg_idx);
431422
break;
@@ -516,7 +507,6 @@ pub fn main() !void {
516507
.error_style = error_style,
517508
.multiline_errors = multiline_errors,
518509
.summary = summary orelse if (watch or webui_listen != null) .line else .failures,
519-
.thread_pool = undefined,
520510

521511
.ttyconf = ttyconf,
522512
};
@@ -547,16 +537,12 @@ pub fn main() !void {
547537
break :w try .init();
548538
};
549539

550-
try run.thread_pool.init(thread_pool_options);
551-
defer run.thread_pool.deinit();
552-
553540
const now = Io.Clock.Timestamp.now(io, .awake) catch |err| fatal("failed to collect timestamp: {t}", .{err});
554541

555542
run.web_server = if (webui_listen) |listen_address| ws: {
556543
if (builtin.single_threaded) unreachable; // `fatal` above
557544
break :ws .init(.{
558545
.gpa = gpa,
559-
.thread_pool = &run.thread_pool,
560546
.ttyconf = ttyconf,
561547
.graph = &graph,
562548
.all_steps = run.step_stack.keys(),
@@ -675,7 +661,6 @@ const Run = struct {
675661
memory_blocked_steps: std.ArrayList(*Step),
676662
/// Allocated into `gpa`.
677663
step_stack: std.AutoArrayHashMapUnmanaged(*Step, void),
678-
thread_pool: std.Thread.Pool,
679664
/// Similar to the `tty.Config` returned by `std.debug.lockStderrWriter`,
680665
/// but also respects the '--color' flag.
681666
ttyconf: tty.Config,
@@ -754,14 +739,13 @@ fn runStepNames(
754739
const gpa = run.gpa;
755740
const io = b.graph.io;
756741
const step_stack = &run.step_stack;
757-
const thread_pool = &run.thread_pool;
758742

759743
{
760744
const step_prog = parent_prog_node.start("steps", step_stack.count());
761745
defer step_prog.end();
762746

763-
var wait_group: std.Thread.WaitGroup = .{};
764-
defer wait_group.wait();
747+
var group: Io.Group = .init;
748+
defer group.wait(io);
765749

766750
// Here we spawn the initial set of tasks with a nice heuristic -
767751
// dependency order. Each worker when it finishes a step will then
@@ -771,9 +755,7 @@ fn runStepNames(
771755
const step = steps_slice[steps_slice.len - i - 1];
772756
if (step.state == .skipped_oom) continue;
773757

774-
thread_pool.spawnWg(&wait_group, workerMakeOneStep, .{
775-
&wait_group, b, step, step_prog, run,
776-
});
758+
group.async(io, workerMakeOneStep, .{ &group, b, step, step_prog, run });
777759
}
778760
}
779761

@@ -855,7 +837,6 @@ fn runStepNames(
855837
var f = std.Build.Fuzz.init(
856838
gpa,
857839
io,
858-
thread_pool,
859840
run.ttyconf,
860841
step_stack.keys(),
861842
parent_prog_node,
@@ -1318,14 +1299,12 @@ fn constructGraphAndCheckForDependencyLoop(
13181299
}
13191300

13201301
fn workerMakeOneStep(
1321-
wg: *std.Thread.WaitGroup,
1302+
group: *Io.Group,
13221303
b: *std.Build,
13231304
s: *Step,
13241305
prog_node: std.Progress.Node,
13251306
run: *Run,
13261307
) void {
1327-
const thread_pool = &run.thread_pool;
1328-
13291308
// First, check the conditions for running this step. If they are not met,
13301309
// then we return without doing the step, relying on another worker to
13311310
// queue this step up again when dependencies are met.
@@ -1381,7 +1360,6 @@ fn workerMakeOneStep(
13811360

13821361
const make_result = s.make(.{
13831362
.progress_node = sub_prog_node,
1384-
.thread_pool = thread_pool,
13851363
.watch = run.watch,
13861364
.web_server = if (run.web_server) |*ws| ws else null,
13871365
.ttyconf = run.ttyconf,
@@ -1400,6 +1378,8 @@ fn workerMakeOneStep(
14001378
printErrorMessages(run.gpa, s, .{}, bw, ttyconf, run.error_style, run.multiline_errors) catch {};
14011379
}
14021380

1381+
const io = b.graph.io;
1382+
14031383
handle_result: {
14041384
if (make_result) |_| {
14051385
@atomicStore(Step.State, &s.state, .success, .seq_cst);
@@ -1419,9 +1399,7 @@ fn workerMakeOneStep(
14191399

14201400
// Successful completion of a step, so we queue up its dependants as well.
14211401
for (s.dependants.items) |dep| {
1422-
thread_pool.spawnWg(wg, workerMakeOneStep, .{
1423-
wg, b, dep, prog_node, run,
1424-
});
1402+
group.async(io, workerMakeOneStep, .{ group, b, dep, prog_node, run });
14251403
}
14261404
}
14271405

@@ -1444,9 +1422,7 @@ fn workerMakeOneStep(
14441422
if (dep.max_rss <= remaining) {
14451423
remaining -= dep.max_rss;
14461424

1447-
thread_pool.spawnWg(wg, workerMakeOneStep, .{
1448-
wg, b, dep, prog_node, run,
1449-
});
1425+
group.async(io, workerMakeOneStep, .{ group, b, dep, prog_node, run });
14501426
} else {
14511427
run.memory_blocked_steps.items[i] = dep;
14521428
i += 1;

lib/std/Build/Fuzz.zig

Lines changed: 16 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -22,10 +22,9 @@ mode: Mode,
2222
/// Allocated into `gpa`.
2323
run_steps: []const *Step.Run,
2424

25-
wait_group: std.Thread.WaitGroup,
25+
group: Io.Group,
2626
root_prog_node: std.Progress.Node,
2727
prog_node: std.Progress.Node,
28-
thread_pool: *std.Thread.Pool,
2928

3029
/// Protects `coverage_files`.
3130
coverage_mutex: std.Thread.Mutex,
@@ -78,7 +77,6 @@ const CoverageMap = struct {
7877
pub fn init(
7978
gpa: Allocator,
8079
io: Io,
81-
thread_pool: *std.Thread.Pool,
8280
ttyconf: tty.Config,
8381
all_steps: []const *Build.Step,
8482
root_prog_node: std.Progress.Node,
@@ -89,20 +87,22 @@ pub fn init(
8987
defer steps.deinit(gpa);
9088
const rebuild_node = root_prog_node.start("Rebuilding Unit Tests", 0);
9189
defer rebuild_node.end();
92-
var rebuild_wg: std.Thread.WaitGroup = .{};
93-
defer rebuild_wg.wait();
90+
var rebuild_group: Io.Group = .init;
91+
defer rebuild_group.cancel(io);
9492

9593
for (all_steps) |step| {
9694
const run = step.cast(Step.Run) orelse continue;
9795
if (run.producer == null) continue;
9896
if (run.fuzz_tests.items.len == 0) continue;
9997
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 });
10199
}
102100

103101
if (steps.items.len == 0) fatal("no fuzz tests found", .{});
104102
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;
106106
};
107107
errdefer gpa.free(run_steps);
108108

@@ -118,8 +118,7 @@ pub fn init(
118118
.ttyconf = ttyconf,
119119
.mode = mode,
120120
.run_steps = run_steps,
121-
.wait_group = .{},
122-
.thread_pool = thread_pool,
121+
.group = .init,
123122
.root_prog_node = root_prog_node,
124123
.prog_node = .none,
125124
.coverage_files = .empty,
@@ -131,29 +130,26 @@ pub fn init(
131130
}
132131

133132
pub fn start(fuzz: *Fuzz) void {
133+
const io = fuzz.io;
134134
fuzz.prog_node = fuzz.root_prog_node.start("Fuzzing", fuzz.run_steps.len);
135135

136136
if (fuzz.mode == .forever) {
137137
// For polling messages and sending updates to subscribers.
138-
fuzz.wait_group.start();
139-
_ = std.Thread.spawn(.{}, coverageRun, .{fuzz}) catch |err| {
140-
fuzz.wait_group.finish();
141-
fatal("unable to spawn coverage thread: {s}", .{@errorName(err)});
142-
};
138+
fuzz.group.concurrent(io, coverageRun, .{fuzz}) catch |err|
139+
fatal("unable to spawn coverage task: {t}", .{err});
143140
}
144141

145142
for (fuzz.run_steps) |run| {
146143
for (run.fuzz_tests.items) |unit_test_index| {
147144
assert(run.rebuilt_executable != null);
148-
fuzz.thread_pool.spawnWg(&fuzz.wait_group, fuzzWorkerRun, .{
149-
fuzz, run, unit_test_index,
150-
});
145+
fuzz.group.async(io, fuzzWorkerRun, .{ fuzz, run, unit_test_index });
151146
}
152147
}
153148
}
154149

155150
pub 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);
157153
fuzz.prog_node.end();
158154
fuzz.gpa.free(fuzz.run_steps);
159155
}
@@ -335,8 +331,6 @@ pub fn sendUpdate(
335331
}
336332

337333
fn coverageRun(fuzz: *Fuzz) void {
338-
defer fuzz.wait_group.finish();
339-
340334
fuzz.queue_mutex.lock();
341335
defer fuzz.queue_mutex.unlock();
342336

@@ -511,8 +505,8 @@ pub fn waitAndPrintReport(fuzz: *Fuzz) void {
511505
assert(fuzz.mode == .limit);
512506
const io = fuzz.io;
513507

514-
fuzz.wait_group.wait();
515-
fuzz.wait_group.reset();
508+
fuzz.group.wait(io);
509+
fuzz.group = .init;
516510

517511
std.debug.print("======= FUZZING REPORT =======\n", .{});
518512
for (fuzz.msg_queue.items) |msg| {

lib/std/Build/Step.zig

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -110,7 +110,6 @@ pub const TestResults = struct {
110110

111111
pub const MakeOptions = struct {
112112
progress_node: std.Progress.Node,
113-
thread_pool: *std.Thread.Pool,
114113
watch: bool,
115114
web_server: switch (builtin.target.cpu.arch) {
116115
else => ?*Build.WebServer,

lib/std/Build/Step/Run.zig

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1151,7 +1151,6 @@ pub fn rerunInFuzzMode(
11511151
const tmp_dir_path = "tmp" ++ fs.path.sep_str ++ std.fmt.hex(rand_int);
11521152
try runCommand(run, argv_list.items, has_side_effects, tmp_dir_path, .{
11531153
.progress_node = prog_node,
1154-
.thread_pool = undefined, // not used by `runCommand`
11551154
.watch = undefined, // not used by `runCommand`
11561155
.web_server = null, // only needed for time reports
11571156
.ttyconf = fuzz.ttyconf,

lib/std/Build/WebServer.zig

Lines changed: 0 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,4 @@
11
gpa: Allocator,
2-
thread_pool: *std.Thread.Pool,
32
graph: *const Build.Graph,
43
all_steps: []const *Build.Step,
54
listen_address: net.IpAddress,
@@ -53,7 +52,6 @@ pub fn notifyUpdate(ws: *WebServer) void {
5352

5453
pub const Options = struct {
5554
gpa: Allocator,
56-
thread_pool: *std.Thread.Pool,
5755
ttyconf: Io.tty.Config,
5856
graph: *const std.Build.Graph,
5957
all_steps: []const *Build.Step,
@@ -100,7 +98,6 @@ pub fn init(opts: Options) WebServer {
10098

10199
return .{
102100
.gpa = opts.gpa,
103-
.thread_pool = opts.thread_pool,
104101
.ttyconf = opts.ttyconf,
105102
.graph = opts.graph,
106103
.all_steps = all_steps,
@@ -235,7 +232,6 @@ pub fn finishBuild(ws: *WebServer, opts: struct {
235232
ws.fuzz = Fuzz.init(
236233
ws.gpa,
237234
ws.graph.io,
238-
ws.thread_pool,
239235
ws.ttyconf,
240236
ws.all_steps,
241237
ws.root_prog_node,

lib/std/Io/Threaded.zig

Lines changed: 11 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,9 @@ wait_group: std.Thread.WaitGroup = .{},
3333
/// immediately.
3434
///
3535
/// 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`.
3639
async_limit: Io.Limit,
3740
/// Maximum thread pool size (excluding main thread) for dispatching concurrent
3841
/// tasks. Until this limit, calls to `Io.concurrent` will increase the thread
@@ -168,6 +171,12 @@ pub const init_single_threaded: Threaded = .{
168171
.have_signal_handler = false,
169172
};
170173

174+
pub 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+
171180
pub fn deinit(t: *Threaded) void {
172181
t.join();
173182
if (is_windows and t.wsa.status == .initialized) {
@@ -507,7 +516,7 @@ fn async(
507516
start: *const fn (context: *const anyopaque, result: *anyopaque) void,
508517
) ?*Io.AnyFuture {
509518
const t: *Threaded = @ptrCast(@alignCast(userdata));
510-
if (builtin.single_threaded or t.async_limit == .nothing) {
519+
if (builtin.single_threaded) {
511520
start(context.ptr, result.ptr);
512521
return null;
513522
}
@@ -684,8 +693,7 @@ fn groupAsync(
684693
start: *const fn (*Io.Group, context: *const anyopaque) void,
685694
) void {
686695
const t: *Threaded = @ptrCast(@alignCast(userdata));
687-
if (builtin.single_threaded or t.async_limit == .nothing)
688-
return start(group, context.ptr);
696+
if (builtin.single_threaded) return start(group, context.ptr);
689697

690698
const gpa = t.allocator;
691699
const gc = GroupClosure.init(gpa, t, group, context, context_alignment, start) catch

0 commit comments

Comments
 (0)