authorgravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2026-03-02 08:30:42-08:00
committergravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2026-03-03 02:26:23+01:00
log0a412853aae9815eb663a88a8a2d37b91c614317
tree949e2b9badad14123c67c3974c327b696d509b32
parentac24e6caf5a79573f16d2ccc273d907ad2199032

std.Io: fix Select cancel deadlock with no tasks


2 files changed, 14 insertions(+), 6 deletions(-)

lib/std/Io.zig+3-6
...@@ -1446,11 +1446,8 @@ pub fn Select(comptime U: type) type {...@@ -1446,11 +1446,8 @@ pub fn Select(comptime U: type) type {
1446 /// Threadsafe.1446 /// Threadsafe.
1447 pub fn cancel(s: *S) ?U {1447 pub fn cancel(s: *S) ?U {
1448 const io = s.io;1448 const io = s.io;
1449 if (s.group.token.load(.acquire)) |token| {1449 s.group.cancel(io);
1450 io.vtable.groupCancel(io.userdata, &s.group, token);1450 s.queue.close(io);
1451 assert(s.group.token.raw == null);
1452 s.queue.close(io);
1453 }
1454 return s.queue.getOneUncancelable(io) catch |err| switch (err) {1451 return s.queue.getOneUncancelable(io) catch |err| switch (err) {
1455 error.Closed => return null,1452 error.Closed => return null,
1456 };1453 };
...@@ -1855,7 +1852,7 @@ pub const TypeErasedQueue = struct {...@@ -1855,7 +1852,7 @@ pub const TypeErasedQueue = struct {
1855 /// there is space in the buffer. However, existing elements of the1852 /// there is space in the buffer. However, existing elements of the
1856 /// queue are retrieved before `error.Closed` is returned.1853 /// queue are retrieved before `error.Closed` is returned.
1857 ///1854 ///
1858 /// Threadsafe.1855 /// Idempotent. Threadsafe.
1859 pub fn close(q: *TypeErasedQueue, io: Io) void {1856 pub fn close(q: *TypeErasedQueue, io: Io) void {
1860 q.mutex.lockUncancelable(io);1857 q.mutex.lockUncancelable(io);
1861 defer q.mutex.unlock(io);1858 defer q.mutex.unlock(io);
lib/std/Io/test.zig+11
...@@ -937,3 +937,14 @@ test "Select with empty buffer, no deadlock" {...@@ -937,3 +937,14 @@ test "Select with empty buffer, no deadlock" {
937 };937 };
938 assert((try select.await()) == .sleeper);938 assert((try select.await()) == .sleeper);
939}939}
940
941test "Select.cancel with no tasks, no deadlock" {
942 const io = testing.io;
943
944 const U = union(enum) {
945 nothing: void,
946 also_nothing: void,
947 };
948 var select: Io.Select(U) = .init(io, &.{});
949 try expectEqual(null, select.cancel());
950}