commit 5ac6ff43d41f23d7d215c3164848bb4ffcf00d59 (tree)
parent 6a9510c0eb85434f75730f512f4d9e4bed5cfcb3
Author: Andrew Kelley <andrew@ziglang.org>
Date: Sat, 21 Feb 2026 01:29:06 +0100
Merge pull request 'Io.Select: cancelation and concurrent' (#30836) from blblack/zig:select-stuff into master
Reviewed-on: https://codeberg.org/ziglang/zig/pulls/30836
Reviewed-by: Andrew Kelley <andrew@ziglang.org>
Diffstat:
| M | lib/std/Io.zig | | | 51 | +++++++++++++++++++++++++++++++++++++++++++++++---- |
1 file changed, 47 insertions(+), 4 deletions(-)
diff --git a/lib/std/Io.zig b/lib/std/Io.zig
@@ -1206,7 +1206,7 @@ pub fn Select(comptime U: type) type {
/// already been called and completed, or it has successfully been
/// assigned a unit of concurrency.
///
- /// After this is called, `wait` or `cancel` must be called before the
+ /// After this is called, `await` or `cancel` must be called before the
/// select is deinitialized.
///
/// Threadsafe.
@@ -1225,10 +1225,13 @@ pub fn Select(comptime U: type) type {
args: @TypeOf(args),
fn start(type_erased_context: *const anyopaque) Cancelable!void {
const context: *const @This() = @ptrCast(@alignCast(type_erased_context));
- const elem = @unionInit(U, @tagName(field), @call(.auto, function, context.args));
+ const raw_result = @call(.auto, function, context.args);
+ const elem = @unionInit(U, @tagName(field), raw_result);
context.select.queue.putOneUncancelable(context.select.io, elem) catch |err| switch (err) {
error.Closed => unreachable,
};
+ if (@typeInfo(@TypeOf(raw_result)) == .error_union)
+ raw_result catch |err| if (err == error.Canceled) return error.Canceled;
}
};
const context: Context = .{ .select = s, .args = args };
@@ -1236,6 +1239,46 @@ pub fn Select(comptime U: type) type {
s.io.vtable.groupAsync(s.io.userdata, &s.group, @ptrCast(&context), .of(Context), Context.start);
}
+ /// Calls `function` with `args` concurrently. The resource spawned is
+ /// owned by the select.
+ ///
+ /// `function` must have return type matching the `field` field of `Union`.
+ ///
+ /// After this function returns successfully, it is guaranteed that
+ /// `function` has been assigned a unit of concurrency, and `await` or
+ /// `cancel` must be called before the select is deinitialized.
+ ///
+ ///
+ /// Threadsafe.
+ ///
+ /// Related:
+ /// * `Io.concurrent`
+ /// * `Group.concurrent`
+ pub fn concurrent(
+ s: *S,
+ comptime field: Field,
+ function: anytype,
+ args: std.meta.ArgsTuple(@TypeOf(function)),
+ ) ConcurrentError!void {
+ const Context = struct {
+ select: *S,
+ args: @TypeOf(args),
+ fn start(type_erased_context: *const anyopaque) Cancelable!void {
+ const context: *const @This() = @ptrCast(@alignCast(type_erased_context));
+ const raw_result = @call(.auto, function, context.args);
+ const elem = @unionInit(U, @tagName(field), raw_result);
+ context.select.queue.putOneUncancelable(context.select.io, elem) catch |err| switch (err) {
+ error.Closed => unreachable,
+ };
+ if (@typeInfo(@TypeOf(raw_result)) == .error_union)
+ raw_result catch |err| if (err == error.Canceled) return error.Canceled;
+ }
+ };
+ const context: Context = .{ .select = s, .args = args };
+ try s.io.vtable.groupConcurrent(s.io.userdata, &s.group, @ptrCast(&context), .of(Context), Context.start);
+ _ = @atomicRmw(usize, &s.outstanding, .Add, 1, .monotonic);
+ }
+
/// Blocks until another task of the select finishes.
///
/// Asserts there is at least one more `outstanding` task.
@@ -1249,12 +1292,12 @@ pub fn Select(comptime U: type) type {
};
}
- /// Equivalent to `wait` but requests cancelation on all remaining
+ /// Equivalent to `await` but requests cancelation on all remaining
/// tasks owned by the select.
///
/// For a description of cancelation and cancelation points, see `Future.cancel`.
///
- /// It is illegal to call `wait` after this.
+ /// It is illegal to call `await` after this.
///
/// Idempotent. Not threadsafe.
pub fn cancel(s: *S) void {