std.Io: fix Select cancel deadlock with no tasks

This commit is contained in:
Andrew Kelley 2026-03-02 08:30:42 -08:00
parent ac24e6caf5
commit 0a412853aa
2 changed files with 14 additions and 6 deletions

View file

@ -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);

View file

@ -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());
}