commit b22eb176b0d6e2275b89b715f7e3f0dd0a5fc574 (tree)
parent 752f991c09901b07a7084555e140384d162d72c9
Author: Andrew Kelley <andrew@ziglang.org>
Date: Wed, 1 Apr 2026 17:45:29 +0200
Merge pull request 'audit remaining uses of thread spawning' (#31725) from audit-thread-spawning into master
Reviewed-on: https://codeberg.org/ziglang/zig/pulls/31725
Diffstat:
2 files changed, 29 insertions(+), 22 deletions(-)
diff --git a/lib/std/Build/WebServer.zig b/lib/std/Build/WebServer.zig
@@ -6,7 +6,7 @@ root_prog_node: std.Progress.Node,
watch: bool,
tcp_server: ?net.Server,
-serve_thread: ?std.Thread,
+serve_task: ?Io.Future(Io.Cancelable!void),
/// Uses `Io.Clock.awake`.
base_timestamp: Io.Timestamp,
@@ -103,7 +103,7 @@ pub fn init(opts: Options) WebServer {
.watch = opts.watch,
.tcp_server = null,
- .serve_thread = null,
+ .serve_task = null,
.base_timestamp = opts.base_timestamp.raw,
.step_names_trailing = step_names_trailing,
@@ -136,9 +136,9 @@ pub fn deinit(ws: *WebServer) void {
gpa.free(ws.time_report_msgs);
gpa.free(ws.time_report_update_times);
- if (ws.serve_thread) |t| {
+ if (ws.serve_task) |t| {
if (ws.tcp_server) |*s| s.stream.close(io);
- t.join();
+ t.await();
}
if (ws.tcp_server) |*s| s.deinit();
@@ -146,15 +146,15 @@ pub fn deinit(ws: *WebServer) void {
}
pub fn start(ws: *WebServer) error{AlreadyReported}!void {
assert(ws.tcp_server == null);
- assert(ws.serve_thread == null);
+ assert(ws.serve_task == null);
const io = ws.graph.io;
ws.tcp_server = ws.listen_address.listen(io, .{ .reuse_address = true }) catch |err| {
- log.err("failed to listen to port {d}: {s}", .{ ws.listen_address.getPort(), @errorName(err) });
+ log.err("failed to listen to port {d}: {t}", .{ ws.listen_address.getPort(), err });
return error.AlreadyReported;
};
- ws.serve_thread = std.Thread.spawn(.{}, serve, .{ws}) catch |err| {
- log.err("unable to spawn web server thread: {s}", .{@errorName(err)});
+ ws.serve_task = io.concurrent(serve, .{ws}) catch |err| {
+ log.err("unable to spawn web server thread: {t}", .{err});
ws.tcp_server.?.deinit(io);
ws.tcp_server = null;
return error.AlreadyReported;
@@ -165,15 +165,20 @@ pub fn start(ws: *WebServer) error{AlreadyReported}!void {
log.info("hint: pass '--webui={f}' to use the same port next time", .{ws.tcp_server.?.socket.address});
}
}
-fn serve(ws: *WebServer) void {
+fn serve(ws: *WebServer) Io.Cancelable!void {
const io = ws.graph.io;
+ var group: Io.Group = .init;
+ defer group.cancel(io);
while (true) {
- var stream = ws.tcp_server.?.accept(io) catch |err| {
- log.err("failed to accept connection: {s}", .{@errorName(err)});
- return;
+ var stream = ws.tcp_server.?.accept(io) catch |err| switch (err) {
+ error.Canceled => |e| return e,
+ else => |e| {
+ log.err("failed to accept connection: {t}", .{e});
+ return;
+ },
};
- _ = std.Thread.spawn(.{}, accept, .{ ws, stream }) catch |err| {
- log.err("unable to spawn connection thread: {s}", .{@errorName(err)});
+ group.concurrent(io, accept, .{ ws, stream }) catch |err| {
+ log.err("unable to spawn connection thread: {t}", .{err});
stream.close(io);
continue;
};
@@ -303,8 +308,8 @@ fn serveWebSocket(ws: *WebServer, sock: *http.Server.WebSocket) !noreturn {
copy.* = @atomicLoad(u8, shared, .monotonic);
}
- const recv_thread = try std.Thread.spawn(.{}, recvWebSocketMessages, .{ ws, sock });
- defer recv_thread.join();
+ var recv_thread = try io.concurrent(recvWebSocketMessages, .{ ws, sock });
+ defer recv_thread.cancel(io);
{
const hello_header: abi.Hello = .{
diff --git a/lib/std/fs/test.zig b/lib/std/fs/test.zig
@@ -1758,9 +1758,7 @@ test "open file with exclusive and shared nonblocking lock" {
}
test "open file with exclusive lock twice, make sure second lock waits" {
- if (builtin.single_threaded) return error.SkipZigTest;
-
- try testWithAllSupportedPathTypes(struct {
+ testWithAllSupportedPathTypes(struct {
fn impl(ctx: *TestContext) !void {
const io = ctx.io;
const filename = try ctx.transformPath("file_lock_test.txt");
@@ -1781,8 +1779,8 @@ test "open file with exclusive lock twice, make sure second lock waits" {
var started: Io.Event = .unset;
var locked: Io.Event = .unset;
- const t = try std.Thread.spawn(.{}, S.checkFn, .{ ctx, filename, &started, &locked });
- defer t.join();
+ var t = try io.concurrent(S.checkFn, .{ ctx, filename, &started, &locked });
+ defer t.cancel(io) catch {};
// Wait for the spawned thread to start trying to acquire the exclusive file lock.
// Then wait a bit to make sure that can't acquire it since we currently hold the file lock.
@@ -1795,8 +1793,12 @@ test "open file with exclusive lock twice, make sure second lock waits" {
// Release the file lock which should unlock the thread to lock it and set the locked event.
file.close(io);
try locked.wait(io);
+ try t.await(io);
}
- }.impl);
+ }.impl) catch |err| switch (err) {
+ error.ConcurrencyUnavailable => return error.SkipZigTest,
+ else => |e| return e,
+ };
}
test "open file with exclusive nonblocking lock twice (absolute paths)" {