authorgravatar for jacobly@ziglang.orgJacob Young <jacobly@ziglang.org> 2026-02-15 05:51:42-05:00
committergravatar for jacobly@ziglang.orgJacob Young <jacobly@ziglang.org> 2026-02-15 05:52:07-05:00
log5763f7dbcc65288d7d13236f82eec9a23d97bee7
tree460566d2e6d92c5c1916493b87e48d13a1e5b1d2
parentd9fc7fa04db797d7b27dab2d9d6f56f63848da76

std.Io.Evented: clean up supporting code for `select`


3 files changed, 207 insertions(+), 210 deletions(-)

lib/std/Io/Dispatch.zig+33-53
......@@ -100,15 +100,11 @@ const Fiber = struct {
100100 required_align: void align(4),
101101 evented: *Evented,
102102 context: Io.fiber.Context,
103 await_count: i32,
104103 link: union {
105104 awaiter: ?*Fiber,
106105 group: struct { prev: ?*Fiber, next: ?*Fiber },
107106 },
108 status: union(enum) {
109 queue_next: ?*Fiber,
110 awaiting_group: Group,
111 },
107 awaiting_group: Group,
112108 cancel_status: CancelStatus,
113109 cancel_protection: CancelProtection,
114110
......@@ -215,7 +211,6 @@ const Fiber = struct {
215211 }
216212
217213 fn destroy(fiber: *Fiber, ev: *Evented) void {
218 assert(fiber.status.queue_next == null);
219214 ev.allocator().free(fiber.allocatedSlice());
220215 }
221216
......@@ -271,8 +266,8 @@ const Fiber = struct {
271266 .group => {
272267 // The awaiter received a cancelation request while awaiting a group,
273268 // so propagate the cancelation to the group.
274 if (fiber.status.awaiting_group.cancel(ev, null)) {
275 fiber.status = .{ .queue_next = null };
269 if (fiber.awaiting_group.cancel(ev, null)) {
270 fiber.awaiting_group = undefined;
276271 ev.queue.async(fiber, &Fiber.@"resume");
277272 }
278273 },
......@@ -516,9 +511,8 @@ pub fn init(ev: *Evented, backing_allocator: Allocator, options: InitOptions) !v
516511 .required_align = {},
517512 .evented = ev,
518513 .context = undefined,
519 .await_count = 0,
520514 .link = .{ .awaiter = null },
521 .status = .{ .queue_next = null },
515 .awaiting_group = undefined,
522516 .cancel_status = .unrequested,
523517 .cancel_protection = .unblocked,
524518 },
......@@ -636,7 +630,7 @@ const SwitchMessage = struct {
636630
637631 const PendingTask = union(enum) {
638632 nothing,
639 await: u31,
633 await: *Fiber,
640634 activate: c.dispatch.object_t,
641635 @"resume": c.dispatch.object_t,
642636 group_await: Group,
......@@ -655,10 +649,10 @@ const SwitchMessage = struct {
655649 thread.current_context = message.contexts.new;
656650 switch (message.pending_task) {
657651 .nothing => {},
658 .await => |count| {
659 const fiber: *Fiber = @alignCast(@fieldParentPtr("context", message.contexts.old));
660 if (@atomicRmw(i32, &fiber.await_count, .Sub, count, .monotonic) > 0)
661 ev.queue.async(fiber, &Fiber.@"resume");
652 .await => |awaiting| {
653 const awaiter: *Fiber = @alignCast(@fieldParentPtr("context", message.contexts.old));
654 if (@atomicRmw(?*Fiber, &awaiting.link.awaiter, .Xchg, awaiter, .acq_rel) ==
655 Fiber.finished) ev.queue.async(awaiter, &Fiber.@"resume");
662656 },
663657 .activate => |object| object.activate(),
664658 .@"resume" => |object| object.@"resume"(),
......@@ -992,7 +986,7 @@ fn crashHandler(userdata: ?*anyopaque) void {
992986}
993987
994988const AsyncClosure = struct {
995 ev: *Evented,
989 evented: *Evented,
996990 fiber: *Fiber,
997991 start: *const fn (context: *const anyopaque, result: *anyopaque) void,
998992 result_align: Alignment,
......@@ -1029,13 +1023,13 @@ const AsyncClosure = struct {
10291023 closure: *AsyncClosure,
10301024 message: *const SwitchMessage,
10311025 ) callconv(.withStackAlign(.c, @alignOf(AsyncClosure))) noreturn {
1032 message.handle(closure.ev);
1026 const ev = closure.evented;
10331027 const fiber = closure.fiber;
1028 message.handle(ev);
10341029 closure.start(closure.contextPointer(), fiber.resultBytes(closure.result_align));
10351030 if (@atomicRmw(?*Fiber, &fiber.link.awaiter, .Xchg, Fiber.finished, .acq_rel)) |awaiter|
1036 if (@atomicRmw(i32, &awaiter.await_count, .Add, 1, .monotonic) == -1)
1037 closure.ev.queue.async(awaiter, &Fiber.@"resume");
1038 closure.ev.yield(.nothing);
1031 ev.queue.async(awaiter, &Fiber.@"resume");
1032 ev.yield(.nothing);
10391033 unreachable; // switched to dead fiber
10401034 }
10411035};
......@@ -1090,14 +1084,13 @@ fn concurrent(
10901084 },
10911085 else => |arch| @compileError("unimplemented architecture: " ++ @tagName(arch)),
10921086 },
1093 .await_count = 0,
10941087 .link = .{ .awaiter = null },
1095 .status = .{ .queue_next = null },
1088 .awaiting_group = undefined,
10961089 .cancel_status = .unrequested,
10971090 .cancel_protection = .unblocked,
10981091 };
10991092 closure.* = .{
1100 .ev = ev,
1093 .evented = ev,
11011094 .fiber = fiber,
11021095 .start = start,
11031096 .result_align = result_alignment,
......@@ -1115,18 +1108,11 @@ fn await(
11151108 result_alignment: Alignment,
11161109) void {
11171110 const ev: *Evented = @ptrCast(@alignCast(userdata));
1118 const fiber = Thread.current().currentFiber();
1119 const future_fiber: *Fiber = @ptrCast(@alignCast(future));
1120 if (@atomicRmw(?*Fiber, &future_fiber.link.awaiter, .Xchg, fiber, .acq_rel)) |awaiter| {
1121 assert(awaiter == Fiber.finished);
1122 } else while (true) {
1123 ev.yield(.{ .await = 1 });
1124 const awaiter = @atomicLoad(?*Fiber, &future_fiber.link.awaiter, .acquire);
1125 if (awaiter == Fiber.finished) break;
1126 assert(awaiter == fiber); // spurious wakeup
1127 }
1128 @memcpy(result, future_fiber.resultBytes(result_alignment));
1129 future_fiber.destroy(ev);
1111 const awaiting: *Fiber = @ptrCast(@alignCast(future));
1112 if (@atomicLoad(?*Fiber, &awaiting.link.awaiter, .acquire) != Fiber.finished)
1113 ev.yield(.{ .await = awaiting });
1114 @memcpy(result, awaiting.resultBytes(result_alignment));
1115 awaiting.destroy(ev);
11301116}
11311117
11321118fn cancel(
......@@ -1261,8 +1247,8 @@ const Group = struct {
12611247 .awaiter_delayed = false,
12621248 .fibers = .null,
12631249 }, .release);
1264 assert(awaiter.status.awaiting_group.ptr == group.ptr);
1265 awaiter.status = .{ .queue_next = null };
1250 assert(awaiter.awaiting_group.ptr == group.ptr);
1251 awaiter.awaiting_group = undefined;
12661252 return awaiter;
12671253 }
12681254 // Race with `Fiber.requestCancel`
......@@ -1330,8 +1316,7 @@ const Group = struct {
13301316
13311317 /// Assumes the mutex is held.
13321318 fn registerAwaiter(group: Group, awaiter: *Fiber) bool {
1333 assert(awaiter.status.queue_next == null);
1334 awaiter.status = .{ .awaiting_group = group };
1319 awaiter.awaiting_group = group;
13351320 assert(@atomicRmw(
13361321 Awaiter,
13371322 group.awaiterPtr(),
......@@ -1343,7 +1328,7 @@ const Group = struct {
13431328 }
13441329
13451330 const AsyncClosure = struct {
1346 ev: *Evented,
1331 evented: *Evented,
13471332 group: Group,
13481333 fiber: *Fiber,
13491334 start: *const fn (context: *const anyopaque) Io.Cancelable!void,
......@@ -1382,19 +1367,15 @@ const Group = struct {
13821367 closure: *Group.AsyncClosure,
13831368 message: *const SwitchMessage,
13841369 ) callconv(.withStackAlign(.c, @alignOf(Group.AsyncClosure))) noreturn {
1385 message.handle(closure.ev);
1386 assert(closure.fiber.status.queue_next == null);
1387 const result = closure.start(closure.contextPointer());
1388 const ev = closure.ev;
1389 const group = closure.group;
1370 const ev = closure.evented;
13901371 const fiber = closure.fiber;
1391 const cancel_acknowledged = fiber.cancel_protection.acknowledged;
1392 if (result) {
1393 assert(!cancel_acknowledged); // group task acknowledged cancelation but did not return `error.Canceled`
1372 message.handle(ev);
1373 if (closure.start(closure.contextPointer())) {
1374 assert(!fiber.cancel_protection.acknowledged); // group task acknowledged cancelation but did not return `error.Canceled`
13941375 } else |err| switch (err) {
1395 error.Canceled => assert(cancel_acknowledged), // group task returned `error.Canceled` but was never canceled
1376 error.Canceled => assert(fiber.cancel_protection.acknowledged), // group task returned `error.Canceled` but was never canceled
13961377 }
1397 if (group.removeFiber(ev, fiber)) |awaiter| ev.queue.async(awaiter, &Fiber.@"resume");
1378 if (closure.group.removeFiber(ev, fiber)) |awaiter| ev.queue.async(awaiter, &Fiber.@"resume");
13981379 ev.yield(.destroy);
13991380 unreachable; // switched to dead fiber
14001381 }
......@@ -1464,14 +1445,13 @@ fn groupConcurrent(
14641445 },
14651446 else => |arch| @compileError("unimplemented architecture: " ++ @tagName(arch)),
14661447 },
1467 .await_count = 0,
14681448 .link = .{ .group = .{ .prev = null, .next = null } },
1469 .status = .{ .queue_next = null },
1449 .awaiting_group = undefined,
14701450 .cancel_status = .unrequested,
14711451 .cancel_protection = .unblocked,
14721452 };
14731453 closure.* = .{
1474 .ev = ev,
1454 .evented = ev,
14751455 .group = group,
14761456 .fiber = fiber,
14771457 .start = start,
lib/std/Io/Uring.zig+170-157
......@@ -150,7 +150,6 @@ const Thread = struct {
150150const Fiber = struct {
151151 required_align: void align(4),
152152 context: Io.fiber.Context,
153 await_count: i32,
154153 link: union {
155154 awaiter: ?*Fiber,
156155 group: struct { prev: ?*Fiber, next: ?*Fiber },
......@@ -856,7 +855,6 @@ pub fn init(ev: *Evented, backing_allocator: Allocator, options: InitOptions) !v
856855 main_fiber.* = .{
857856 .required_align = {},
858857 .context = undefined,
859 .await_count = 0,
860858 .link = .{ .awaiter = null },
861859 .status = .{ .queue_next = null },
862860 .cancel_status = .unrequested,
......@@ -1260,7 +1258,7 @@ const SwitchMessage = struct {
12601258 const PendingTask = union(enum) {
12611259 nothing,
12621260 reschedule,
1263 await: u31,
1261 await: *Fiber,
12641262 group_await: Group,
12651263 group_cancel: Group,
12661264 batch_await: *Io.Batch,
......@@ -1284,10 +1282,11 @@ const SwitchMessage = struct {
12841282 assert(fiber.status.queue_next == null);
12851283 _ = ev.schedule(thread, .{ .head = fiber, .tail = fiber });
12861284 },
1287 .await => |count| {
1288 const fiber: *Fiber = @alignCast(@fieldParentPtr("context", message.contexts.old));
1289 if (@atomicRmw(i32, &fiber.await_count, .Sub, count, .monotonic) > 0)
1290 _ = ev.schedule(thread, .{ .head = fiber, .tail = fiber });
1285 .await => |awaiting| {
1286 const awaiter: *Fiber = @alignCast(@fieldParentPtr("context", message.contexts.old));
1287 assert(awaiter.status.queue_next == null);
1288 if (@atomicRmw(?*Fiber, &awaiting.link.awaiter, .Xchg, awaiter, .acq_rel) ==
1289 Fiber.finished) _ = ev.schedule(thread, .{ .head = awaiter, .tail = awaiter });
12911290 },
12921291 .group_await => |group| {
12931292 const fiber: *Fiber = @alignCast(@fieldParentPtr("context", message.contexts.old));
......@@ -1361,7 +1360,7 @@ fn crashHandler(userdata: ?*anyopaque) void {
13611360}
13621361
13631362const AsyncClosure = struct {
1364 ev: *Evented,
1363 evented: *Evented,
13651364 fiber: *Fiber,
13661365 start: *const fn (context: *const anyopaque, result: *anyopaque) void,
13671366 result_align: Alignment,
......@@ -1398,16 +1397,11 @@ const AsyncClosure = struct {
13981397 closure: *AsyncClosure,
13991398 message: *const SwitchMessage,
14001399 ) callconv(.withStackAlign(.c, @alignOf(AsyncClosure))) noreturn {
1401 message.handle(closure.ev);
1400 const ev = closure.evented;
14021401 const fiber = closure.fiber;
1402 message.handle(ev);
14031403 closure.start(closure.contextPointer(), fiber.resultBytes(closure.result_align));
1404 closure.ev.yield(
1405 if (@atomicRmw(?*Fiber, &fiber.link.awaiter, .Xchg, Fiber.finished, .acq_rel)) |awaiter|
1406 if (@atomicRmw(i32, &awaiter.await_count, .Add, 1, .monotonic) == -1) awaiter else null
1407 else
1408 null,
1409 .nothing,
1410 );
1404 ev.yield(@atomicRmw(?*Fiber, &fiber.link.awaiter, .Xchg, Fiber.finished, .acq_rel), .nothing);
14111405 unreachable; // switched to dead fiber
14121406 }
14131407};
......@@ -1461,7 +1455,6 @@ fn concurrent(
14611455 },
14621456 else => |arch| @compileError("unimplemented architecture: " ++ @tagName(arch)),
14631457 },
1464 .await_count = 0,
14651458 .link = .{ .awaiter = null },
14661459 .status = .{ .queue_next = null },
14671460 .cancel_status = .unrequested,
......@@ -1479,7 +1472,7 @@ fn concurrent(
14791472 },
14801473 };
14811474 closure.* = .{
1482 .ev = ev,
1475 .evented = ev,
14831476 .fiber = fiber,
14841477 .start = start,
14851478 .result_align = result_alignment,
......@@ -1498,18 +1491,11 @@ fn await(
14981491 result_alignment: Alignment,
14991492) void {
15001493 const ev: *Evented = @ptrCast(@alignCast(userdata));
1501 const fiber = Thread.current().currentFiber();
1502 const future_fiber: *Fiber = @ptrCast(@alignCast(future));
1503 if (@atomicRmw(?*Fiber, &future_fiber.link.awaiter, .Xchg, fiber, .acq_rel)) |awaiter| {
1504 assert(awaiter == Fiber.finished);
1505 } else while (true) {
1506 ev.yield(null, .{ .await = 1 });
1507 const awaiter = @atomicLoad(?*Fiber, &future_fiber.link.awaiter, .acquire);
1508 if (awaiter == Fiber.finished) break;
1509 assert(awaiter == fiber); // spurious wakeup
1510 }
1511 @memcpy(result, future_fiber.resultBytes(result_alignment));
1512 future_fiber.destroy();
1494 const awaiting: *Fiber = @ptrCast(@alignCast(future));
1495 if (@atomicLoad(?*Fiber, &awaiting.link.awaiter, .acquire) != Fiber.finished)
1496 ev.yield(null, .{ .await = awaiting });
1497 @memcpy(result, awaiting.resultBytes(result_alignment));
1498 awaiting.destroy();
15131499}
15141500
15151501fn cancel(
......@@ -1726,7 +1712,7 @@ const Group = struct {
17261712 }
17271713
17281714 const AsyncClosure = struct {
1729 ev: *Evented,
1715 evented: *Evented,
17301716 group: Group,
17311717 fiber: *Fiber,
17321718 start: *const fn (context: *const anyopaque) Io.Cancelable!void,
......@@ -1765,19 +1751,16 @@ const Group = struct {
17651751 closure: *Group.AsyncClosure,
17661752 message: *const SwitchMessage,
17671753 ) callconv(.withStackAlign(.c, @alignOf(Group.AsyncClosure))) noreturn {
1768 message.handle(closure.ev);
1769 assert(closure.fiber.status.queue_next == null);
1770 const result = closure.start(closure.contextPointer());
1771 const ev = closure.ev;
1772 const group = closure.group;
1754 const ev = closure.evented;
17731755 const fiber = closure.fiber;
1774 const cancel_acknowledged = fiber.cancel_protection.acknowledged;
1775 if (result) {
1776 assert(!cancel_acknowledged); // group task acknowledged cancelation but did not return `error.Canceled`
1756 message.handle(ev);
1757 assert(fiber.status.queue_next == null);
1758 if (closure.start(closure.contextPointer())) {
1759 assert(!fiber.cancel_protection.acknowledged); // group task acknowledged cancelation but did not return `error.Canceled`
17771760 } else |err| switch (err) {
1778 error.Canceled => assert(cancel_acknowledged), // group task returned `error.Canceled` but was never canceled
1761 error.Canceled => assert(fiber.cancel_protection.acknowledged), // group task returned `error.Canceled` but was never canceled
17791762 }
1780 ev.yield(group.removeFiber(ev, fiber), .destroy);
1763 ev.yield(closure.group.removeFiber(ev, fiber), .destroy);
17811764 unreachable; // switched to dead fiber
17821765 }
17831766 };
......@@ -1845,7 +1828,6 @@ fn groupConcurrent(
18451828 },
18461829 else => |arch| @compileError("unimplemented architecture: " ++ @tagName(arch)),
18471830 },
1848 .await_count = 0,
18491831 .link = .{ .group = .{ .prev = null, .next = null } },
18501832 .status = .{ .queue_next = null },
18511833 .cancel_status = .unrequested,
......@@ -1863,7 +1845,7 @@ fn groupConcurrent(
18631845 },
18641846 };
18651847 closure.* = .{
1866 .ev = ev,
1848 .evented = ev,
18671849 .group = group,
18681850 .fiber = fiber,
18691851 .start = start,
......@@ -2173,7 +2155,7 @@ fn deviceIoControl(
21732155 const rc = linux.ioctl(o.file.handle, @bitCast(o.code), @intFromPtr(o.arg));
21742156 switch (linux.errno(rc)) {
21752157 .SUCCESS => return @bitCast(@as(u32, @truncate(rc))),
2176 .INTR => continue,
2158 .INTR => {},
21772159 else => |err| return -@as(i32, @intFromEnum(err)),
21782160 }
21792161 }
......@@ -2571,7 +2553,7 @@ fn dirCreateDir(
25712553 ev.yield(null, .nothing);
25722554 switch (cancel_region.errno()) {
25732555 .SUCCESS => return,
2574 .INTR, .CANCELED => continue,
2556 .INTR, .CANCELED => {},
25752557 .ACCES => return error.AccessDenied,
25762558 .BADF => |err| return errnoBug(err), // File descriptor used after closed.
25772559 .PERM => return error.PermissionDenied,
......@@ -2654,7 +2636,7 @@ fn filePathKind(ev: *Evented, dir: Dir, sub_path: []const u8) !File.Kind {
26542636 if (!statx_buf.mask.TYPE) return error.Unexpected;
26552637 return statxKind(statx_buf.mode);
26562638 },
2657 .INTR, .CANCELED => continue,
2639 .INTR, .CANCELED => {},
26582640 .ACCES => |err| return errnoBug(err),
26592641 .BADF => |err| return errnoBug(err), // File descriptor used after closed.
26602642 .FAULT => |err| return errnoBug(err),
......@@ -2767,7 +2749,7 @@ fn dirAccess(
27672749 try sync.cancel_region.await(.nothing);
27682750 switch (linux.errno(linux.faccessat(dir.handle, sub_path_posix, mode, flags))) {
27692751 .SUCCESS => return,
2770 .INTR => continue,
2752 .INTR => {},
27712753 .ACCES => return error.AccessDenied,
27722754 .PERM => return error.PermissionDenied,
27732755 .ROFS => return error.ReadOnlyFileSystem,
......@@ -2806,7 +2788,7 @@ fn dirCreateFile(
28062788 .EXCL = flags.exclusive,
28072789 .CLOEXEC = true,
28082790 }, flags.permissions.toMode());
2809 errdefer ev.close(fd);
2791 errdefer ev.closeAsync(fd);
28102792
28112793 switch (flags.lock) {
28122794 .none => {},
......@@ -2994,7 +2976,7 @@ fn dirOpenFile(
29942976 .CLOEXEC = true,
29952977 .PATH = flags.path_only,
29962978 }, 0);
2997 errdefer ev.close(fd);
2979 errdefer ev.closeAsync(fd);
29982980
29992981 if (!flags.allow_directory) {
30002982 const is_dir = is_dir: {
......@@ -3048,7 +3030,7 @@ fn dirRead(userdata: ?*anyopaque, dr: *Dir.Reader, buffer: []Dir.Entry) Dir.Read
30483030 const rc = linux.getdents64(dr.dir.handle, dr.buffer.ptr, dr.buffer.len);
30493031 switch (linux.errno(rc)) {
30503032 .SUCCESS => break rc,
3051 .INTR => continue,
3033 .INTR => {},
30523034 .BADF => |err| return errnoBug(err), // Dir is invalid or was opened without iteration ability.
30533035 .FAULT => |err| return errnoBug(err),
30543036 .NOTDIR => |err| return errnoBug(err),
......@@ -3141,7 +3123,7 @@ fn dirRealPathFile(
31413123 error.FileLocksUnsupported => return errnoBug(.OPNOTSUPP), // Not asking for locks.
31423124 else => |e| return e,
31433125 };
3144 defer ev.close(fd);
3126 defer ev.closeAsync(fd);
31453127 return ev.realPath(try maybe_sync.enterSync(ev), fd, out_buffer);
31463128}
31473129
......@@ -3174,7 +3156,7 @@ fn dirDeleteFile(userdata: ?*anyopaque, dir: Dir, sub_path: []const u8) Dir.Dele
31743156 ev.yield(null, .nothing);
31753157 switch (cancel_region.errno()) {
31763158 .SUCCESS => return,
3177 .INTR, .CANCELED => continue,
3159 .INTR, .CANCELED => {},
31783160 .PERM => return error.PermissionDenied,
31793161 .ACCES => return error.AccessDenied,
31803162 .BUSY => return error.FileBusy,
......@@ -3226,7 +3208,7 @@ fn dirDeleteDir(userdata: ?*anyopaque, dir: Dir, sub_path: []const u8) Dir.Delet
32263208 ev.yield(null, .nothing);
32273209 switch (cancel_region.errno()) {
32283210 .SUCCESS => return,
3229 .INTR, .CANCELED => continue,
3211 .INTR, .CANCELED => {},
32303212 .ACCES => return error.AccessDenied,
32313213 .PERM => return error.PermissionDenied,
32323214 .BUSY => return error.FileBusy,
......@@ -3342,7 +3324,7 @@ fn dirSymLink(
33423324 ev.yield(null, .nothing);
33433325 switch (cancel_region.errno()) {
33443326 .SUCCESS => return,
3345 .INTR, .CANCELED => continue,
3327 .INTR, .CANCELED => {},
33463328 .FAULT => |err| return errnoBug(err),
33473329 .INVAL => |err| return errnoBug(err),
33483330 .ACCES => return error.AccessDenied,
......@@ -3381,7 +3363,7 @@ fn dirReadLink(
33813363 const rc = linux.readlinkat(dir.handle, sub_path_posix, buffer.ptr, buffer.len);
33823364 switch (linux.errno(rc)) {
33833365 .SUCCESS => return @bitCast(rc),
3384 .INTR => continue,
3366 .INTR => {},
33853367 .ACCES => return error.AccessDenied,
33863368 .FAULT => |err| return errnoBug(err),
33873369 .INVAL => return error.NotLink,
......@@ -3571,7 +3553,7 @@ fn fileLength(userdata: ?*anyopaque, file: File) File.LengthError!u64 {
35713553 if (!statx_buf.mask.SIZE) return error.Unexpected;
35723554 return statx_buf.size;
35733555 },
3574 .INTR, .CANCELED => continue,
3556 .INTR, .CANCELED => {},
35753557 .ACCES => |err| return errnoBug(err),
35763558 .BADF => |err| return errnoBug(err), // File descriptor used after closed.
35773559 .FAULT => |err| return errnoBug(err),
......@@ -3754,7 +3736,7 @@ fn fileSync(userdata: ?*anyopaque, file: File) File.SyncError!void {
37543736 ev.yield(null, .nothing);
37553737 switch (cancel_region.errno()) {
37563738 .SUCCESS => return,
3757 .INTR, .CANCELED => continue,
3739 .INTR, .CANCELED => {},
37583740 .BADF => |err| return errnoBug(err),
37593741 .INVAL => |err| return errnoBug(err),
37603742 .ROFS => |err| return errnoBug(err),
......@@ -3776,7 +3758,7 @@ fn fileIsTty(userdata: ?*anyopaque, file: File) Io.Cancelable!bool {
37763758 const rc = linux.ioctl(file.handle, linux.T.IOCGWINSZ, @intFromPtr(&wsz));
37773759 switch (linux.errno(rc)) {
37783760 .SUCCESS => return true,
3779 .INTR => continue,
3761 .INTR => {},
37803762 else => return false,
37813763 }
37823764 }
......@@ -3812,7 +3794,7 @@ fn fileSetLength(userdata: ?*anyopaque, file: File, length: u64) File.SetLengthE
38123794 ev.yield(null, .nothing);
38133795 switch (cancel_region.errno()) {
38143796 .SUCCESS => return,
3815 .INTR, .CANCELED => continue,
3797 .INTR, .CANCELED => {},
38163798 .FBIG => return error.FileTooBig,
38173799 .IO => return error.InputOutput,
38183800 .PERM => return error.PermissionDenied,
......@@ -3994,7 +3976,7 @@ fn fileMemoryMapCreate(
39943976 const rc = linux.mmap(null, options.len, prot, flags, file.handle, casted_offset);
39953977 switch (linux.errno(rc)) {
39963978 .SUCCESS => break @as([*]align(page_align) u8, @ptrFromInt(rc))[0..options.len],
3997 .INTR => continue,
3979 .INTR => {},
39983980 .ACCES => return error.AccessDenied,
39993981 .AGAIN => return error.LockedMemoryLimitExceeded,
40003982 .MFILE => return error.ProcessFdQuotaExceeded,
......@@ -4054,7 +4036,7 @@ fn fileMemoryMapSetLength(
40544036 const rc = linux.mremap(old_memory.ptr, old_memory.len, new_len, flags, addr_hint);
40554037 switch (linux.errno(rc)) {
40564038 .SUCCESS => break @as([*]align(page_align) u8, @ptrFromInt(rc))[0..new_len],
4057 .INTR => continue,
4039 .INTR => {},
40584040 .AGAIN => return error.LockedMemoryLimitExceeded,
40594041 .NOMEM => return error.OutOfMemory,
40604042 .INVAL => |err| return errnoBug(err),
......@@ -4163,7 +4145,7 @@ fn processCurrentPath(userdata: ?*anyopaque, buffer: []u8) process.CurrentPathEr
41634145 try sync.cancel_region.await(.nothing);
41644146 switch (linux.errno(linux.getcwd(buffer.ptr, buffer.len))) {
41654147 .SUCCESS => return std.mem.findScalar(u8, buffer, 0).?,
4166 .INTR => continue,
4148 .INTR => {},
41674149 .NOENT => return error.CurrentDirUnlinked,
41684150 .RANGE => return error.NameTooLong,
41694151 .FAULT => |err| return errnoBug(err),
......@@ -4178,7 +4160,7 @@ fn processSetCurrentDir(userdata: ?*anyopaque, dir: Dir) process.SetCurrentDirEr
41784160 if (dir.handle == linux.AT.FDCWD) return;
41794161 var sync: CancelRegion.Sync = try .init(ev);
41804162 defer sync.deinit(ev);
4181 return ev.fchdir(&sync, dir.handle);
4163 return fchdir(&sync, dir.handle);
41824164}
41834165
41844166fn processSetCurrentPath(userdata: ?*anyopaque, dir_path: []const u8) ChdirError!void {
......@@ -4215,7 +4197,7 @@ fn processReplace(userdata: ?*anyopaque, options: process.ReplaceOptions) proces
42154197
42164198 var sync: CancelRegion.Sync = try .init(ev);
42174199 defer sync.deinit(ev);
4218 return ev.execv(&sync, options.expand_arg0, argv_buf.ptr[0].?, argv_buf.ptr, env_block, PATH);
4200 return execv(&sync, options.expand_arg0, argv_buf.ptr[0].?, argv_buf.ptr, env_block, PATH);
42194201}
42204202
42214203fn processReplacePath(
......@@ -4235,7 +4217,7 @@ fn processSpawn(userdata: ?*anyopaque, options: process.SpawnOptions) process.Sp
42354217 const spawned = try ev.spawn(options);
42364218 var cancel_region: CancelRegion = .initBlocked();
42374219 defer cancel_region.deinit();
4238 defer ev.close(spawned.err_fd);
4220 defer ev.closeAsync(spawned.err_fd);
42394221
42404222 // Wait for the child to report any errors in or before `execvpe`.
42414223 var child_err: ForkBailError = undefined;
......@@ -4377,8 +4359,9 @@ fn spawn(ev: *Evented, options: process.SpawnOptions) process.SpawnError!Spawned
43774359
43784360 if (pid_result == 0) {
43794361 defer comptime unreachable; // We are the child.
4362 // Note that the parent uring is no longer accessible, so we must no longer reference `ev`.
43804363 var sync: CancelRegion.Sync = .{ .cancel_region = .initBlocked() };
4381 const err = ev.setUpChild(&sync, .{
4364 const err = setUpChild(&sync, .{
43824365 .stdin_pipe = stdin_pipe[0],
43834366 .stdout_pipe = stdout_pipe[1],
43844367 .stderr_pipe = stderr_pipe[1],
......@@ -4389,7 +4372,7 @@ fn spawn(ev: *Evented, options: process.SpawnOptions) process.SpawnError!Spawned
43894372 .PATH = PATH,
43904373 .spawn = options,
43914374 });
4392 ev.writeAll(&sync.cancel_region, err_pipe[1], @ptrCast(&err)) catch {};
4375 writeAllSync(&sync, err_pipe[1], @ptrCast(&err)) catch {};
43934376 const exit = if (builtin.single_threaded) linux.exit else linux.exit_group;
43944377 exit(1);
43954378 }
......@@ -4397,13 +4380,13 @@ fn spawn(ev: *Evented, options: process.SpawnOptions) process.SpawnError!Spawned
43974380 const pid: pid_t = @intCast(pid_result); // We are the parent.
43984381 errdefer comptime unreachable; // The child is forked; we must not error from now on
43994382
4400 ev.close(err_pipe[1]); // make sure only the child holds the write end open
4383 ev.closeAsync(err_pipe[1]); // make sure only the child holds the write end open
44014384
4402 if (options.stdin == .pipe) ev.close(stdin_pipe[0]);
4403 if (options.stdout == .pipe) ev.close(stdout_pipe[1]);
4404 if (options.stderr == .pipe) ev.close(stderr_pipe[1]);
4385 if (options.stdin == .pipe) ev.closeAsync(stdin_pipe[0]);
4386 if (options.stdout == .pipe) ev.closeAsync(stdout_pipe[1]);
4387 if (options.stderr == .pipe) ev.closeAsync(stderr_pipe[1]);
44054388
4406 if (prog_pipe[1] != -1) ev.close(prog_pipe[1]);
4389 if (prog_pipe[1] != -1) ev.closeAsync(prog_pipe[1]);
44074390
44084391 options.progress_node.setIpcFile(ev, .{ .handle = prog_pipe[0], .flags = .{ .nonblocking = true } });
44094392
......@@ -4440,14 +4423,14 @@ pub fn pipe2(flags: linux.O) PipeError![2]fd_t {
44404423 }
44414424}
44424425fn destroyPipe(ev: *Evented, pipe: [2]fd_t) void {
4443 if (pipe[0] != -1) ev.close(pipe[0]);
4444 if (pipe[0] != pipe[1]) ev.close(pipe[1]);
4426 if (pipe[0] != -1) ev.closeAsync(pipe[0]);
4427 if (pipe[0] != pipe[1]) ev.closeAsync(pipe[1]);
44454428}
44464429
44474430/// Errors that can occur between fork() and execv()
44484431const ForkBailError = process.SetCurrentDirError || ChdirError ||
44494432 process.SpawnError || process.ReplaceError;
4450fn setUpChild(ev: *Evented, sync: *CancelRegion.Sync, options: struct {
4433fn setUpChild(sync: *CancelRegion.Sync, options: struct {
44514434 stdin_pipe: fd_t,
44524435 stdout_pipe: fd_t,
44534436 stderr_pipe: fd_t,
......@@ -4458,21 +4441,21 @@ fn setUpChild(ev: *Evented, sync: *CancelRegion.Sync, options: struct {
44584441 PATH: []const u8,
44594442 spawn: process.SpawnOptions,
44604443}) ForkBailError {
4461 try ev.setUpChildIo(
4444 try setUpChildIo(
44624445 sync,
44634446 options.spawn.stdin,
44644447 options.stdin_pipe,
44654448 linux.STDIN_FILENO,
44664449 options.dev_null_fd,
44674450 );
4468 try ev.setUpChildIo(
4451 try setUpChildIo(
44694452 sync,
44704453 options.spawn.stdout,
44714454 options.stdout_pipe,
44724455 linux.STDOUT_FILENO,
44734456 options.dev_null_fd,
44744457 );
4475 try ev.setUpChildIo(
4458 try setUpChildIo(
44764459 sync,
44774460 options.spawn.stderr,
44784461 options.stderr_pipe,
......@@ -4482,17 +4465,17 @@ fn setUpChild(ev: *Evented, sync: *CancelRegion.Sync, options: struct {
44824465
44834466 switch (options.spawn.cwd) {
44844467 .inherit => {},
4485 .dir => |cwd_dir| try ev.fchdir(sync, cwd_dir.handle),
4468 .dir => |cwd_dir| try fchdir(sync, cwd_dir.handle),
44864469 .path => |cwd_path| {
44874470 var cwd_path_buffer: [PATH_MAX]u8 = undefined;
44884471 const cwd_path_posix = try pathToPosix(cwd_path, &cwd_path_buffer);
4489 try ev.chdir(sync, cwd_path_posix);
4472 try chdir(sync, cwd_path_posix);
44904473 },
44914474 }
44924475
44934476 // Must happen after fchdir above, the cwd file descriptor might be
44944477 // equal to prog_fileno and be clobbered by this dup2 call.
4495 if (options.prog_pipe != -1) try ev.dup2(sync, options.prog_pipe, prog_fileno);
4478 if (options.prog_pipe != -1) try dup2(sync, options.prog_pipe, prog_fileno);
44964479
44974480 if (options.spawn.gid) |gid| {
44984481 switch (linux.errno(linux.setregid(gid, gid))) {
......@@ -4532,7 +4515,7 @@ fn setUpChild(ev: *Evented, sync: *CancelRegion.Sync, options: struct {
45324515 }
45334516 }
45344517
4535 return ev.execv(
4518 return execv(
45364519 sync,
45374520 options.spawn.expand_arg0,
45384521 options.argv_buf.ptr[0].?,
......@@ -4543,7 +4526,6 @@ fn setUpChild(ev: *Evented, sync: *CancelRegion.Sync, options: struct {
45434526}
45444527
45454528fn setUpChildIo(
4546 ev: *Evented,
45474529 sync: *CancelRegion.Sync,
45484530 stdio: process.SpawnOptions.StdIo,
45494531 pipe_fd: fd_t,
......@@ -4551,13 +4533,13 @@ fn setUpChildIo(
45514533 dev_null_fd: fd_t,
45524534) !void {
45534535 switch (stdio) {
4554 .pipe => try ev.dup2(sync, pipe_fd, std_fileno),
4536 .pipe => try dup2(sync, pipe_fd, std_fileno),
45554537 .close => _ = linux.close(std_fileno),
45564538 .inherit => {},
4557 .ignore => try ev.dup2(sync, dev_null_fd, std_fileno),
4539 .ignore => try dup2(sync, dev_null_fd, std_fileno),
45584540 .file => |file| {
45594541 if (file.flags.nonblocking) @panic("TODO implement setUpChildIo when nonblocking file is used");
4560 try ev.dup2(sync, file.handle, std_fileno);
4542 try dup2(sync, file.handle, std_fileno);
45614543 },
45624544 }
45634545}
......@@ -4566,13 +4548,12 @@ pub const DupError = error{
45664548 ProcessFdQuotaExceeded,
45674549 SystemResources,
45684550} || Io.UnexpectedError || Io.Cancelable;
4569pub fn dup2(ev: *Evented, sync: *CancelRegion.Sync, old_fd: fd_t, new_fd: fd_t) DupError!void {
4570 _ = ev;
4551pub fn dup2(sync: *CancelRegion.Sync, old_fd: fd_t, new_fd: fd_t) DupError!void {
45714552 while (true) {
45724553 try sync.cancel_region.await(.nothing);
45734554 switch (linux.errno(linux.dup2(old_fd, new_fd))) {
45744555 .SUCCESS => {},
4575 .BUSY, .INTR => continue,
4556 .BUSY, .INTR => {},
45764557 .INVAL => |err| return errnoBug(err), // invalid parameters
45774558 .BADF => |err| return errnoBug(err), // use after free
45784559 .MFILE => return error.ProcessFdQuotaExceeded,
......@@ -4583,7 +4564,6 @@ pub fn dup2(ev: *Evented, sync: *CancelRegion.Sync, old_fd: fd_t, new_fd: fd_t)
45834564}
45844565
45854566fn execv(
4586 ev: *Evented,
45874567 sync: *CancelRegion.Sync,
45884568 arg0_expand: process.ArgExpansion,
45894569 file: [*:0]const u8,
......@@ -4592,7 +4572,8 @@ fn execv(
45924572 PATH: []const u8,
45934573) process.ReplaceError {
45944574 const file_slice = std.mem.sliceTo(file, 0);
4595 if (std.mem.findScalar(u8, file_slice, '/') != null) return ev.execvPath(sync, file, child_argv, env_block);
4575 if (std.mem.findScalar(u8, file_slice, '/') != null)
4576 return execvPath(sync, file, child_argv, env_block);
45964577
45974578 // Use of PATH_MAX here is valid as the path_buf will be passed
45984579 // directly to the operating system in posixExecvPath.
......@@ -4620,7 +4601,7 @@ fn execv(
46204601 .expand => child_argv[0] = full_path,
46214602 .no_expand => {},
46224603 }
4623 err = ev.execvPath(sync, full_path, child_argv, env_block);
4604 err = execvPath(sync, full_path, child_argv, env_block);
46244605 switch (err) {
46254606 error.AccessDenied => seen_eacces = true,
46264607 error.FileNotFound, error.NotDir => {},
......@@ -4632,13 +4613,11 @@ fn execv(
46324613}
46334614/// This function ignores PATH environment variable.
46344615pub fn execvPath(
4635 ev: *Evented,
46364616 sync: *CancelRegion.Sync,
46374617 path: [*:0]const u8,
46384618 child_argv: [*:null]const ?[*:0]const u8,
46394619 env_block: process.Environ.PosixBlock,
46404620) process.ReplaceError {
4641 _ = ev;
46424621 try sync.cancel_region.await(.nothing);
46434622 switch (linux.errno(linux.execve(path, child_argv, env_block.slice.ptr))) {
46444623 .FAULT => |err| return errnoBug(err), // Bad pointer parameter.
......@@ -4709,7 +4688,7 @@ fn childWait(userdata: ?*anyopaque, child: *process.Child) process.Child.WaitErr
47094688 child.resource_usage_statistics.rusage = rusage;
47104689 break;
47114690 },
4712 .INTR, .CANCELED => continue,
4691 .INTR, .CANCELED => {},
47134692 .CHILD => |err| return errnoBug(err), // Double-free.
47144693 else => |err| return unexpectedErrno(err),
47154694 }
......@@ -4724,7 +4703,7 @@ fn childWait(userdata: ?*anyopaque, child: *process.Child) process.Child.WaitErr
47244703 _, .CONTINUED => .{ .unknown = status },
47254704 };
47264705 },
4727 .INTR, .CANCELED => continue,
4706 .INTR, .CANCELED => {},
47284707 .CHILD => |err| return errnoBug(err), // Double-free.
47294708 else => |err| return unexpectedErrno(err),
47304709 }
......@@ -4741,7 +4720,7 @@ fn childKill(userdata: ?*anyopaque, child: *process.Child) void {
47414720 const pid = child.id.?;
47424721 while (true) switch (linux.errno(linux.kill(pid, .TERM))) {
47434722 .SUCCESS => break,
4744 .INTR => continue,
4723 .INTR => {},
47454724 .PERM => return,
47464725 .INVAL => |err| return errnoBug(err) catch {},
47474726 .SRCH => |err| return errnoBug(err) catch {},
......@@ -4773,7 +4752,7 @@ fn childKill(userdata: ?*anyopaque, child: *process.Child) void {
47734752 ev.yield(null, .nothing);
47744753 switch (maybe_sync.cancel_region.errno()) {
47754754 .SUCCESS => return,
4776 .INTR, .CANCELED => continue,
4755 .INTR, .CANCELED => {},
47774756 .CHILD => |err| return errnoBug(err) catch {}, // Double-free.
47784757 else => |err| return unexpectedErrno(err) catch {},
47794758 }
......@@ -4782,15 +4761,15 @@ fn childKill(userdata: ?*anyopaque, child: *process.Child) void {
47824761
47834762fn childCleanup(ev: *Evented, child: *process.Child) void {
47844763 if (child.stdin) |*stdin| {
4785 ev.close(stdin.handle);
4764 ev.closeAsync(stdin.handle);
47864765 child.stdin = null;
47874766 }
47884767 if (child.stdout) |*stdout| {
4789 ev.close(stdout.handle);
4768 ev.closeAsync(stdout.handle);
47904769 child.stdout = null;
47914770 }
47924771 if (child.stderr) |*stderr| {
4793 ev.close(stderr.handle);
4772 ev.closeAsync(stderr.handle);
47944773 child.stderr = null;
47954774 }
47964775 child.id = null;
......@@ -4896,14 +4875,10 @@ fn sleep(userdata: ?*anyopaque, timeout: Io.Timeout) Io.Cancelable!void {
48964875 .resv = 0,
48974876 };
48984877 ev.yield(null, .nothing);
4899 switch (cancel_region.errno()) {
4900 // Handles SUCCESS as well as clock not available and unexpected
4901 // errors. The user had a chance to check clock resolution before
4902 // getting here, which would have reported 0, making this a legal
4903 // amount of time to sleep.
4904 else => return,
4905 .INTR, .CANCELED => return error.Canceled,
4906 }
4878 // Handles SUCCESS as well as clock not available and unexpected
4879 // errors. The user had a chance to check clock resolution before
4880 // getting here, which would have reported 0, making this a legal
4881 // amount of time to sleep.
49074882}
49084883
49094884fn random(userdata: ?*anyopaque, buffer: []u8) void {
......@@ -4980,7 +4955,7 @@ fn netBindIp(
49804955 var maybe_sync: CancelRegion.Sync.Maybe = .{ .cancel_region = .init() };
49814956 defer maybe_sync.deinit(ev);
49824957 const socket_fd = try ev.socket(&maybe_sync.cancel_region, family, options);
4983 errdefer ev.close(socket_fd);
4958 errdefer ev.closeAsync(socket_fd);
49844959 var storage: PosixAddress = undefined;
49854960 var addr_len = addressToPosix(address, &storage);
49864961 try ev.bind(&maybe_sync.cancel_region, socket_fd, &storage.any, addr_len);
......@@ -5147,23 +5122,19 @@ fn netReceive(
51475122 .data = data,
51485123 .control = if (msg.control) |ptr| @as([*]u8, @ptrCast(ptr))[0..msg.controllen] else message.control,
51495124 .flags = .{
5150 .eor = (msg.flags & linux.MSG.EOR) != 0,
5151 .trunc = (msg.flags & linux.MSG.TRUNC) != 0,
5152 .ctrunc = (msg.flags & linux.MSG.CTRUNC) != 0,
5153 .oob = (msg.flags & linux.MSG.OOB) != 0,
5154 .errqueue = if (@hasDecl(linux.MSG, "ERRQUEUE")) (msg.flags & linux.MSG.ERRQUEUE) != 0 else false,
5125 .eor = msg.flags & linux.MSG.EOR != 0,
5126 .trunc = msg.flags & linux.MSG.TRUNC != 0,
5127 .ctrunc = msg.flags & linux.MSG.CTRUNC != 0,
5128 .oob = msg.flags & linux.MSG.OOB != 0,
5129 .errqueue = msg.flags & linux.MSG.ERRQUEUE != 0,
51555130 },
51565131 };
51575132 message_i += 1;
51585133 continue;
51595134 },
51605135 .AGAIN => unreachable,
5161 .INTR, .CANCELED => {
5162 if (deadline) |d| {
5163 if (now(ev, d.clock).nanoseconds >= d.raw.nanoseconds) return .{ error.Timeout, message_i };
5164 }
5165 continue;
5166 },
5136 .INTR, .CANCELED => if (deadline) |d| if (now(ev, d.clock).nanoseconds >= d.raw.nanoseconds)
5137 return .{ error.Timeout, message_i },
51675138
51685139 .BADF => |err| return .{ errnoBug(err), message_i },
51695140 .NFILE => return .{ error.SystemFdQuotaExceeded, message_i },
......@@ -5266,7 +5237,7 @@ fn netShutdown(
52665237 ev.yield(null, .nothing);
52675238 switch (cancel_region.errno()) {
52685239 .SUCCESS => return,
5269 .INTR, .CANCELED => continue,
5240 .INTR, .CANCELED => {},
52705241 .BADF, .NOTSOCK, .INVAL => |err| return errnoBug(err),
52715242 .NOTCONN => return error.SocketUnconnected,
52725243 .NOBUFS => return error.SystemResources,
......@@ -5336,7 +5307,7 @@ fn bind(
53365307 ev.yield(null, .nothing);
53375308 switch (cancel_region.errno()) {
53385309 .SUCCESS => return,
5339 .INTR, .CANCELED => continue,
5310 .INTR, .CANCELED => {},
53405311 .ADDRINUSE => return error.AddressInUse,
53415312 .BADF => |err| return errnoBug(err), // File descriptor used after closed.
53425313 .INVAL => |err| return errnoBug(err), // invalid parameters
......@@ -5350,13 +5321,12 @@ fn bind(
53505321 }
53515322}
53525323
5353fn chdir(ev: *Evented, sync: *CancelRegion.Sync, path: [*:0]const u8) ChdirError!void {
5354 _ = ev;
5324fn chdir(sync: *CancelRegion.Sync, path: [*:0]const u8) ChdirError!void {
53555325 while (true) {
53565326 try sync.cancel_region.await(.nothing);
53575327 switch (linux.errno(linux.chdir(path))) {
53585328 .SUCCESS => return,
5359 .INTR => continue,
5329 .INTR => {},
53605330 .ACCES => return error.AccessDenied,
53615331 .IO => return error.FileSystem,
53625332 .LOOP => return error.SymLinkLoop,
......@@ -5372,6 +5342,36 @@ fn chdir(ev: *Evented, sync: *CancelRegion.Sync, path: [*:0]const u8) ChdirError
53725342}
53735343
53745344fn close(ev: *Evented, fd: fd_t) void {
5345 var cancel_region: CancelRegion = .initBlocked();
5346 defer cancel_region.deinit();
5347 const thread = cancel_region.awaitIoUring() catch |err| switch (err) {
5348 error.Canceled => unreachable, // blocked
5349 };
5350 thread.enqueue().* = .{
5351 .opcode = .CLOSE,
5352 .flags = 0,
5353 .ioprio = 0,
5354 .fd = fd,
5355 .off = 0,
5356 .addr = 0,
5357 .len = 0,
5358 .rw_flags = 0,
5359 .user_data = @intFromPtr(cancel_region.fiber),
5360 .buf_index = 0,
5361 .personality = 0,
5362 .splice_fd_in = 0,
5363 .addr3 = 0,
5364 .resv = 0,
5365 };
5366 ev.yield(null, .nothing);
5367 switch (cancel_region.errno()) {
5368 .BADF => recoverableOsBugDetected(), // Always a race condition.
5369 .INTR => {}, // This is still a success. See https://github.com/ziglang/zig/issues/2425
5370 else => {},
5371 }
5372}
5373
5374fn closeAsync(ev: *Evented, fd: fd_t) void {
53755375 _ = ev;
53765376 const thread: *Thread = .current();
53775377 thread.enqueue().* = .{
......@@ -5392,14 +5392,13 @@ fn close(ev: *Evented, fd: fd_t) void {
53925392 };
53935393}
53945394
5395fn fchdir(ev: *Evented, sync: *CancelRegion.Sync, dir: fd_t) process.SetCurrentDirError!void {
5396 _ = ev;
5395fn fchdir(sync: *CancelRegion.Sync, dir: fd_t) process.SetCurrentDirError!void {
53975396 if (dir == linux.AT.FDCWD) return;
53985397 while (true) {
53995398 try sync.cancel_region.await(.nothing);
54005399 switch (linux.errno(linux.fchdir(dir))) {
54015400 .SUCCESS => return,
5402 .INTR => continue,
5401 .INTR => {},
54035402 .ACCES => return error.AccessDenied,
54045403 .NOTDIR => return error.NotDir,
54055404 .IO => return error.FileSystem,
......@@ -5422,7 +5421,7 @@ fn fchmodat(
54225421 try sync.cancel_region.await(.nothing);
54235422 switch (linux.errno(linux.fchmodat2(dir, path, mode, flags))) {
54245423 .SUCCESS => return,
5425 .INTR => continue,
5424 .INTR => {},
54265425 .BADF => |err| return errnoBug(err),
54275426 .FAULT => |err| return errnoBug(err),
54285427 .INVAL => |err| return errnoBug(err),
......@@ -5454,7 +5453,7 @@ fn fchownat(
54545453 try sync.cancel_region.await(.nothing);
54555454 switch (linux.errno(linux.fchownat(dir, path, owner, group, flags))) {
54565455 .SUCCESS => return,
5457 .INTR => continue,
5456 .INTR => {},
54585457 .BADF => |err| return errnoBug(err), // likely fd refers to directory opened without `Dir.OpenOptions.iterate`
54595458 .FAULT => |err| return errnoBug(err),
54605459 .INVAL => |err| return errnoBug(err),
......@@ -5486,7 +5485,7 @@ fn flock(
54865485 .exclusive => LOCK.EX,
54875486 })))) {
54885487 .SUCCESS => return,
5489 .INTR => continue,
5488 .INTR => {},
54905489 .BADF => |err| return errnoBug(err),
54915490 .INVAL => |err| return errnoBug(err), // invalid parameters
54925491 .NOLCK => return error.SystemResources,
......@@ -5536,7 +5535,7 @@ fn getsockname(
55365535 try sync.cancel_region.await(.nothing);
55375536 switch (linux.errno(linux.getsockname(socket_fd, addr, addr_len))) {
55385537 .SUCCESS => return,
5539 .INTR => continue,
5538 .INTR => {},
55405539 .BADF => |err| return errnoBug(err), // File descriptor used after closed.
55415540 .FAULT => |err| return errnoBug(err),
55425541 .INVAL => |err| return errnoBug(err), // invalid parameters
......@@ -5577,7 +5576,7 @@ fn linkat(
55775576 ev.yield(null, .nothing);
55785577 switch (cancel_region.errno()) {
55795578 .SUCCESS => return,
5580 .INTR, .CANCELED => continue,
5579 .INTR, .CANCELED => {},
55815580 .ACCES => return error.AccessDenied,
55825581 .DQUOT => return error.DiskQuota,
55835582 .EXIST => return error.PathAlreadyExists,
......@@ -5617,7 +5616,7 @@ fn lseek(
56175616 8 => linux.lseek(fd, @bitCast(offset), whence),
56185617 })) {
56195618 .SUCCESS => return,
5620 .INTR => continue,
5619 .INTR => {},
56215620 .BADF => |err| return errnoBug(err), // File descriptor used after closed.
56225621 .INVAL => return error.Unseekable,
56235622 .OVERFLOW => return error.Unseekable,
......@@ -5660,7 +5659,7 @@ fn openat(
56605659 const completion = cancel_region.completion();
56615660 switch (completion.errno()) {
56625661 .SUCCESS => return completion.result,
5663 .INTR, .CANCELED => continue,
5662 .INTR, .CANCELED => {},
56645663 .FAULT => |err| return errnoBug(err),
56655664 .INVAL => return error.BadPathName,
56665665 .BADF => |err| return errnoBug(err), // File descriptor used after closed.
......@@ -5722,7 +5721,7 @@ fn preadv(
57225721 const completion = cancel_region.completion();
57235722 switch (completion.errno()) {
57245723 .SUCCESS => return @as(u32, @bitCast(completion.result)),
5725 .INTR, .CANCELED => continue,
5724 .INTR, .CANCELED => {},
57265725 .INVAL => |err| return errnoBug(err),
57275726 .FAULT => |err| return errnoBug(err),
57285727 .AGAIN => return error.WouldBlock,
......@@ -5769,7 +5768,7 @@ fn pwritev(
57695768 const completion = cancel_region.completion();
57705769 switch (completion.errno()) {
57715770 .SUCCESS => return @as(u32, @bitCast(completion.result)),
5772 .INTR, .CANCELED => continue,
5771 .INTR, .CANCELED => {},
57735772 .INVAL => |err| return errnoBug(err),
57745773 .FAULT => |err| return errnoBug(err),
57755774 .AGAIN => return error.WouldBlock,
......@@ -5819,7 +5818,7 @@ fn realPath(
58195818 const rc = linux.readlink(proc_path, out_buffer.ptr, out_buffer.len);
58205819 switch (linux.errno(rc)) {
58215820 .SUCCESS => return rc,
5822 .INTR => continue,
5821 .INTR => {},
58235822 .ACCES => return error.AccessDenied,
58245823 .FAULT => |err| return errnoBug(err),
58255824 .IO => return error.FileSystem,
......@@ -5864,7 +5863,7 @@ fn renameat(
58645863 ev.yield(null, .nothing);
58655864 switch (cancel_region.errno()) {
58665865 .SUCCESS => return,
5867 .INTR, .CANCELED => continue,
5866 .INTR, .CANCELED => {},
58685867 .ACCES => return error.AccessDenied,
58695868 .PERM => return error.PermissionDenied,
58705869 .BUSY => return error.FileBusy,
......@@ -5931,7 +5930,7 @@ fn setsockopt(
59315930 ev.yield(null, .nothing);
59325931 switch (cancel_region.errno()) {
59335932 .SUCCESS => return,
5934 .INTR, .CANCELED => continue,
5933 .INTR, .CANCELED => {},
59355934 .BADF => |err| return errnoBug(err), // File descriptor used after closed.
59365935 .NOTSOCK => |err| return errnoBug(err),
59375936 .INVAL => |err| return errnoBug(err),
......@@ -5982,7 +5981,7 @@ fn socket(
59825981 const completion = cancel_region.completion();
59835982 switch (completion.errno()) {
59845983 .SUCCESS => break completion.result,
5985 .INTR, .CANCELED => continue,
5984 .INTR, .CANCELED => {},
59865985 .AFNOSUPPORT => return error.AddressFamilyUnsupported,
59875986 .INVAL => return error.ProtocolUnsupportedBySystem,
59885987 .MFILE => return error.ProcessFdQuotaExceeded,
......@@ -5994,7 +5993,7 @@ fn socket(
59945993 else => |err| return unexpectedErrno(err),
59955994 }
59965995 };
5997 errdefer ev.close(socket_fd);
5996 errdefer ev.closeAsync(socket_fd);
59985997
59995998 if (options.ip6_only) {
60005999 if (linux.IPV6 == void) return error.OptionUnsupported;
......@@ -6044,7 +6043,7 @@ fn statx(
60446043 ev.yield(null, .nothing);
60456044 switch (cancel_region.errno()) {
60466045 .SUCCESS => return statFromLinux(&statx_buf),
6047 .INTR, .CANCELED => continue,
6046 .INTR, .CANCELED => {},
60486047 .ACCES => return error.AccessDenied,
60496048 .BADF => |err| return errnoBug(err), // File descriptor used after closed.
60506049 .FAULT => |err| return errnoBug(err),
......@@ -6083,7 +6082,7 @@ fn utimensat(
60836082 try sync.cancel_region.await(.nothing);
60846083 switch (linux.errno(linux.utimensat(dir, path, times, flags))) {
60856084 .SUCCESS => return,
6086 .INTR => continue,
6085 .INTR => {},
60876086 .BADF => |err| return errnoBug(err), // always a race condition
60886087 .FAULT => |err| return errnoBug(err),
60896088 .INVAL => |err| return errnoBug(err),
......@@ -6095,19 +6094,33 @@ fn utimensat(
60956094 }
60966095}
60976096
6098fn writeAll(
6099 ev: *Evented,
6100 cancel_region: *CancelRegion,
6101 fd: fd_t,
6102 buffer: []const u8,
6103) (File.Writer.Error || error{EndOfStream})!void {
6097fn writeAllSync(sync: *CancelRegion.Sync, fd: fd_t, buffer: []const u8) File.Writer.Error!void {
61046098 var index: usize = 0;
6105 while (buffer.len - index != 0) {
6106 const len = try ev.pwritev(cancel_region, fd, &.{
6107 .{ .base = buffer[index..].ptr, .len = buffer.len - index },
6108 }, null);
6109 if (len == 0) return error.EndOfStream;
6110 index += len;
6099 while (buffer.len - index != 0) index += try writeSync(sync, fd, buffer[index..]);
6100}
6101
6102fn writeSync(sync: *CancelRegion.Sync, fd: fd_t, buffer: []const u8) File.Writer.Error!usize {
6103 while (true) {
6104 try sync.cancel_region.await(.nothing);
6105 const rc = linux.write(fd, buffer.ptr, buffer.len);
6106 switch (linux.errno(rc)) {
6107 .SUCCESS => return @intCast(rc),
6108 .INTR => {},
6109 .INVAL => |err| return errnoBug(err),
6110 .FAULT => |err| return errnoBug(err),
6111 .AGAIN => return error.WouldBlock,
6112 .BADF => return error.NotOpenForWriting, // Can be a race condition.
6113 .DESTADDRREQ => |err| return errnoBug(err), // `connect` was never called.
6114 .DQUOT => return error.DiskQuota,
6115 .FBIG => return error.FileTooBig,
6116 .IO => return error.InputOutput,
6117 .NOSPC => return error.NoSpaceLeft,
6118 .PERM => return error.PermissionDenied,
6119 .PIPE => return error.BrokenPipe,
6120 .CONNRESET => |err| return errnoBug(err), // Not a socket handle.
6121 .BUSY => return error.DeviceBusy,
6122 else => |err| return unexpectedErrno(err),
6123 }
61116124 }
61126125}
61136126
lib/std/os/linux/IoUring.zig+4
......@@ -201,6 +201,10 @@ pub fn enter(self: *IoUring, to_submit: u32, min_complete: u32, flags: u32) !u32
201201 // The kernel believes our `self.fd` does not refer to an io_uring instance,
202202 // or the opcode is valid but not supported by this kernel (more likely):
203203 .OPNOTSUPP => return error.OpcodeNotSupported,
204 // The thread submitting the work is invalid. This may occur if IORING_ENTER_GETEVENTS
205 // and IORING_SETUP_DEFER_TASKRUN is set, but the submitting thread is not the thread
206 // that initially created or enabled the io_uring associated with fd.
207 .EXIST => return error.InvalidThread,
204208 // The operation was interrupted by a delivery of a signal before it could complete.
205209 // This can happen while waiting for events with IORING_ENTER_GETEVENTS:
206210 .INTR => return error.SignalInterrupt,