zig

fork of https://codeberg.org/ziglang/zig
Log | Files | Refs | README | LICENSE

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:
Mlib/std/Build/WebServer.zig | 37+++++++++++++++++++++----------------
Mlib/std/fs/test.zig | 14++++++++------
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)" {