| ... | @@ -1183,10 +1183,12 @@ fn groupConcurrent( | ... | @@ -1183,10 +1183,12 @@ fn groupConcurrent( |
| 1183 | t.cond.signal(); | 1183 | t.cond.signal(); |
| 1184 | } | 1184 | } |
| 1185 | | 1185 | |
| 1186 | fn groupWait(userdata: ?*anyopaque, group: *Io.Group, token: *anyopaque) void { | 1186 | fn groupWait(userdata: ?*anyopaque, group: *Io.Group, initial_token: *anyopaque) void { |
| 1187 | const t: *Threaded = @ptrCast(@alignCast(userdata)); | 1187 | const t: *Threaded = @ptrCast(@alignCast(userdata)); |
| 1188 | const gpa = t.allocator; | 1188 | const gpa = t.allocator; |
| 1189 | | 1189 | |
| | 1190 | _ = initial_token; // we need to load `token` *after* the group finishes |
| | 1191 | |
| 1190 | if (builtin.single_threaded) unreachable; // we never set `group.token` to non-`null` | 1192 | if (builtin.single_threaded) unreachable; // we never set `group.token` to non-`null` |
| 1191 | | 1193 | |
| 1192 | const group_state: *std.atomic.Value(usize) = @ptrCast(&group.state); | 1194 | const group_state: *std.atomic.Value(usize) = @ptrCast(&group.state); |
| ... | @@ -1195,42 +1197,40 @@ fn groupWait(userdata: ?*anyopaque, group: *Io.Group, token: *anyopaque) void { | ... | @@ -1195,42 +1197,40 @@ fn groupWait(userdata: ?*anyopaque, group: *Io.Group, token: *anyopaque) void { |
| 1195 | assert(prev_state & GroupClosure.sync_is_waiting == 0); | 1197 | assert(prev_state & GroupClosure.sync_is_waiting == 0); |
| 1196 | if ((prev_state / GroupClosure.sync_one_pending) > 0) event.wait(ioBasic(t)) catch |err| switch (err) { | 1198 | if ((prev_state / GroupClosure.sync_one_pending) > 0) event.wait(ioBasic(t)) catch |err| switch (err) { |
| 1197 | error.Canceled => { | 1199 | error.Canceled => { |
| 1198 | var node: *std.SinglyLinkedList.Node = @ptrCast(@alignCast(token)); | 1200 | var it: ?*std.SinglyLinkedList.Node = @ptrCast(@alignCast(group.token.load(.monotonic))); |
| 1199 | while (true) { | 1201 | while (it) |node| : (it = node.next) { |
| 1200 | const gc: *GroupClosure = @fieldParentPtr("node", node); | 1202 | const gc: *GroupClosure = @fieldParentPtr("node", node); |
| 1201 | gc.closure.requestCancel(t); | 1203 | gc.closure.requestCancel(t); |
| 1202 | node = node.next orelse break; | | |
| 1203 | } | 1204 | } |
| 1204 | event.waitUncancelable(ioBasic(t)); | 1205 | event.waitUncancelable(ioBasic(t)); |
| 1205 | }, | 1206 | }, |
| 1206 | }; | 1207 | }; |
| 1207 | | 1208 | |
| 1208 | var node: *std.SinglyLinkedList.Node = @ptrCast(@alignCast(token)); | | |
| 1209 | while (true) { | | |
| 1210 | const gc: *GroupClosure = @fieldParentPtr("node", node); | | |
| 1211 | const node_next = node.next; | | |
| 1212 | gc.deinit(gpa); | | |
| 1213 | node = node_next orelse break; | | |
| 1214 | } | | |
| 1215 | | | |
| 1216 | // Since the group has now finished, it's illegal to add more tasks to it until we return. It's | 1209 | // Since the group has now finished, it's illegal to add more tasks to it until we return. It's |
| 1217 | // also illegal for us to race with another `await` or `cancel`. Therefore, we must be the only | 1210 | // also illegal for us to race with another `await` or `cancel`. Therefore, we must be the only |
| 1218 | // thread who can access `group` right now. | 1211 | // thread who can access `group` right now. |
| | 1212 | var it: ?*std.SinglyLinkedList.Node = @ptrCast(@alignCast(group.token.raw)); |
| 1219 | group.token.raw = null; | 1213 | group.token.raw = null; |
| | 1214 | while (it) |node| { |
| | 1215 | it = node.next; // update `it` now, because `deinit` will invalidate `node` |
| | 1216 | const gc: *GroupClosure = @fieldParentPtr("node", node); |
| | 1217 | gc.deinit(gpa); |
| | 1218 | } |
| 1220 | } | 1219 | } |
| 1221 | | 1220 | |
| 1222 | fn groupCancel(userdata: ?*anyopaque, group: *Io.Group, token: *anyopaque) void { | 1221 | fn groupCancel(userdata: ?*anyopaque, group: *Io.Group, initial_token: *anyopaque) void { |
| 1223 | const t: *Threaded = @ptrCast(@alignCast(userdata)); | 1222 | const t: *Threaded = @ptrCast(@alignCast(userdata)); |
| 1224 | const gpa = t.allocator; | 1223 | const gpa = t.allocator; |
| 1225 | | 1224 | |
| | 1225 | _ = initial_token; // we need to load `token` *after* the group finishes |
| | 1226 | |
| 1226 | if (builtin.single_threaded) unreachable; // we never set `group.token` to non-`null` | 1227 | if (builtin.single_threaded) unreachable; // we never set `group.token` to non-`null` |
| 1227 | | 1228 | |
| 1228 | { | 1229 | { |
| 1229 | var node: *std.SinglyLinkedList.Node = @ptrCast(@alignCast(token)); | 1230 | var it: ?*std.SinglyLinkedList.Node = @ptrCast(@alignCast(group.token.load(.monotonic))); |
| 1230 | while (true) { | 1231 | while (it) |node| : (it = node.next) { |
| 1231 | const gc: *GroupClosure = @fieldParentPtr("node", node); | 1232 | const gc: *GroupClosure = @fieldParentPtr("node", node); |
| 1232 | gc.closure.requestCancel(t); | 1233 | gc.closure.requestCancel(t); |
| 1233 | node = node.next orelse break; | | |
| 1234 | } | 1234 | } |
| 1235 | } | 1235 | } |
| 1236 | | 1236 | |
| ... | @@ -1240,20 +1240,16 @@ fn groupCancel(userdata: ?*anyopaque, group: *Io.Group, token: *anyopaque) void | ... | @@ -1240,20 +1240,16 @@ fn groupCancel(userdata: ?*anyopaque, group: *Io.Group, token: *anyopaque) void |
| 1240 | assert(prev_state & GroupClosure.sync_is_waiting == 0); | 1240 | assert(prev_state & GroupClosure.sync_is_waiting == 0); |
| 1241 | if ((prev_state / GroupClosure.sync_one_pending) > 0) event.waitUncancelable(ioBasic(t)); | 1241 | if ((prev_state / GroupClosure.sync_one_pending) > 0) event.waitUncancelable(ioBasic(t)); |
| 1242 | | 1242 | |
| 1243 | { | | |
| 1244 | var node: *std.SinglyLinkedList.Node = @ptrCast(@alignCast(token)); | | |
| 1245 | while (true) { | | |
| 1246 | const gc: *GroupClosure = @fieldParentPtr("node", node); | | |
| 1247 | const node_next = node.next; | | |
| 1248 | gc.deinit(gpa); | | |
| 1249 | node = node_next orelse break; | | |
| 1250 | } | | |
| 1251 | } | | |
| 1252 | | | |
| 1253 | // Since the group has now finished, it's illegal to add more tasks to it until we return. It's | 1243 | // Since the group has now finished, it's illegal to add more tasks to it until we return. It's |
| 1254 | // also illegal for us to race with another `await` or `cancel`. Therefore, we must be the only | 1244 | // also illegal for us to race with another `await` or `cancel`. Therefore, we must be the only |
| 1255 | // thread who can access `group` right now. | 1245 | // thread who can access `group` right now. |
| | 1246 | var it: ?*std.SinglyLinkedList.Node = @ptrCast(@alignCast(group.token.raw)); |
| 1256 | group.token.raw = null; | 1247 | group.token.raw = null; |
| | 1248 | while (it) |node| { |
| | 1249 | it = node.next; // update `it` now, because `deinit` will invalidate `node` |
| | 1250 | const gc: *GroupClosure = @fieldParentPtr("node", node); |
| | 1251 | gc.deinit(gpa); |
| | 1252 | } |
| 1257 | } | 1253 | } |
| 1258 | | 1254 | |
| 1259 | fn recancel(userdata: ?*anyopaque) void { | 1255 | fn recancel(userdata: ?*anyopaque) void { |