| ... | ... | @@ -631,7 +631,7 @@ pub const VTable = struct { |
| 631 | 631 | /// Copied and then passed to `start`. |
| 632 | 632 | context: []const u8, |
| 633 | 633 | context_alignment: std.mem.Alignment, |
| 634 | | start: *const fn (*Group, context: *const anyopaque) Cancelable!void, |
| 634 | start: *const fn (context: *const anyopaque) Cancelable!void, |
| 635 | 635 | ) void, |
| 636 | 636 | /// Thread-safe. |
| 637 | 637 | groupConcurrent: *const fn ( |
| ... | ... | @@ -642,7 +642,7 @@ pub const VTable = struct { |
| 642 | 642 | /// Copied and then passed to `start`. |
| 643 | 643 | context: []const u8, |
| 644 | 644 | context_alignment: std.mem.Alignment, |
| 645 | | start: *const fn (*Group, context: *const anyopaque) Cancelable!void, |
| 645 | start: *const fn (context: *const anyopaque) Cancelable!void, |
| 646 | 646 | ) ConcurrentError!void, |
| 647 | 647 | groupAwait: *const fn (?*anyopaque, *Group, token: *anyopaque) Cancelable!void, |
| 648 | 648 | groupCancel: *const fn (?*anyopaque, *Group, token: *anyopaque) void, |
| ... | ... | @@ -1050,40 +1050,40 @@ pub fn Future(Result: type) type { |
| 1050 | 1050 | }; |
| 1051 | 1051 | } |
| 1052 | 1052 | |
| 1053 | /// An unordered set of tasks which can only be awaited or canceled as a whole. |
| 1054 | /// Tasks are spawned in the group with `Group.async` and `Group.concurrent`. |
| 1055 | /// |
| 1056 | /// The resources associated with each task are *guaranteed* to be released when |
| 1057 | /// the individual task returns, as opposed to when the whole group completes or |
| 1058 | /// is awaited. For this reason, it is not a resource leak to have a long-lived |
| 1059 | /// group which concurrent tasks are repeatedly added to. However, asynchronous |
| 1060 | /// tasks are not guaranteed to run until `Group.await` or `Group.cancel` is |
| 1061 | /// called, so adding async tasks to a group without ever awaiting it may leak |
| 1062 | /// resources. |
| 1053 | 1063 | pub const Group = struct { |
| 1054 | | state: usize, |
| 1055 | | context: ?*anyopaque, |
| 1056 | 1064 | /// This value indicates whether or not a group has pending tasks. `null` |
| 1057 | 1065 | /// means there are no pending tasks, and no resources associated with the |
| 1058 | 1066 | /// group, so `await` and `cancel` return immediately without calling the |
| 1059 | 1067 | /// implementation. This means that `token` must be accessed atomically to |
| 1060 | 1068 | /// avoid racing with the check in `await` and `cancel`. |
| 1061 | 1069 | token: std.atomic.Value(?*anyopaque), |
| 1070 | /// This value is available for the implementation to use as it wishes. |
| 1071 | state: usize, |
| 1062 | 1072 | |
| 1063 | | pub const init: Group = .{ .state = 0, .context = null, .token = .init(null) }; |
| 1073 | pub const init: Group = .{ .token = .init(null), .state = 0 }; |
| 1064 | 1074 | |
| 1065 | | /// Calls `function` with `args` asynchronously. The resource spawned is |
| 1066 | | /// owned by the group. |
| 1067 | | /// |
| 1068 | | /// `function` *may* be called immediately, before `async` returns. |
| 1075 | /// Equivalent to `Io.async`, except the task is spawned in this `Group` |
| 1076 | /// instead of becoming associated with a `Future`. |
| 1069 | 1077 | /// |
| 1070 | | /// When this function returns, it is guaranteed that `function` has |
| 1071 | | /// already been called and completed, or it has successfully been assigned |
| 1072 | | /// a unit of concurrency. |
| 1078 | /// The return type of `function` must be coercible to `Cancelable!void`. |
| 1073 | 1079 | /// |
| 1074 | | /// After this is called, `await` or `cancel` must be called before the |
| 1075 | | /// group is deinitialized. |
| 1076 | | /// |
| 1077 | | /// Threadsafe. |
| 1078 | | /// |
| 1079 | | /// See also: |
| 1080 | | /// * `concurrent` |
| 1081 | | /// * `Io.async` |
| 1080 | /// Once this function is called, there are resources associated with the |
| 1081 | /// group. To release those resources, `Group.await` or `Group.cancel` must |
| 1082 | /// eventually be called. |
| 1082 | 1083 | pub fn async(g: *Group, io: Io, function: anytype, args: std.meta.ArgsTuple(@TypeOf(function))) void { |
| 1083 | 1084 | const Args = @TypeOf(args); |
| 1084 | 1085 | const TypeErased = struct { |
| 1085 | | fn start(group: *Group, context: *const anyopaque) Cancelable!void { |
| 1086 | | _ = group; |
| 1086 | fn start(context: *const anyopaque) Cancelable!void { |
| 1087 | 1087 | const args_casted: *const Args = @ptrCast(@alignCast(context)); |
| 1088 | 1088 | return @call(.auto, function, args_casted.*); |
| 1089 | 1089 | } |
| ... | ... | @@ -1091,27 +1091,18 @@ pub const Group = struct { |
| 1091 | 1091 | io.vtable.groupAsync(io.userdata, g, @ptrCast(&args), .of(Args), TypeErased.start); |
| 1092 | 1092 | } |
| 1093 | 1093 | |
| 1094 | | /// Calls `function` with `args`, such that the function is not guaranteed |
| 1095 | | /// to have returned until `await` is called, allowing the caller to |
| 1096 | | /// progress while waiting for any `Io` operations. |
| 1097 | | /// |
| 1098 | | /// The resource spawned is owned by the group; after this is called, |
| 1099 | | /// `await` or `cancel` must be called before the group is deinitialized. |
| 1100 | | /// |
| 1101 | | /// This has stronger guarantee than `async`, placing restrictions on what kind |
| 1102 | | /// of `Io` implementations are supported. By calling `async` instead, one |
| 1103 | | /// allows, for example, stackful single-threaded blocking I/O. |
| 1094 | /// Equivalent to `Io.concurrent`, except the task is spawned in this |
| 1095 | /// `Group` instead of becoming associated with a `Future`. |
| 1104 | 1096 | /// |
| 1105 | | /// Threadsafe. |
| 1097 | /// The return type of `function` must be coercible to `Cancelable!void`. |
| 1106 | 1098 | /// |
| 1107 | | /// See also: |
| 1108 | | /// * `async` |
| 1109 | | /// * `Io.concurrent` |
| 1099 | /// Once this function is called, there are resources associated with the |
| 1100 | /// group. To release those resources, `Group.await` or `Group.cancel` must |
| 1101 | /// eventually be called. |
| 1110 | 1102 | pub fn concurrent(g: *Group, io: Io, function: anytype, args: std.meta.ArgsTuple(@TypeOf(function))) ConcurrentError!void { |
| 1111 | 1103 | const Args = @TypeOf(args); |
| 1112 | 1104 | const TypeErased = struct { |
| 1113 | | fn start(group: *Group, context: *const anyopaque) Cancelable!void { |
| 1114 | | _ = group; |
| 1105 | fn start(context: *const anyopaque) Cancelable!void { |
| 1115 | 1106 | const args_casted: *const Args = @ptrCast(@alignCast(context)); |
| 1116 | 1107 | return @call(.auto, function, args_casted.*); |
| 1117 | 1108 | } |
| ... | ... | @@ -1263,19 +1254,20 @@ pub fn Select(comptime U: type) type { |
| 1263 | 1254 | function: anytype, |
| 1264 | 1255 | args: std.meta.ArgsTuple(@TypeOf(function)), |
| 1265 | 1256 | ) void { |
| 1266 | | const Args = @TypeOf(args); |
| 1267 | | const TypeErased = struct { |
| 1268 | | fn start(group: *Group, context: *const anyopaque) Cancelable!void { |
| 1269 | | const args_casted: *const Args = @ptrCast(@alignCast(context)); |
| 1270 | | const unerased_select: *S = @fieldParentPtr("group", group); |
| 1271 | | const elem = @unionInit(U, @tagName(field), @call(.auto, function, args_casted.*)); |
| 1272 | | unerased_select.queue.putOneUncancelable(unerased_select.io, elem) catch |err| switch (err) { |
| 1257 | const Context = struct { |
| 1258 | select: *S, |
| 1259 | args: @TypeOf(args), |
| 1260 | fn start(type_erased_context: *const anyopaque) Cancelable!void { |
| 1261 | const context: *const @This() = @ptrCast(@alignCast(type_erased_context)); |
| 1262 | const elem = @unionInit(U, @tagName(field), @call(.auto, function, context.args)); |
| 1263 | context.select.queue.putOneUncancelable(context.select.io, elem) catch |err| switch (err) { |
| 1273 | 1264 | error.Closed => unreachable, |
| 1274 | 1265 | }; |
| 1275 | 1266 | } |
| 1276 | 1267 | }; |
| 1268 | const context: Context = .{ .select = s, .args = args }; |
| 1277 | 1269 | _ = @atomicRmw(usize, &s.outstanding, .Add, 1, .monotonic); |
| 1278 | | s.io.vtable.groupAsync(s.io.userdata, &s.group, @ptrCast(&args), .of(Args), TypeErased.start); |
| 1270 | s.io.vtable.groupAsync(s.io.userdata, &s.group, @ptrCast(&context), .of(Context), Context.start); |
| 1279 | 1271 | } |
| 1280 | 1272 | |
| 1281 | 1273 | /// Blocks until another task of the select finishes. |