| author | |
| committer | |
| log | 7b5886118dabb59967d3e9b17d0502146df2ef92 |
| tree | 8594534f260e37114700ebe6fb2d1f92e15fafa8 |
| parent | fdf15fae9779c1b0fc05b12abf0865245dd95d4f |
3 files changed, 78 insertions(+), 24 deletions(-)
lib/std/Io.zig+18-7| ... | @@ -983,7 +983,7 @@ pub const VTable = struct { | ... | @@ -983,7 +983,7 @@ pub const VTable = struct { |
| 983 | mutexUnlock: *const fn (?*anyopaque, prev_state: Mutex.State, mutex: *Mutex) void, | 983 | mutexUnlock: *const fn (?*anyopaque, prev_state: Mutex.State, mutex: *Mutex) void, |
| 984 | 984 | ||
| 985 | conditionWait: *const fn (?*anyopaque, cond: *Condition, mutex: *Mutex) Cancelable!void, | 985 | conditionWait: *const fn (?*anyopaque, cond: *Condition, mutex: *Mutex) Cancelable!void, |
| 986 | conditionWake: *const fn (?*anyopaque, cond: *Condition) void, | 986 | conditionWake: *const fn (?*anyopaque, cond: *Condition, wake: Condition.Wake) void, |
| 987 | 987 | ||
| 988 | createFile: *const fn (?*anyopaque, dir: fs.Dir, sub_path: []const u8, flags: fs.File.CreateFlags) FileOpenError!fs.File, | 988 | createFile: *const fn (?*anyopaque, dir: fs.Dir, sub_path: []const u8, flags: fs.File.CreateFlags) FileOpenError!fs.File, |
| 989 | openFile: *const fn (?*anyopaque, dir: fs.Dir, sub_path: []const u8, flags: fs.File.OpenFlags) FileOpenError!fs.File, | 989 | openFile: *const fn (?*anyopaque, dir: fs.Dir, sub_path: []const u8, flags: fs.File.OpenFlags) FileOpenError!fs.File, |
| ... | @@ -1162,9 +1162,20 @@ pub const Condition = struct { | ... | @@ -1162,9 +1162,20 @@ pub const Condition = struct { |
| 1162 | return io.vtable.conditionWait(io.userdata, cond, mutex); | 1162 | return io.vtable.conditionWait(io.userdata, cond, mutex); |
| 1163 | } | 1163 | } |
| 1164 | 1164 | ||
| 1165 | pub fn wake(cond: *Condition, io: Io) void { | 1165 | pub fn signal(cond: *Condition, io: Io) void { |
| 1166 | io.vtable.conditionWake(io.userdata, cond); | 1166 | io.vtable.conditionWake(io.userdata, cond, .one); |
| 1167 | } | 1167 | } |
| 1168 | |||
| 1169 | pub fn broadcast(cond: *Condition, io: Io) void { | ||
| 1170 | io.vtable.conditionWake(io.userdata, cond, .all); | ||
| 1171 | } | ||
| 1172 | |||
| 1173 | pub const Wake = enum { | ||
| 1174 | /// wake up only one thread | ||
| 1175 | one, | ||
| 1176 | /// wake up all thread | ||
| 1177 | all, | ||
| 1178 | }; | ||
| 1168 | }; | 1179 | }; |
| 1169 | 1180 | ||
| 1170 | pub const TypeErasedQueue = struct { | 1181 | pub const TypeErasedQueue = struct { |
| ... | @@ -1216,7 +1227,7 @@ pub const TypeErasedQueue = struct { | ... | @@ -1216,7 +1227,7 @@ pub const TypeErasedQueue = struct { |
| 1216 | remaining = remaining[copy_len..]; | 1227 | remaining = remaining[copy_len..]; |
| 1217 | getter.data.remaining = getter.data.remaining[copy_len..]; | 1228 | getter.data.remaining = getter.data.remaining[copy_len..]; |
| 1218 | if (getter.data.remaining.len == 0) { | 1229 | if (getter.data.remaining.len == 0) { |
| 1219 | getter.data.condition.wake(io); | 1230 | getter.data.condition.signal(io); |
| 1220 | continue; | 1231 | continue; |
| 1221 | } | 1232 | } |
| 1222 | q.getters.prepend(getter); | 1233 | q.getters.prepend(getter); |
| ... | @@ -1299,7 +1310,7 @@ pub const TypeErasedQueue = struct { | ... | @@ -1299,7 +1310,7 @@ pub const TypeErasedQueue = struct { |
| 1299 | putter.data.remaining = putter.data.remaining[copy_len..]; | 1310 | putter.data.remaining = putter.data.remaining[copy_len..]; |
| 1300 | remaining = remaining[copy_len..]; | 1311 | remaining = remaining[copy_len..]; |
| 1301 | if (putter.data.remaining.len == 0) { | 1312 | if (putter.data.remaining.len == 0) { |
| 1302 | putter.data.condition.wake(io); | 1313 | putter.data.condition.signal(io); |
| 1303 | } else { | 1314 | } else { |
| 1304 | assert(remaining.len == 0); | 1315 | assert(remaining.len == 0); |
| 1305 | q.putters.prepend(putter); | 1316 | q.putters.prepend(putter); |
| ... | @@ -1332,7 +1343,7 @@ pub const TypeErasedQueue = struct { | ... | @@ -1332,7 +1343,7 @@ pub const TypeErasedQueue = struct { |
| 1332 | putter.data.remaining = putter.data.remaining[copy_len..]; | 1343 | putter.data.remaining = putter.data.remaining[copy_len..]; |
| 1333 | q.put_index += copy_len; | 1344 | q.put_index += copy_len; |
| 1334 | if (putter.data.remaining.len == 0) { | 1345 | if (putter.data.remaining.len == 0) { |
| 1335 | putter.data.condition.wake(io); | 1346 | putter.data.condition.signal(io); |
| 1336 | continue; | 1347 | continue; |
| 1337 | } | 1348 | } |
| 1338 | const second_available = q.buffer[0..q.get_index]; | 1349 | const second_available = q.buffer[0..q.get_index]; |
| ... | @@ -1341,7 +1352,7 @@ pub const TypeErasedQueue = struct { | ... | @@ -1341,7 +1352,7 @@ pub const TypeErasedQueue = struct { |
| 1341 | putter.data.remaining = putter.data.remaining[copy_len..]; | 1352 | putter.data.remaining = putter.data.remaining[copy_len..]; |
| 1342 | q.put_index = copy_len; | 1353 | q.put_index = copy_len; |
| 1343 | if (putter.data.remaining.len == 0) { | 1354 | if (putter.data.remaining.len == 0) { |
| 1344 | putter.data.condition.wake(io); | 1355 | putter.data.condition.signal(io); |
| 1345 | continue; | 1356 | continue; |
| 1346 | } | 1357 | } |
| 1347 | q.putters.prepend(putter); | 1358 | q.putters.prepend(putter); |
lib/std/Io/EventLoop.zig+55-15| ... | @@ -555,8 +555,24 @@ const SwitchMessage = struct { | ... | @@ -555,8 +555,24 @@ const SwitchMessage = struct { |
| 555 | .condition_wait => |condition_wait| { | 555 | .condition_wait => |condition_wait| { |
| 556 | const prev_fiber: *Fiber = @alignCast(@fieldParentPtr("context", message.contexts.prev)); | 556 | const prev_fiber: *Fiber = @alignCast(@fieldParentPtr("context", message.contexts.prev)); |
| 557 | assert(prev_fiber.queue_next == null); | 557 | assert(prev_fiber.queue_next == null); |
| 558 | const cond_state: *?*Fiber = @ptrCast(&condition_wait.cond.state); | 558 | const cond_impl = prev_fiber.resultPointer(ConditionImpl); |
| 559 | assert(@atomicRmw(?*Fiber, cond_state, .Xchg, prev_fiber, .release) == null); // More than one wait on same Condition is illegal. | 559 | cond_impl.* = .{ |
| 560 | .tail = prev_fiber, | ||
| 561 | .event = .queued, | ||
| 562 | }; | ||
| 563 | if (@cmpxchgStrong( | ||
| 564 | ?*Fiber, | ||
| 565 | @as(*?*Fiber, @ptrCast(&condition_wait.cond.state)), | ||
| 566 | null, | ||
| 567 | prev_fiber, | ||
| 568 | .release, | ||
| 569 | .acquire, | ||
| 570 | )) |waiting_fiber| { | ||
| 571 | const waiting_cond_impl = waiting_fiber.?.resultPointer(ConditionImpl); | ||
| 572 | assert(waiting_cond_impl.tail.queue_next == null); | ||
| 573 | waiting_cond_impl.tail.queue_next = prev_fiber; | ||
| 574 | waiting_cond_impl.tail = prev_fiber; | ||
| 575 | } | ||
| 560 | condition_wait.mutex.unlock(el.io()); | 576 | condition_wait.mutex.unlock(el.io()); |
| 561 | }, | 577 | }, |
| 562 | .exit => for (el.threads.allocated[0..@atomicLoad(u32, &el.threads.active, .acquire)]) |*each_thread| { | 578 | .exit => for (el.threads.allocated[0..@atomicLoad(u32, &el.threads.active, .acquire)]) |*each_thread| { |
| ... | @@ -1267,10 +1283,7 @@ fn sleep(userdata: ?*anyopaque, clockid: std.posix.clockid_t, deadline: Io.Deadl | ... | @@ -1267,10 +1283,7 @@ fn sleep(userdata: ?*anyopaque, clockid: std.posix.clockid_t, deadline: Io.Deadl |
| 1267 | 1283 | ||
| 1268 | fn mutexLock(userdata: ?*anyopaque, prev_state: Io.Mutex.State, mutex: *Io.Mutex) error{Canceled}!void { | 1284 | fn mutexLock(userdata: ?*anyopaque, prev_state: Io.Mutex.State, mutex: *Io.Mutex) error{Canceled}!void { |
| 1269 | const el: *EventLoop = @alignCast(@ptrCast(userdata)); | 1285 | const el: *EventLoop = @alignCast(@ptrCast(userdata)); |
| 1270 | el.yield(null, .{ .mutex_lock = .{ | 1286 | el.yield(null, .{ .mutex_lock = .{ .prev_state = prev_state, .mutex = mutex } }); |
| 1271 | .prev_state = prev_state, | ||
| 1272 | .mutex = mutex, | ||
| 1273 | } }); | ||
| 1274 | } | 1287 | } |
| 1275 | fn mutexUnlock(userdata: ?*anyopaque, prev_state: Io.Mutex.State, mutex: *Io.Mutex) void { | 1288 | fn mutexUnlock(userdata: ?*anyopaque, prev_state: Io.Mutex.State, mutex: *Io.Mutex) void { |
| 1276 | var maybe_waiting_fiber: ?*Fiber = @ptrFromInt(@intFromEnum(prev_state)); | 1289 | var maybe_waiting_fiber: ?*Fiber = @ptrFromInt(@intFromEnum(prev_state)); |
| ... | @@ -1294,21 +1307,48 @@ fn mutexUnlock(userdata: ?*anyopaque, prev_state: Io.Mutex.State, mutex: *Io.Mut | ... | @@ -1294,21 +1307,48 @@ fn mutexUnlock(userdata: ?*anyopaque, prev_state: Io.Mutex.State, mutex: *Io.Mut |
| 1294 | el.yield(maybe_waiting_fiber.?, .reschedule); | 1307 | el.yield(maybe_waiting_fiber.?, .reschedule); |
| 1295 | } | 1308 | } |
| 1296 | 1309 | ||
| 1310 | const ConditionImpl = struct { | ||
| 1311 | tail: *Fiber, | ||
| 1312 | event: union(enum) { | ||
| 1313 | queued, | ||
| 1314 | wake: Io.Condition.Wake, | ||
| 1315 | }, | ||
| 1316 | }; | ||
| 1317 | |||
| 1297 | fn conditionWait(userdata: ?*anyopaque, cond: *Io.Condition, mutex: *Io.Mutex) Io.Cancelable!void { | 1318 | fn conditionWait(userdata: ?*anyopaque, cond: *Io.Condition, mutex: *Io.Mutex) Io.Cancelable!void { |
| 1298 | const el: *EventLoop = @alignCast(@ptrCast(userdata)); | 1319 | const el: *EventLoop = @alignCast(@ptrCast(userdata)); |
| 1299 | el.yield(null, .{ .condition_wait = .{ | 1320 | el.yield(null, .{ .condition_wait = .{ .cond = cond, .mutex = mutex } }); |
| 1300 | .cond = cond, | 1321 | const thread = Thread.current(); |
| 1301 | .mutex = mutex, | 1322 | const fiber = thread.currentFiber(); |
| 1302 | } }); | 1323 | const cond_impl = fiber.resultPointer(ConditionImpl); |
| 1303 | try mutex.lock(el.io()); | 1324 | try mutex.lock(el.io()); |
| 1325 | switch (cond_impl.event) { | ||
| 1326 | .queued => {}, | ||
| 1327 | .wake => |wake| if (fiber.queue_next) |next_fiber| switch (wake) { | ||
| 1328 | .one => if (@cmpxchgStrong( | ||
| 1329 | ?*Fiber, | ||
| 1330 | @as(*?*Fiber, @ptrCast(&cond.state)), | ||
| 1331 | null, | ||
| 1332 | next_fiber, | ||
| 1333 | .release, | ||
| 1334 | .acquire, | ||
| 1335 | )) |old_fiber| { | ||
| 1336 | const old_cond_impl = old_fiber.?.resultPointer(ConditionImpl); | ||
| 1337 | assert(old_cond_impl.tail.queue_next == null); | ||
| 1338 | old_cond_impl.tail.queue_next = next_fiber; | ||
| 1339 | old_cond_impl.tail = cond_impl.tail; | ||
| 1340 | }, | ||
| 1341 | .all => el.schedule(thread, .{ .head = next_fiber, .tail = cond_impl.tail }), | ||
| 1342 | }, | ||
| 1343 | } | ||
| 1344 | fiber.queue_next = null; | ||
| 1304 | } | 1345 | } |
| 1305 | 1346 | ||
| 1306 | fn conditionWake(userdata: ?*anyopaque, cond: *Io.Condition) void { | 1347 | fn conditionWake(userdata: ?*anyopaque, cond: *Io.Condition, wake: Io.Condition.Wake) void { |
| 1307 | const el: *EventLoop = @alignCast(@ptrCast(userdata)); | 1348 | const el: *EventLoop = @alignCast(@ptrCast(userdata)); |
| 1308 | const cond_state: *?*Fiber = @ptrCast(&cond.state); | 1349 | const waiting_fiber = @atomicRmw(?*Fiber, @as(*?*Fiber, @ptrCast(&cond.state)), .Xchg, null, .acquire) orelse return; |
| 1309 | if (@atomicRmw(?*Fiber, cond_state, .Xchg, null, .acquire)) |fiber| { | 1350 | waiting_fiber.resultPointer(ConditionImpl).event = .{ .wake = wake }; |
| 1310 | el.yield(fiber, .reschedule); | 1351 | el.yield(waiting_fiber, .reschedule); |
| 1311 | } | ||
| 1312 | } | 1352 | } |
| 1313 | 1353 | ||
| 1314 | fn errno(signed: i32) std.os.linux.E { | 1354 | fn errno(signed: i32) std.os.linux.E { |
lib/std/Thread/Pool.zig+5-2| ... | @@ -666,7 +666,7 @@ fn conditionWait(userdata: ?*anyopaque, cond: *Io.Condition, mutex: *Io.Mutex) I | ... | @@ -666,7 +666,7 @@ fn conditionWait(userdata: ?*anyopaque, cond: *Io.Condition, mutex: *Io.Mutex) I |
| 666 | } | 666 | } |
| 667 | } | 667 | } |
| 668 | 668 | ||
| 669 | fn conditionWake(userdata: ?*anyopaque, cond: *Io.Condition) void { | 669 | fn conditionWake(userdata: ?*anyopaque, cond: *Io.Condition, wake: Io.Condition.Wake) void { |
| 670 | const pool: *std.Thread.Pool = @alignCast(@ptrCast(userdata)); | 670 | const pool: *std.Thread.Pool = @alignCast(@ptrCast(userdata)); |
| 671 | _ = pool; | 671 | _ = pool; |
| 672 | comptime assert(@TypeOf(cond.state) == u64); | 672 | comptime assert(@TypeOf(cond.state) == u64); |
| ... | @@ -690,7 +690,10 @@ fn conditionWake(userdata: ?*anyopaque, cond: *Io.Condition) void { | ... | @@ -690,7 +690,10 @@ fn conditionWake(userdata: ?*anyopaque, cond: *Io.Condition) void { |
| 690 | return; | 690 | return; |
| 691 | } | 691 | } |
| 692 | 692 | ||
| 693 | const to_wake = 1; | 693 | const to_wake = switch (wake) { |
| 694 | .one => 1, | ||
| 695 | .all => wakeable, | ||
| 696 | }; | ||
| 694 | 697 | ||
| 695 | // Reserve the amount of waiters to wake by incrementing the signals count. | 698 | // Reserve the amount of waiters to wake by incrementing the signals count. |
| 696 | // Release barrier ensures code before the wake() happens before the signal it posted and consumed by the wait() threads. | 699 | // Release barrier ensures code before the wake() happens before the signal it posted and consumed by the wait() threads. |