diff --git a/lib/std/Io.zig b/lib/std/Io.zig index d9bf914aa4..7b06ccfb6a 100644 --- a/lib/std/Io.zig +++ b/lib/std/Io.zig @@ -1446,11 +1446,8 @@ pub fn Select(comptime U: type) type { /// Threadsafe. pub fn cancel(s: *S) ?U { const io = s.io; - if (s.group.token.load(.acquire)) |token| { - io.vtable.groupCancel(io.userdata, &s.group, token); - assert(s.group.token.raw == null); - s.queue.close(io); - } + s.group.cancel(io); + s.queue.close(io); return s.queue.getOneUncancelable(io) catch |err| switch (err) { error.Closed => return null, }; @@ -1855,7 +1852,7 @@ pub const TypeErasedQueue = struct { /// there is space in the buffer. However, existing elements of the /// queue are retrieved before `error.Closed` is returned. /// - /// Threadsafe. + /// Idempotent. Threadsafe. pub fn close(q: *TypeErasedQueue, io: Io) void { q.mutex.lockUncancelable(io); defer q.mutex.unlock(io); diff --git a/lib/std/Io/test.zig b/lib/std/Io/test.zig index 2de8d1022c..62ad3c2ec8 100644 --- a/lib/std/Io/test.zig +++ b/lib/std/Io/test.zig @@ -937,3 +937,14 @@ test "Select with empty buffer, no deadlock" { }; assert((try select.await()) == .sleeper); } + +test "Select.cancel with no tasks, no deadlock" { + const io = testing.io; + + const U = union(enum) { + nothing: void, + also_nothing: void, + }; + var select: Io.Select(U) = .init(io, &.{}); + try expectEqual(null, select.cancel()); +}