authorgravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2026-02-21 01:29:06+01:00
committergravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2026-02-21 01:29:06+01:00
log5ac6ff43d41f23d7d215c3164848bb4ffcf00d59
tree343cdf22de8f22b60ceab745a4eea15bd0c09005
parent6a9510c0eb85434f75730f512f4d9e4bed5cfcb3
parent6b733537abec526a878c3fd2f62d7ec2386ded56

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>

1 files changed, 47 insertions(+), 4 deletions(-)

lib/std/Io.zig+47-4
......@@ -1206,7 +1206,7 @@ pub fn Select(comptime U: type) type {
12061206 /// already been called and completed, or it has successfully been
12071207 /// assigned a unit of concurrency.
12081208 ///
1209 /// After this is called, `wait` or `cancel` must be called before the
1209 /// After this is called, `await` or `cancel` must be called before the
12101210 /// select is deinitialized.
12111211 ///
12121212 /// Threadsafe.
......@@ -1225,10 +1225,13 @@ pub fn Select(comptime U: type) type {
12251225 args: @TypeOf(args),
12261226 fn start(type_erased_context: *const anyopaque) Cancelable!void {
12271227 const context: *const @This() = @ptrCast(@alignCast(type_erased_context));
1228 const elem = @unionInit(U, @tagName(field), @call(.auto, function, context.args));
1228 const raw_result = @call(.auto, function, context.args);
1229 const elem = @unionInit(U, @tagName(field), raw_result);
12291230 context.select.queue.putOneUncancelable(context.select.io, elem) catch |err| switch (err) {
12301231 error.Closed => unreachable,
12311232 };
1233 if (@typeInfo(@TypeOf(raw_result)) == .error_union)
1234 raw_result catch |err| if (err == error.Canceled) return error.Canceled;
12321235 }
12331236 };
12341237 const context: Context = .{ .select = s, .args = args };
......@@ -1236,6 +1239,46 @@ pub fn Select(comptime U: type) type {
12361239 s.io.vtable.groupAsync(s.io.userdata, &s.group, @ptrCast(&context), .of(Context), Context.start);
12371240 }
12381241
1242 /// Calls `function` with `args` concurrently. The resource spawned is
1243 /// owned by the select.
1244 ///
1245 /// `function` must have return type matching the `field` field of `Union`.
1246 ///
1247 /// After this function returns successfully, it is guaranteed that
1248 /// `function` has been assigned a unit of concurrency, and `await` or
1249 /// `cancel` must be called before the select is deinitialized.
1250 ///
1251 ///
1252 /// Threadsafe.
1253 ///
1254 /// Related:
1255 /// * `Io.concurrent`
1256 /// * `Group.concurrent`
1257 pub fn concurrent(
1258 s: *S,
1259 comptime field: Field,
1260 function: anytype,
1261 args: std.meta.ArgsTuple(@TypeOf(function)),
1262 ) ConcurrentError!void {
1263 const Context = struct {
1264 select: *S,
1265 args: @TypeOf(args),
1266 fn start(type_erased_context: *const anyopaque) Cancelable!void {
1267 const context: *const @This() = @ptrCast(@alignCast(type_erased_context));
1268 const raw_result = @call(.auto, function, context.args);
1269 const elem = @unionInit(U, @tagName(field), raw_result);
1270 context.select.queue.putOneUncancelable(context.select.io, elem) catch |err| switch (err) {
1271 error.Closed => unreachable,
1272 };
1273 if (@typeInfo(@TypeOf(raw_result)) == .error_union)
1274 raw_result catch |err| if (err == error.Canceled) return error.Canceled;
1275 }
1276 };
1277 const context: Context = .{ .select = s, .args = args };
1278 try s.io.vtable.groupConcurrent(s.io.userdata, &s.group, @ptrCast(&context), .of(Context), Context.start);
1279 _ = @atomicRmw(usize, &s.outstanding, .Add, 1, .monotonic);
1280 }
1281
12391282 /// Blocks until another task of the select finishes.
12401283 ///
12411284 /// Asserts there is at least one more `outstanding` task.
......@@ -1249,12 +1292,12 @@ pub fn Select(comptime U: type) type {
12491292 };
12501293 }
12511294
1252 /// Equivalent to `wait` but requests cancelation on all remaining
1295 /// Equivalent to `await` but requests cancelation on all remaining
12531296 /// tasks owned by the select.
12541297 ///
12551298 /// For a description of cancelation and cancelation points, see `Future.cancel`.
12561299 ///
1257 /// It is illegal to call `wait` after this.
1300 /// It is illegal to call `await` after this.
12581301 ///
12591302 /// Idempotent. Not threadsafe.
12601303 pub fn cancel(s: *S) void {