@@ -626,8 +626,9 @@ pub const VTable = struct {
626626 /// Thread-safe.
627627 cancelRequested : * const fn (? * anyopaque ) bool ,
628628
629- /// Executes `start` asynchronously in a manner such that it cleans itself
630- /// up. This mode does not support results, await, or cancel.
629+ /// When this function returns, implementation guarantees that `start` has
630+ /// either already been called, or a unit of concurrency has been assigned
631+ /// to the task of calling the function.
631632 ///
632633 /// Thread-safe.
633634 groupAsync : * const fn (
@@ -640,6 +641,17 @@ pub const VTable = struct {
640641 context_alignment : std.mem.Alignment ,
641642 start : * const fn (* Group , context : * const anyopaque ) void ,
642643 ) void ,
644+ /// Thread-safe.
645+ groupConcurrent : * const fn (
646+ /// Corresponds to `Io.userdata`.
647+ userdata : ? * anyopaque ,
648+ /// Owner of the spawned async task.
649+ group : * Group ,
650+ /// Copied and then passed to `start`.
651+ context : []const u8 ,
652+ context_alignment : std.mem.Alignment ,
653+ start : * const fn (* Group , context : * const anyopaque ) void ,
654+ ) ConcurrentError ! void ,
643655 groupWait : * const fn (? * anyopaque , * Group , token : * anyopaque ) void ,
644656 groupCancel : * const fn (? * anyopaque , * Group , token : * anyopaque ) void ,
645657
@@ -1021,8 +1033,8 @@ pub const Group = struct {
10211033 /// Threadsafe.
10221034 ///
10231035 /// See also:
1024- /// * `Io.async`
10251036 /// * `concurrent`
1037+ /// * `Io.async`
10261038 pub fn async (g : * Group , io : Io , function : anytype , args : std .meta .ArgsTuple (@TypeOf (function ))) void {
10271039 const Args = @TypeOf (args );
10281040 const TypeErased = struct {
@@ -1035,6 +1047,34 @@ pub const Group = struct {
10351047 io .vtable .groupAsync (io .userdata , g , @ptrCast (& args ), .of (Args ), TypeErased .start );
10361048 }
10371049
1050+ /// Calls `function` with `args`, such that the function is not guaranteed
1051+ /// to have returned until `wait` is called, allowing the caller to
1052+ /// progress while waiting for any `Io` operations.
1053+ ///
1054+ /// The resource spawned is owned by the group; after this is called,
1055+ /// `wait` or `cancel` must be called before the group is deinitialized.
1056+ ///
1057+ /// This has stronger guarantee than `async`, placing restrictions on what kind
1058+ /// of `Io` implementations are supported. By calling `async` instead, one
1059+ /// allows, for example, stackful single-threaded blocking I/O.
1060+ ///
1061+ /// Threadsafe.
1062+ ///
1063+ /// See also:
1064+ /// * `async`
1065+ /// * `Io.concurrent`
1066+ pub fn concurrent (g : * Group , io : Io , function : anytype , args : std .meta .ArgsTuple (@TypeOf (function ))) ConcurrentError ! void {
1067+ const Args = @TypeOf (args );
1068+ const TypeErased = struct {
1069+ fn start (group : * Group , context : * const anyopaque ) void {
1070+ _ = group ;
1071+ const args_casted : * const Args = @ptrCast (@alignCast (context ));
1072+ @call (.auto , function , args_casted .* );
1073+ }
1074+ };
1075+ return io .vtable .groupConcurrent (io .userdata , g , @ptrCast (& args ), .of (Args ), TypeErased .start );
1076+ }
1077+
10381078 /// Blocks until all tasks of the group finish. During this time,
10391079 /// cancellation requests propagate to all members of the group.
10401080 ///
0 commit comments