| ... | ... | @@ -150,7 +150,6 @@ const Thread = struct { |
| 150 | 150 | const Fiber = struct { |
| 151 | 151 | required_align: void align(4), |
| 152 | 152 | context: Io.fiber.Context, |
| 153 | | await_count: i32, |
| 154 | 153 | link: union { |
| 155 | 154 | awaiter: ?*Fiber, |
| 156 | 155 | group: struct { prev: ?*Fiber, next: ?*Fiber }, |
| ... | ... | @@ -856,7 +855,6 @@ pub fn init(ev: *Evented, backing_allocator: Allocator, options: InitOptions) !v |
| 856 | 855 | main_fiber.* = .{ |
| 857 | 856 | .required_align = {}, |
| 858 | 857 | .context = undefined, |
| 859 | | .await_count = 0, |
| 860 | 858 | .link = .{ .awaiter = null }, |
| 861 | 859 | .status = .{ .queue_next = null }, |
| 862 | 860 | .cancel_status = .unrequested, |
| ... | ... | @@ -1260,7 +1258,7 @@ const SwitchMessage = struct { |
| 1260 | 1258 | const PendingTask = union(enum) { |
| 1261 | 1259 | nothing, |
| 1262 | 1260 | reschedule, |
| 1263 | | await: u31, |
| 1261 | await: *Fiber, |
| 1264 | 1262 | group_await: Group, |
| 1265 | 1263 | group_cancel: Group, |
| 1266 | 1264 | batch_await: *Io.Batch, |
| ... | ... | @@ -1284,10 +1282,11 @@ const SwitchMessage = struct { |
| 1284 | 1282 | assert(fiber.status.queue_next == null); |
| 1285 | 1283 | _ = ev.schedule(thread, .{ .head = fiber, .tail = fiber }); |
| 1286 | 1284 | }, |
| 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 }); |
| 1291 | 1290 | }, |
| 1292 | 1291 | .group_await => |group| { |
| 1293 | 1292 | const fiber: *Fiber = @alignCast(@fieldParentPtr("context", message.contexts.old)); |
| ... | ... | @@ -1361,7 +1360,7 @@ fn crashHandler(userdata: ?*anyopaque) void { |
| 1361 | 1360 | } |
| 1362 | 1361 | |
| 1363 | 1362 | const AsyncClosure = struct { |
| 1364 | | ev: *Evented, |
| 1363 | evented: *Evented, |
| 1365 | 1364 | fiber: *Fiber, |
| 1366 | 1365 | start: *const fn (context: *const anyopaque, result: *anyopaque) void, |
| 1367 | 1366 | result_align: Alignment, |
| ... | ... | @@ -1398,16 +1397,11 @@ const AsyncClosure = struct { |
| 1398 | 1397 | closure: *AsyncClosure, |
| 1399 | 1398 | message: *const SwitchMessage, |
| 1400 | 1399 | ) callconv(.withStackAlign(.c, @alignOf(AsyncClosure))) noreturn { |
| 1401 | | message.handle(closure.ev); |
| 1400 | const ev = closure.evented; |
| 1402 | 1401 | const fiber = closure.fiber; |
| 1402 | message.handle(ev); |
| 1403 | 1403 | 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); |
| 1411 | 1405 | unreachable; // switched to dead fiber |
| 1412 | 1406 | } |
| 1413 | 1407 | }; |
| ... | ... | @@ -1461,7 +1455,6 @@ fn concurrent( |
| 1461 | 1455 | }, |
| 1462 | 1456 | else => |arch| @compileError("unimplemented architecture: " ++ @tagName(arch)), |
| 1463 | 1457 | }, |
| 1464 | | .await_count = 0, |
| 1465 | 1458 | .link = .{ .awaiter = null }, |
| 1466 | 1459 | .status = .{ .queue_next = null }, |
| 1467 | 1460 | .cancel_status = .unrequested, |
| ... | ... | @@ -1479,7 +1472,7 @@ fn concurrent( |
| 1479 | 1472 | }, |
| 1480 | 1473 | }; |
| 1481 | 1474 | closure.* = .{ |
| 1482 | | .ev = ev, |
| 1475 | .evented = ev, |
| 1483 | 1476 | .fiber = fiber, |
| 1484 | 1477 | .start = start, |
| 1485 | 1478 | .result_align = result_alignment, |
| ... | ... | @@ -1498,18 +1491,11 @@ fn await( |
| 1498 | 1491 | result_alignment: Alignment, |
| 1499 | 1492 | ) void { |
| 1500 | 1493 | 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(); |
| 1513 | 1499 | } |
| 1514 | 1500 | |
| 1515 | 1501 | fn cancel( |
| ... | ... | @@ -1726,7 +1712,7 @@ const Group = struct { |
| 1726 | 1712 | } |
| 1727 | 1713 | |
| 1728 | 1714 | const AsyncClosure = struct { |
| 1729 | | ev: *Evented, |
| 1715 | evented: *Evented, |
| 1730 | 1716 | group: Group, |
| 1731 | 1717 | fiber: *Fiber, |
| 1732 | 1718 | start: *const fn (context: *const anyopaque) Io.Cancelable!void, |
| ... | ... | @@ -1765,19 +1751,16 @@ const Group = struct { |
| 1765 | 1751 | closure: *Group.AsyncClosure, |
| 1766 | 1752 | message: *const SwitchMessage, |
| 1767 | 1753 | ) 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; |
| 1773 | 1755 | 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` |
| 1777 | 1760 | } 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 |
| 1779 | 1762 | } |
| 1780 | | ev.yield(group.removeFiber(ev, fiber), .destroy); |
| 1763 | ev.yield(closure.group.removeFiber(ev, fiber), .destroy); |
| 1781 | 1764 | unreachable; // switched to dead fiber |
| 1782 | 1765 | } |
| 1783 | 1766 | }; |
| ... | ... | @@ -1845,7 +1828,6 @@ fn groupConcurrent( |
| 1845 | 1828 | }, |
| 1846 | 1829 | else => |arch| @compileError("unimplemented architecture: " ++ @tagName(arch)), |
| 1847 | 1830 | }, |
| 1848 | | .await_count = 0, |
| 1849 | 1831 | .link = .{ .group = .{ .prev = null, .next = null } }, |
| 1850 | 1832 | .status = .{ .queue_next = null }, |
| 1851 | 1833 | .cancel_status = .unrequested, |
| ... | ... | @@ -1863,7 +1845,7 @@ fn groupConcurrent( |
| 1863 | 1845 | }, |
| 1864 | 1846 | }; |
| 1865 | 1847 | closure.* = .{ |
| 1866 | | .ev = ev, |
| 1848 | .evented = ev, |
| 1867 | 1849 | .group = group, |
| 1868 | 1850 | .fiber = fiber, |
| 1869 | 1851 | .start = start, |
| ... | ... | @@ -2173,7 +2155,7 @@ fn deviceIoControl( |
| 2173 | 2155 | const rc = linux.ioctl(o.file.handle, @bitCast(o.code), @intFromPtr(o.arg)); |
| 2174 | 2156 | switch (linux.errno(rc)) { |
| 2175 | 2157 | .SUCCESS => return @bitCast(@as(u32, @truncate(rc))), |
| 2176 | | .INTR => continue, |
| 2158 | .INTR => {}, |
| 2177 | 2159 | else => |err| return -@as(i32, @intFromEnum(err)), |
| 2178 | 2160 | } |
| 2179 | 2161 | } |
| ... | ... | @@ -2571,7 +2553,7 @@ fn dirCreateDir( |
| 2571 | 2553 | ev.yield(null, .nothing); |
| 2572 | 2554 | switch (cancel_region.errno()) { |
| 2573 | 2555 | .SUCCESS => return, |
| 2574 | | .INTR, .CANCELED => continue, |
| 2556 | .INTR, .CANCELED => {}, |
| 2575 | 2557 | .ACCES => return error.AccessDenied, |
| 2576 | 2558 | .BADF => |err| return errnoBug(err), // File descriptor used after closed. |
| 2577 | 2559 | .PERM => return error.PermissionDenied, |
| ... | ... | @@ -2654,7 +2636,7 @@ fn filePathKind(ev: *Evented, dir: Dir, sub_path: []const u8) !File.Kind { |
| 2654 | 2636 | if (!statx_buf.mask.TYPE) return error.Unexpected; |
| 2655 | 2637 | return statxKind(statx_buf.mode); |
| 2656 | 2638 | }, |
| 2657 | | .INTR, .CANCELED => continue, |
| 2639 | .INTR, .CANCELED => {}, |
| 2658 | 2640 | .ACCES => |err| return errnoBug(err), |
| 2659 | 2641 | .BADF => |err| return errnoBug(err), // File descriptor used after closed. |
| 2660 | 2642 | .FAULT => |err| return errnoBug(err), |
| ... | ... | @@ -2767,7 +2749,7 @@ fn dirAccess( |
| 2767 | 2749 | try sync.cancel_region.await(.nothing); |
| 2768 | 2750 | switch (linux.errno(linux.faccessat(dir.handle, sub_path_posix, mode, flags))) { |
| 2769 | 2751 | .SUCCESS => return, |
| 2770 | | .INTR => continue, |
| 2752 | .INTR => {}, |
| 2771 | 2753 | .ACCES => return error.AccessDenied, |
| 2772 | 2754 | .PERM => return error.PermissionDenied, |
| 2773 | 2755 | .ROFS => return error.ReadOnlyFileSystem, |
| ... | ... | @@ -2806,7 +2788,7 @@ fn dirCreateFile( |
| 2806 | 2788 | .EXCL = flags.exclusive, |
| 2807 | 2789 | .CLOEXEC = true, |
| 2808 | 2790 | }, flags.permissions.toMode()); |
| 2809 | | errdefer ev.close(fd); |
| 2791 | errdefer ev.closeAsync(fd); |
| 2810 | 2792 | |
| 2811 | 2793 | switch (flags.lock) { |
| 2812 | 2794 | .none => {}, |
| ... | ... | @@ -2994,7 +2976,7 @@ fn dirOpenFile( |
| 2994 | 2976 | .CLOEXEC = true, |
| 2995 | 2977 | .PATH = flags.path_only, |
| 2996 | 2978 | }, 0); |
| 2997 | | errdefer ev.close(fd); |
| 2979 | errdefer ev.closeAsync(fd); |
| 2998 | 2980 | |
| 2999 | 2981 | if (!flags.allow_directory) { |
| 3000 | 2982 | const is_dir = is_dir: { |
| ... | ... | @@ -3048,7 +3030,7 @@ fn dirRead(userdata: ?*anyopaque, dr: *Dir.Reader, buffer: []Dir.Entry) Dir.Read |
| 3048 | 3030 | const rc = linux.getdents64(dr.dir.handle, dr.buffer.ptr, dr.buffer.len); |
| 3049 | 3031 | switch (linux.errno(rc)) { |
| 3050 | 3032 | .SUCCESS => break rc, |
| 3051 | | .INTR => continue, |
| 3033 | .INTR => {}, |
| 3052 | 3034 | .BADF => |err| return errnoBug(err), // Dir is invalid or was opened without iteration ability. |
| 3053 | 3035 | .FAULT => |err| return errnoBug(err), |
| 3054 | 3036 | .NOTDIR => |err| return errnoBug(err), |
| ... | ... | @@ -3141,7 +3123,7 @@ fn dirRealPathFile( |
| 3141 | 3123 | error.FileLocksUnsupported => return errnoBug(.OPNOTSUPP), // Not asking for locks. |
| 3142 | 3124 | else => |e| return e, |
| 3143 | 3125 | }; |
| 3144 | | defer ev.close(fd); |
| 3126 | defer ev.closeAsync(fd); |
| 3145 | 3127 | return ev.realPath(try maybe_sync.enterSync(ev), fd, out_buffer); |
| 3146 | 3128 | } |
| 3147 | 3129 | |
| ... | ... | @@ -3174,7 +3156,7 @@ fn dirDeleteFile(userdata: ?*anyopaque, dir: Dir, sub_path: []const u8) Dir.Dele |
| 3174 | 3156 | ev.yield(null, .nothing); |
| 3175 | 3157 | switch (cancel_region.errno()) { |
| 3176 | 3158 | .SUCCESS => return, |
| 3177 | | .INTR, .CANCELED => continue, |
| 3159 | .INTR, .CANCELED => {}, |
| 3178 | 3160 | .PERM => return error.PermissionDenied, |
| 3179 | 3161 | .ACCES => return error.AccessDenied, |
| 3180 | 3162 | .BUSY => return error.FileBusy, |
| ... | ... | @@ -3226,7 +3208,7 @@ fn dirDeleteDir(userdata: ?*anyopaque, dir: Dir, sub_path: []const u8) Dir.Delet |
| 3226 | 3208 | ev.yield(null, .nothing); |
| 3227 | 3209 | switch (cancel_region.errno()) { |
| 3228 | 3210 | .SUCCESS => return, |
| 3229 | | .INTR, .CANCELED => continue, |
| 3211 | .INTR, .CANCELED => {}, |
| 3230 | 3212 | .ACCES => return error.AccessDenied, |
| 3231 | 3213 | .PERM => return error.PermissionDenied, |
| 3232 | 3214 | .BUSY => return error.FileBusy, |
| ... | ... | @@ -3342,7 +3324,7 @@ fn dirSymLink( |
| 3342 | 3324 | ev.yield(null, .nothing); |
| 3343 | 3325 | switch (cancel_region.errno()) { |
| 3344 | 3326 | .SUCCESS => return, |
| 3345 | | .INTR, .CANCELED => continue, |
| 3327 | .INTR, .CANCELED => {}, |
| 3346 | 3328 | .FAULT => |err| return errnoBug(err), |
| 3347 | 3329 | .INVAL => |err| return errnoBug(err), |
| 3348 | 3330 | .ACCES => return error.AccessDenied, |
| ... | ... | @@ -3381,7 +3363,7 @@ fn dirReadLink( |
| 3381 | 3363 | const rc = linux.readlinkat(dir.handle, sub_path_posix, buffer.ptr, buffer.len); |
| 3382 | 3364 | switch (linux.errno(rc)) { |
| 3383 | 3365 | .SUCCESS => return @bitCast(rc), |
| 3384 | | .INTR => continue, |
| 3366 | .INTR => {}, |
| 3385 | 3367 | .ACCES => return error.AccessDenied, |
| 3386 | 3368 | .FAULT => |err| return errnoBug(err), |
| 3387 | 3369 | .INVAL => return error.NotLink, |
| ... | ... | @@ -3571,7 +3553,7 @@ fn fileLength(userdata: ?*anyopaque, file: File) File.LengthError!u64 { |
| 3571 | 3553 | if (!statx_buf.mask.SIZE) return error.Unexpected; |
| 3572 | 3554 | return statx_buf.size; |
| 3573 | 3555 | }, |
| 3574 | | .INTR, .CANCELED => continue, |
| 3556 | .INTR, .CANCELED => {}, |
| 3575 | 3557 | .ACCES => |err| return errnoBug(err), |
| 3576 | 3558 | .BADF => |err| return errnoBug(err), // File descriptor used after closed. |
| 3577 | 3559 | .FAULT => |err| return errnoBug(err), |
| ... | ... | @@ -3754,7 +3736,7 @@ fn fileSync(userdata: ?*anyopaque, file: File) File.SyncError!void { |
| 3754 | 3736 | ev.yield(null, .nothing); |
| 3755 | 3737 | switch (cancel_region.errno()) { |
| 3756 | 3738 | .SUCCESS => return, |
| 3757 | | .INTR, .CANCELED => continue, |
| 3739 | .INTR, .CANCELED => {}, |
| 3758 | 3740 | .BADF => |err| return errnoBug(err), |
| 3759 | 3741 | .INVAL => |err| return errnoBug(err), |
| 3760 | 3742 | .ROFS => |err| return errnoBug(err), |
| ... | ... | @@ -3776,7 +3758,7 @@ fn fileIsTty(userdata: ?*anyopaque, file: File) Io.Cancelable!bool { |
| 3776 | 3758 | const rc = linux.ioctl(file.handle, linux.T.IOCGWINSZ, @intFromPtr(&wsz)); |
| 3777 | 3759 | switch (linux.errno(rc)) { |
| 3778 | 3760 | .SUCCESS => return true, |
| 3779 | | .INTR => continue, |
| 3761 | .INTR => {}, |
| 3780 | 3762 | else => return false, |
| 3781 | 3763 | } |
| 3782 | 3764 | } |
| ... | ... | @@ -3812,7 +3794,7 @@ fn fileSetLength(userdata: ?*anyopaque, file: File, length: u64) File.SetLengthE |
| 3812 | 3794 | ev.yield(null, .nothing); |
| 3813 | 3795 | switch (cancel_region.errno()) { |
| 3814 | 3796 | .SUCCESS => return, |
| 3815 | | .INTR, .CANCELED => continue, |
| 3797 | .INTR, .CANCELED => {}, |
| 3816 | 3798 | .FBIG => return error.FileTooBig, |
| 3817 | 3799 | .IO => return error.InputOutput, |
| 3818 | 3800 | .PERM => return error.PermissionDenied, |
| ... | ... | @@ -3994,7 +3976,7 @@ fn fileMemoryMapCreate( |
| 3994 | 3976 | const rc = linux.mmap(null, options.len, prot, flags, file.handle, casted_offset); |
| 3995 | 3977 | switch (linux.errno(rc)) { |
| 3996 | 3978 | .SUCCESS => break @as([*]align(page_align) u8, @ptrFromInt(rc))[0..options.len], |
| 3997 | | .INTR => continue, |
| 3979 | .INTR => {}, |
| 3998 | 3980 | .ACCES => return error.AccessDenied, |
| 3999 | 3981 | .AGAIN => return error.LockedMemoryLimitExceeded, |
| 4000 | 3982 | .MFILE => return error.ProcessFdQuotaExceeded, |
| ... | ... | @@ -4054,7 +4036,7 @@ fn fileMemoryMapSetLength( |
| 4054 | 4036 | const rc = linux.mremap(old_memory.ptr, old_memory.len, new_len, flags, addr_hint); |
| 4055 | 4037 | switch (linux.errno(rc)) { |
| 4056 | 4038 | .SUCCESS => break @as([*]align(page_align) u8, @ptrFromInt(rc))[0..new_len], |
| 4057 | | .INTR => continue, |
| 4039 | .INTR => {}, |
| 4058 | 4040 | .AGAIN => return error.LockedMemoryLimitExceeded, |
| 4059 | 4041 | .NOMEM => return error.OutOfMemory, |
| 4060 | 4042 | .INVAL => |err| return errnoBug(err), |
| ... | ... | @@ -4163,7 +4145,7 @@ fn processCurrentPath(userdata: ?*anyopaque, buffer: []u8) process.CurrentPathEr |
| 4163 | 4145 | try sync.cancel_region.await(.nothing); |
| 4164 | 4146 | switch (linux.errno(linux.getcwd(buffer.ptr, buffer.len))) { |
| 4165 | 4147 | .SUCCESS => return std.mem.findScalar(u8, buffer, 0).?, |
| 4166 | | .INTR => continue, |
| 4148 | .INTR => {}, |
| 4167 | 4149 | .NOENT => return error.CurrentDirUnlinked, |
| 4168 | 4150 | .RANGE => return error.NameTooLong, |
| 4169 | 4151 | .FAULT => |err| return errnoBug(err), |
| ... | ... | @@ -4178,7 +4160,7 @@ fn processSetCurrentDir(userdata: ?*anyopaque, dir: Dir) process.SetCurrentDirEr |
| 4178 | 4160 | if (dir.handle == linux.AT.FDCWD) return; |
| 4179 | 4161 | var sync: CancelRegion.Sync = try .init(ev); |
| 4180 | 4162 | defer sync.deinit(ev); |
| 4181 | | return ev.fchdir(&sync, dir.handle); |
| 4163 | return fchdir(&sync, dir.handle); |
| 4182 | 4164 | } |
| 4183 | 4165 | |
| 4184 | 4166 | fn processSetCurrentPath(userdata: ?*anyopaque, dir_path: []const u8) ChdirError!void { |
| ... | ... | @@ -4215,7 +4197,7 @@ fn processReplace(userdata: ?*anyopaque, options: process.ReplaceOptions) proces |
| 4215 | 4197 | |
| 4216 | 4198 | var sync: CancelRegion.Sync = try .init(ev); |
| 4217 | 4199 | 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); |
| 4219 | 4201 | } |
| 4220 | 4202 | |
| 4221 | 4203 | fn processReplacePath( |
| ... | ... | @@ -4235,7 +4217,7 @@ fn processSpawn(userdata: ?*anyopaque, options: process.SpawnOptions) process.Sp |
| 4235 | 4217 | const spawned = try ev.spawn(options); |
| 4236 | 4218 | var cancel_region: CancelRegion = .initBlocked(); |
| 4237 | 4219 | defer cancel_region.deinit(); |
| 4238 | | defer ev.close(spawned.err_fd); |
| 4220 | defer ev.closeAsync(spawned.err_fd); |
| 4239 | 4221 | |
| 4240 | 4222 | // Wait for the child to report any errors in or before `execvpe`. |
| 4241 | 4223 | var child_err: ForkBailError = undefined; |
| ... | ... | @@ -4377,8 +4359,9 @@ fn spawn(ev: *Evented, options: process.SpawnOptions) process.SpawnError!Spawned |
| 4377 | 4359 | |
| 4378 | 4360 | if (pid_result == 0) { |
| 4379 | 4361 | defer comptime unreachable; // We are the child. |
| 4362 | // Note that the parent uring is no longer accessible, so we must no longer reference `ev`. |
| 4380 | 4363 | var sync: CancelRegion.Sync = .{ .cancel_region = .initBlocked() }; |
| 4381 | | const err = ev.setUpChild(&sync, .{ |
| 4364 | const err = setUpChild(&sync, .{ |
| 4382 | 4365 | .stdin_pipe = stdin_pipe[0], |
| 4383 | 4366 | .stdout_pipe = stdout_pipe[1], |
| 4384 | 4367 | .stderr_pipe = stderr_pipe[1], |
| ... | ... | @@ -4389,7 +4372,7 @@ fn spawn(ev: *Evented, options: process.SpawnOptions) process.SpawnError!Spawned |
| 4389 | 4372 | .PATH = PATH, |
| 4390 | 4373 | .spawn = options, |
| 4391 | 4374 | }); |
| 4392 | | ev.writeAll(&sync.cancel_region, err_pipe[1], @ptrCast(&err)) catch {}; |
| 4375 | writeAllSync(&sync, err_pipe[1], @ptrCast(&err)) catch {}; |
| 4393 | 4376 | const exit = if (builtin.single_threaded) linux.exit else linux.exit_group; |
| 4394 | 4377 | exit(1); |
| 4395 | 4378 | } |
| ... | ... | @@ -4397,13 +4380,13 @@ fn spawn(ev: *Evented, options: process.SpawnOptions) process.SpawnError!Spawned |
| 4397 | 4380 | const pid: pid_t = @intCast(pid_result); // We are the parent. |
| 4398 | 4381 | errdefer comptime unreachable; // The child is forked; we must not error from now on |
| 4399 | 4382 | |
| 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 |
| 4401 | 4384 | |
| 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]); |
| 4405 | 4388 | |
| 4406 | | if (prog_pipe[1] != -1) ev.close(prog_pipe[1]); |
| 4389 | if (prog_pipe[1] != -1) ev.closeAsync(prog_pipe[1]); |
| 4407 | 4390 | |
| 4408 | 4391 | options.progress_node.setIpcFile(ev, .{ .handle = prog_pipe[0], .flags = .{ .nonblocking = true } }); |
| 4409 | 4392 | |
| ... | ... | @@ -4440,14 +4423,14 @@ pub fn pipe2(flags: linux.O) PipeError![2]fd_t { |
| 4440 | 4423 | } |
| 4441 | 4424 | } |
| 4442 | 4425 | fn 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]); |
| 4445 | 4428 | } |
| 4446 | 4429 | |
| 4447 | 4430 | /// Errors that can occur between fork() and execv() |
| 4448 | 4431 | const ForkBailError = process.SetCurrentDirError || ChdirError || |
| 4449 | 4432 | process.SpawnError || process.ReplaceError; |
| 4450 | | fn setUpChild(ev: *Evented, sync: *CancelRegion.Sync, options: struct { |
| 4433 | fn setUpChild(sync: *CancelRegion.Sync, options: struct { |
| 4451 | 4434 | stdin_pipe: fd_t, |
| 4452 | 4435 | stdout_pipe: fd_t, |
| 4453 | 4436 | stderr_pipe: fd_t, |
| ... | ... | @@ -4458,21 +4441,21 @@ fn setUpChild(ev: *Evented, sync: *CancelRegion.Sync, options: struct { |
| 4458 | 4441 | PATH: []const u8, |
| 4459 | 4442 | spawn: process.SpawnOptions, |
| 4460 | 4443 | }) ForkBailError { |
| 4461 | | try ev.setUpChildIo( |
| 4444 | try setUpChildIo( |
| 4462 | 4445 | sync, |
| 4463 | 4446 | options.spawn.stdin, |
| 4464 | 4447 | options.stdin_pipe, |
| 4465 | 4448 | linux.STDIN_FILENO, |
| 4466 | 4449 | options.dev_null_fd, |
| 4467 | 4450 | ); |
| 4468 | | try ev.setUpChildIo( |
| 4451 | try setUpChildIo( |
| 4469 | 4452 | sync, |
| 4470 | 4453 | options.spawn.stdout, |
| 4471 | 4454 | options.stdout_pipe, |
| 4472 | 4455 | linux.STDOUT_FILENO, |
| 4473 | 4456 | options.dev_null_fd, |
| 4474 | 4457 | ); |
| 4475 | | try ev.setUpChildIo( |
| 4458 | try setUpChildIo( |
| 4476 | 4459 | sync, |
| 4477 | 4460 | options.spawn.stderr, |
| 4478 | 4461 | options.stderr_pipe, |
| ... | ... | @@ -4482,17 +4465,17 @@ fn setUpChild(ev: *Evented, sync: *CancelRegion.Sync, options: struct { |
| 4482 | 4465 | |
| 4483 | 4466 | switch (options.spawn.cwd) { |
| 4484 | 4467 | .inherit => {}, |
| 4485 | | .dir => |cwd_dir| try ev.fchdir(sync, cwd_dir.handle), |
| 4468 | .dir => |cwd_dir| try fchdir(sync, cwd_dir.handle), |
| 4486 | 4469 | .path => |cwd_path| { |
| 4487 | 4470 | var cwd_path_buffer: [PATH_MAX]u8 = undefined; |
| 4488 | 4471 | 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); |
| 4490 | 4473 | }, |
| 4491 | 4474 | } |
| 4492 | 4475 | |
| 4493 | 4476 | // Must happen after fchdir above, the cwd file descriptor might be |
| 4494 | 4477 | // 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); |
| 4496 | 4479 | |
| 4497 | 4480 | if (options.spawn.gid) |gid| { |
| 4498 | 4481 | switch (linux.errno(linux.setregid(gid, gid))) { |
| ... | ... | @@ -4532,7 +4515,7 @@ fn setUpChild(ev: *Evented, sync: *CancelRegion.Sync, options: struct { |
| 4532 | 4515 | } |
| 4533 | 4516 | } |
| 4534 | 4517 | |
| 4535 | | return ev.execv( |
| 4518 | return execv( |
| 4536 | 4519 | sync, |
| 4537 | 4520 | options.spawn.expand_arg0, |
| 4538 | 4521 | options.argv_buf.ptr[0].?, |
| ... | ... | @@ -4543,7 +4526,6 @@ fn setUpChild(ev: *Evented, sync: *CancelRegion.Sync, options: struct { |
| 4543 | 4526 | } |
| 4544 | 4527 | |
| 4545 | 4528 | fn setUpChildIo( |
| 4546 | | ev: *Evented, |
| 4547 | 4529 | sync: *CancelRegion.Sync, |
| 4548 | 4530 | stdio: process.SpawnOptions.StdIo, |
| 4549 | 4531 | pipe_fd: fd_t, |
| ... | ... | @@ -4551,13 +4533,13 @@ fn setUpChildIo( |
| 4551 | 4533 | dev_null_fd: fd_t, |
| 4552 | 4534 | ) !void { |
| 4553 | 4535 | switch (stdio) { |
| 4554 | | .pipe => try ev.dup2(sync, pipe_fd, std_fileno), |
| 4536 | .pipe => try dup2(sync, pipe_fd, std_fileno), |
| 4555 | 4537 | .close => _ = linux.close(std_fileno), |
| 4556 | 4538 | .inherit => {}, |
| 4557 | | .ignore => try ev.dup2(sync, dev_null_fd, std_fileno), |
| 4539 | .ignore => try dup2(sync, dev_null_fd, std_fileno), |
| 4558 | 4540 | .file => |file| { |
| 4559 | 4541 | 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); |
| 4561 | 4543 | }, |
| 4562 | 4544 | } |
| 4563 | 4545 | } |
| ... | ... | @@ -4566,13 +4548,12 @@ pub const DupError = error{ |
| 4566 | 4548 | ProcessFdQuotaExceeded, |
| 4567 | 4549 | SystemResources, |
| 4568 | 4550 | } || Io.UnexpectedError || Io.Cancelable; |
| 4569 | | pub fn dup2(ev: *Evented, sync: *CancelRegion.Sync, old_fd: fd_t, new_fd: fd_t) DupError!void { |
| 4570 | | _ = ev; |
| 4551 | pub fn dup2(sync: *CancelRegion.Sync, old_fd: fd_t, new_fd: fd_t) DupError!void { |
| 4571 | 4552 | while (true) { |
| 4572 | 4553 | try sync.cancel_region.await(.nothing); |
| 4573 | 4554 | switch (linux.errno(linux.dup2(old_fd, new_fd))) { |
| 4574 | 4555 | .SUCCESS => {}, |
| 4575 | | .BUSY, .INTR => continue, |
| 4556 | .BUSY, .INTR => {}, |
| 4576 | 4557 | .INVAL => |err| return errnoBug(err), // invalid parameters |
| 4577 | 4558 | .BADF => |err| return errnoBug(err), // use after free |
| 4578 | 4559 | .MFILE => return error.ProcessFdQuotaExceeded, |
| ... | ... | @@ -4583,7 +4564,6 @@ pub fn dup2(ev: *Evented, sync: *CancelRegion.Sync, old_fd: fd_t, new_fd: fd_t) |
| 4583 | 4564 | } |
| 4584 | 4565 | |
| 4585 | 4566 | fn execv( |
| 4586 | | ev: *Evented, |
| 4587 | 4567 | sync: *CancelRegion.Sync, |
| 4588 | 4568 | arg0_expand: process.ArgExpansion, |
| 4589 | 4569 | file: [*:0]const u8, |
| ... | ... | @@ -4592,7 +4572,8 @@ fn execv( |
| 4592 | 4572 | PATH: []const u8, |
| 4593 | 4573 | ) process.ReplaceError { |
| 4594 | 4574 | 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); |
| 4596 | 4577 | |
| 4597 | 4578 | // Use of PATH_MAX here is valid as the path_buf will be passed |
| 4598 | 4579 | // directly to the operating system in posixExecvPath. |
| ... | ... | @@ -4620,7 +4601,7 @@ fn execv( |
| 4620 | 4601 | .expand => child_argv[0] = full_path, |
| 4621 | 4602 | .no_expand => {}, |
| 4622 | 4603 | } |
| 4623 | | err = ev.execvPath(sync, full_path, child_argv, env_block); |
| 4604 | err = execvPath(sync, full_path, child_argv, env_block); |
| 4624 | 4605 | switch (err) { |
| 4625 | 4606 | error.AccessDenied => seen_eacces = true, |
| 4626 | 4607 | error.FileNotFound, error.NotDir => {}, |
| ... | ... | @@ -4632,13 +4613,11 @@ fn execv( |
| 4632 | 4613 | } |
| 4633 | 4614 | /// This function ignores PATH environment variable. |
| 4634 | 4615 | pub fn execvPath( |
| 4635 | | ev: *Evented, |
| 4636 | 4616 | sync: *CancelRegion.Sync, |
| 4637 | 4617 | path: [*:0]const u8, |
| 4638 | 4618 | child_argv: [*:null]const ?[*:0]const u8, |
| 4639 | 4619 | env_block: process.Environ.PosixBlock, |
| 4640 | 4620 | ) process.ReplaceError { |
| 4641 | | _ = ev; |
| 4642 | 4621 | try sync.cancel_region.await(.nothing); |
| 4643 | 4622 | switch (linux.errno(linux.execve(path, child_argv, env_block.slice.ptr))) { |
| 4644 | 4623 | .FAULT => |err| return errnoBug(err), // Bad pointer parameter. |
| ... | ... | @@ -4709,7 +4688,7 @@ fn childWait(userdata: ?*anyopaque, child: *process.Child) process.Child.WaitErr |
| 4709 | 4688 | child.resource_usage_statistics.rusage = rusage; |
| 4710 | 4689 | break; |
| 4711 | 4690 | }, |
| 4712 | | .INTR, .CANCELED => continue, |
| 4691 | .INTR, .CANCELED => {}, |
| 4713 | 4692 | .CHILD => |err| return errnoBug(err), // Double-free. |
| 4714 | 4693 | else => |err| return unexpectedErrno(err), |
| 4715 | 4694 | } |
| ... | ... | @@ -4724,7 +4703,7 @@ fn childWait(userdata: ?*anyopaque, child: *process.Child) process.Child.WaitErr |
| 4724 | 4703 | _, .CONTINUED => .{ .unknown = status }, |
| 4725 | 4704 | }; |
| 4726 | 4705 | }, |
| 4727 | | .INTR, .CANCELED => continue, |
| 4706 | .INTR, .CANCELED => {}, |
| 4728 | 4707 | .CHILD => |err| return errnoBug(err), // Double-free. |
| 4729 | 4708 | else => |err| return unexpectedErrno(err), |
| 4730 | 4709 | } |
| ... | ... | @@ -4741,7 +4720,7 @@ fn childKill(userdata: ?*anyopaque, child: *process.Child) void { |
| 4741 | 4720 | const pid = child.id.?; |
| 4742 | 4721 | while (true) switch (linux.errno(linux.kill(pid, .TERM))) { |
| 4743 | 4722 | .SUCCESS => break, |
| 4744 | | .INTR => continue, |
| 4723 | .INTR => {}, |
| 4745 | 4724 | .PERM => return, |
| 4746 | 4725 | .INVAL => |err| return errnoBug(err) catch {}, |
| 4747 | 4726 | .SRCH => |err| return errnoBug(err) catch {}, |
| ... | ... | @@ -4773,7 +4752,7 @@ fn childKill(userdata: ?*anyopaque, child: *process.Child) void { |
| 4773 | 4752 | ev.yield(null, .nothing); |
| 4774 | 4753 | switch (maybe_sync.cancel_region.errno()) { |
| 4775 | 4754 | .SUCCESS => return, |
| 4776 | | .INTR, .CANCELED => continue, |
| 4755 | .INTR, .CANCELED => {}, |
| 4777 | 4756 | .CHILD => |err| return errnoBug(err) catch {}, // Double-free. |
| 4778 | 4757 | else => |err| return unexpectedErrno(err) catch {}, |
| 4779 | 4758 | } |
| ... | ... | @@ -4782,15 +4761,15 @@ fn childKill(userdata: ?*anyopaque, child: *process.Child) void { |
| 4782 | 4761 | |
| 4783 | 4762 | fn childCleanup(ev: *Evented, child: *process.Child) void { |
| 4784 | 4763 | if (child.stdin) |*stdin| { |
| 4785 | | ev.close(stdin.handle); |
| 4764 | ev.closeAsync(stdin.handle); |
| 4786 | 4765 | child.stdin = null; |
| 4787 | 4766 | } |
| 4788 | 4767 | if (child.stdout) |*stdout| { |
| 4789 | | ev.close(stdout.handle); |
| 4768 | ev.closeAsync(stdout.handle); |
| 4790 | 4769 | child.stdout = null; |
| 4791 | 4770 | } |
| 4792 | 4771 | if (child.stderr) |*stderr| { |
| 4793 | | ev.close(stderr.handle); |
| 4772 | ev.closeAsync(stderr.handle); |
| 4794 | 4773 | child.stderr = null; |
| 4795 | 4774 | } |
| 4796 | 4775 | child.id = null; |
| ... | ... | @@ -4896,14 +4875,10 @@ fn sleep(userdata: ?*anyopaque, timeout: Io.Timeout) Io.Cancelable!void { |
| 4896 | 4875 | .resv = 0, |
| 4897 | 4876 | }; |
| 4898 | 4877 | 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. |
| 4907 | 4882 | } |
| 4908 | 4883 | |
| 4909 | 4884 | fn random(userdata: ?*anyopaque, buffer: []u8) void { |
| ... | ... | @@ -4980,7 +4955,7 @@ fn netBindIp( |
| 4980 | 4955 | var maybe_sync: CancelRegion.Sync.Maybe = .{ .cancel_region = .init() }; |
| 4981 | 4956 | defer maybe_sync.deinit(ev); |
| 4982 | 4957 | const socket_fd = try ev.socket(&maybe_sync.cancel_region, family, options); |
| 4983 | | errdefer ev.close(socket_fd); |
| 4958 | errdefer ev.closeAsync(socket_fd); |
| 4984 | 4959 | var storage: PosixAddress = undefined; |
| 4985 | 4960 | var addr_len = addressToPosix(address, &storage); |
| 4986 | 4961 | try ev.bind(&maybe_sync.cancel_region, socket_fd, &storage.any, addr_len); |
| ... | ... | @@ -5147,23 +5122,19 @@ fn netReceive( |
| 5147 | 5122 | .data = data, |
| 5148 | 5123 | .control = if (msg.control) |ptr| @as([*]u8, @ptrCast(ptr))[0..msg.controllen] else message.control, |
| 5149 | 5124 | .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, |
| 5155 | 5130 | }, |
| 5156 | 5131 | }; |
| 5157 | 5132 | message_i += 1; |
| 5158 | 5133 | continue; |
| 5159 | 5134 | }, |
| 5160 | 5135 | .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 }, |
| 5167 | 5138 | |
| 5168 | 5139 | .BADF => |err| return .{ errnoBug(err), message_i }, |
| 5169 | 5140 | .NFILE => return .{ error.SystemFdQuotaExceeded, message_i }, |
| ... | ... | @@ -5266,7 +5237,7 @@ fn netShutdown( |
| 5266 | 5237 | ev.yield(null, .nothing); |
| 5267 | 5238 | switch (cancel_region.errno()) { |
| 5268 | 5239 | .SUCCESS => return, |
| 5269 | | .INTR, .CANCELED => continue, |
| 5240 | .INTR, .CANCELED => {}, |
| 5270 | 5241 | .BADF, .NOTSOCK, .INVAL => |err| return errnoBug(err), |
| 5271 | 5242 | .NOTCONN => return error.SocketUnconnected, |
| 5272 | 5243 | .NOBUFS => return error.SystemResources, |
| ... | ... | @@ -5336,7 +5307,7 @@ fn bind( |
| 5336 | 5307 | ev.yield(null, .nothing); |
| 5337 | 5308 | switch (cancel_region.errno()) { |
| 5338 | 5309 | .SUCCESS => return, |
| 5339 | | .INTR, .CANCELED => continue, |
| 5310 | .INTR, .CANCELED => {}, |
| 5340 | 5311 | .ADDRINUSE => return error.AddressInUse, |
| 5341 | 5312 | .BADF => |err| return errnoBug(err), // File descriptor used after closed. |
| 5342 | 5313 | .INVAL => |err| return errnoBug(err), // invalid parameters |
| ... | ... | @@ -5350,13 +5321,12 @@ fn bind( |
| 5350 | 5321 | } |
| 5351 | 5322 | } |
| 5352 | 5323 | |
| 5353 | | fn chdir(ev: *Evented, sync: *CancelRegion.Sync, path: [*:0]const u8) ChdirError!void { |
| 5354 | | _ = ev; |
| 5324 | fn chdir(sync: *CancelRegion.Sync, path: [*:0]const u8) ChdirError!void { |
| 5355 | 5325 | while (true) { |
| 5356 | 5326 | try sync.cancel_region.await(.nothing); |
| 5357 | 5327 | switch (linux.errno(linux.chdir(path))) { |
| 5358 | 5328 | .SUCCESS => return, |
| 5359 | | .INTR => continue, |
| 5329 | .INTR => {}, |
| 5360 | 5330 | .ACCES => return error.AccessDenied, |
| 5361 | 5331 | .IO => return error.FileSystem, |
| 5362 | 5332 | .LOOP => return error.SymLinkLoop, |
| ... | ... | @@ -5372,6 +5342,36 @@ fn chdir(ev: *Evented, sync: *CancelRegion.Sync, path: [*:0]const u8) ChdirError |
| 5372 | 5342 | } |
| 5373 | 5343 | |
| 5374 | 5344 | fn 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 | |
| 5374 | fn closeAsync(ev: *Evented, fd: fd_t) void { |
| 5375 | 5375 | _ = ev; |
| 5376 | 5376 | const thread: *Thread = .current(); |
| 5377 | 5377 | thread.enqueue().* = .{ |
| ... | ... | @@ -5392,14 +5392,13 @@ fn close(ev: *Evented, fd: fd_t) void { |
| 5392 | 5392 | }; |
| 5393 | 5393 | } |
| 5394 | 5394 | |
| 5395 | | fn fchdir(ev: *Evented, sync: *CancelRegion.Sync, dir: fd_t) process.SetCurrentDirError!void { |
| 5396 | | _ = ev; |
| 5395 | fn fchdir(sync: *CancelRegion.Sync, dir: fd_t) process.SetCurrentDirError!void { |
| 5397 | 5396 | if (dir == linux.AT.FDCWD) return; |
| 5398 | 5397 | while (true) { |
| 5399 | 5398 | try sync.cancel_region.await(.nothing); |
| 5400 | 5399 | switch (linux.errno(linux.fchdir(dir))) { |
| 5401 | 5400 | .SUCCESS => return, |
| 5402 | | .INTR => continue, |
| 5401 | .INTR => {}, |
| 5403 | 5402 | .ACCES => return error.AccessDenied, |
| 5404 | 5403 | .NOTDIR => return error.NotDir, |
| 5405 | 5404 | .IO => return error.FileSystem, |
| ... | ... | @@ -5422,7 +5421,7 @@ fn fchmodat( |
| 5422 | 5421 | try sync.cancel_region.await(.nothing); |
| 5423 | 5422 | switch (linux.errno(linux.fchmodat2(dir, path, mode, flags))) { |
| 5424 | 5423 | .SUCCESS => return, |
| 5425 | | .INTR => continue, |
| 5424 | .INTR => {}, |
| 5426 | 5425 | .BADF => |err| return errnoBug(err), |
| 5427 | 5426 | .FAULT => |err| return errnoBug(err), |
| 5428 | 5427 | .INVAL => |err| return errnoBug(err), |
| ... | ... | @@ -5454,7 +5453,7 @@ fn fchownat( |
| 5454 | 5453 | try sync.cancel_region.await(.nothing); |
| 5455 | 5454 | switch (linux.errno(linux.fchownat(dir, path, owner, group, flags))) { |
| 5456 | 5455 | .SUCCESS => return, |
| 5457 | | .INTR => continue, |
| 5456 | .INTR => {}, |
| 5458 | 5457 | .BADF => |err| return errnoBug(err), // likely fd refers to directory opened without `Dir.OpenOptions.iterate` |
| 5459 | 5458 | .FAULT => |err| return errnoBug(err), |
| 5460 | 5459 | .INVAL => |err| return errnoBug(err), |
| ... | ... | @@ -5486,7 +5485,7 @@ fn flock( |
| 5486 | 5485 | .exclusive => LOCK.EX, |
| 5487 | 5486 | })))) { |
| 5488 | 5487 | .SUCCESS => return, |
| 5489 | | .INTR => continue, |
| 5488 | .INTR => {}, |
| 5490 | 5489 | .BADF => |err| return errnoBug(err), |
| 5491 | 5490 | .INVAL => |err| return errnoBug(err), // invalid parameters |
| 5492 | 5491 | .NOLCK => return error.SystemResources, |
| ... | ... | @@ -5536,7 +5535,7 @@ fn getsockname( |
| 5536 | 5535 | try sync.cancel_region.await(.nothing); |
| 5537 | 5536 | switch (linux.errno(linux.getsockname(socket_fd, addr, addr_len))) { |
| 5538 | 5537 | .SUCCESS => return, |
| 5539 | | .INTR => continue, |
| 5538 | .INTR => {}, |
| 5540 | 5539 | .BADF => |err| return errnoBug(err), // File descriptor used after closed. |
| 5541 | 5540 | .FAULT => |err| return errnoBug(err), |
| 5542 | 5541 | .INVAL => |err| return errnoBug(err), // invalid parameters |
| ... | ... | @@ -5577,7 +5576,7 @@ fn linkat( |
| 5577 | 5576 | ev.yield(null, .nothing); |
| 5578 | 5577 | switch (cancel_region.errno()) { |
| 5579 | 5578 | .SUCCESS => return, |
| 5580 | | .INTR, .CANCELED => continue, |
| 5579 | .INTR, .CANCELED => {}, |
| 5581 | 5580 | .ACCES => return error.AccessDenied, |
| 5582 | 5581 | .DQUOT => return error.DiskQuota, |
| 5583 | 5582 | .EXIST => return error.PathAlreadyExists, |
| ... | ... | @@ -5617,7 +5616,7 @@ fn lseek( |
| 5617 | 5616 | 8 => linux.lseek(fd, @bitCast(offset), whence), |
| 5618 | 5617 | })) { |
| 5619 | 5618 | .SUCCESS => return, |
| 5620 | | .INTR => continue, |
| 5619 | .INTR => {}, |
| 5621 | 5620 | .BADF => |err| return errnoBug(err), // File descriptor used after closed. |
| 5622 | 5621 | .INVAL => return error.Unseekable, |
| 5623 | 5622 | .OVERFLOW => return error.Unseekable, |
| ... | ... | @@ -5660,7 +5659,7 @@ fn openat( |
| 5660 | 5659 | const completion = cancel_region.completion(); |
| 5661 | 5660 | switch (completion.errno()) { |
| 5662 | 5661 | .SUCCESS => return completion.result, |
| 5663 | | .INTR, .CANCELED => continue, |
| 5662 | .INTR, .CANCELED => {}, |
| 5664 | 5663 | .FAULT => |err| return errnoBug(err), |
| 5665 | 5664 | .INVAL => return error.BadPathName, |
| 5666 | 5665 | .BADF => |err| return errnoBug(err), // File descriptor used after closed. |
| ... | ... | @@ -5722,7 +5721,7 @@ fn preadv( |
| 5722 | 5721 | const completion = cancel_region.completion(); |
| 5723 | 5722 | switch (completion.errno()) { |
| 5724 | 5723 | .SUCCESS => return @as(u32, @bitCast(completion.result)), |
| 5725 | | .INTR, .CANCELED => continue, |
| 5724 | .INTR, .CANCELED => {}, |
| 5726 | 5725 | .INVAL => |err| return errnoBug(err), |
| 5727 | 5726 | .FAULT => |err| return errnoBug(err), |
| 5728 | 5727 | .AGAIN => return error.WouldBlock, |
| ... | ... | @@ -5769,7 +5768,7 @@ fn pwritev( |
| 5769 | 5768 | const completion = cancel_region.completion(); |
| 5770 | 5769 | switch (completion.errno()) { |
| 5771 | 5770 | .SUCCESS => return @as(u32, @bitCast(completion.result)), |
| 5772 | | .INTR, .CANCELED => continue, |
| 5771 | .INTR, .CANCELED => {}, |
| 5773 | 5772 | .INVAL => |err| return errnoBug(err), |
| 5774 | 5773 | .FAULT => |err| return errnoBug(err), |
| 5775 | 5774 | .AGAIN => return error.WouldBlock, |
| ... | ... | @@ -5819,7 +5818,7 @@ fn realPath( |
| 5819 | 5818 | const rc = linux.readlink(proc_path, out_buffer.ptr, out_buffer.len); |
| 5820 | 5819 | switch (linux.errno(rc)) { |
| 5821 | 5820 | .SUCCESS => return rc, |
| 5822 | | .INTR => continue, |
| 5821 | .INTR => {}, |
| 5823 | 5822 | .ACCES => return error.AccessDenied, |
| 5824 | 5823 | .FAULT => |err| return errnoBug(err), |
| 5825 | 5824 | .IO => return error.FileSystem, |
| ... | ... | @@ -5864,7 +5863,7 @@ fn renameat( |
| 5864 | 5863 | ev.yield(null, .nothing); |
| 5865 | 5864 | switch (cancel_region.errno()) { |
| 5866 | 5865 | .SUCCESS => return, |
| 5867 | | .INTR, .CANCELED => continue, |
| 5866 | .INTR, .CANCELED => {}, |
| 5868 | 5867 | .ACCES => return error.AccessDenied, |
| 5869 | 5868 | .PERM => return error.PermissionDenied, |
| 5870 | 5869 | .BUSY => return error.FileBusy, |
| ... | ... | @@ -5931,7 +5930,7 @@ fn setsockopt( |
| 5931 | 5930 | ev.yield(null, .nothing); |
| 5932 | 5931 | switch (cancel_region.errno()) { |
| 5933 | 5932 | .SUCCESS => return, |
| 5934 | | .INTR, .CANCELED => continue, |
| 5933 | .INTR, .CANCELED => {}, |
| 5935 | 5934 | .BADF => |err| return errnoBug(err), // File descriptor used after closed. |
| 5936 | 5935 | .NOTSOCK => |err| return errnoBug(err), |
| 5937 | 5936 | .INVAL => |err| return errnoBug(err), |
| ... | ... | @@ -5982,7 +5981,7 @@ fn socket( |
| 5982 | 5981 | const completion = cancel_region.completion(); |
| 5983 | 5982 | switch (completion.errno()) { |
| 5984 | 5983 | .SUCCESS => break completion.result, |
| 5985 | | .INTR, .CANCELED => continue, |
| 5984 | .INTR, .CANCELED => {}, |
| 5986 | 5985 | .AFNOSUPPORT => return error.AddressFamilyUnsupported, |
| 5987 | 5986 | .INVAL => return error.ProtocolUnsupportedBySystem, |
| 5988 | 5987 | .MFILE => return error.ProcessFdQuotaExceeded, |
| ... | ... | @@ -5994,7 +5993,7 @@ fn socket( |
| 5994 | 5993 | else => |err| return unexpectedErrno(err), |
| 5995 | 5994 | } |
| 5996 | 5995 | }; |
| 5997 | | errdefer ev.close(socket_fd); |
| 5996 | errdefer ev.closeAsync(socket_fd); |
| 5998 | 5997 | |
| 5999 | 5998 | if (options.ip6_only) { |
| 6000 | 5999 | if (linux.IPV6 == void) return error.OptionUnsupported; |
| ... | ... | @@ -6044,7 +6043,7 @@ fn statx( |
| 6044 | 6043 | ev.yield(null, .nothing); |
| 6045 | 6044 | switch (cancel_region.errno()) { |
| 6046 | 6045 | .SUCCESS => return statFromLinux(&statx_buf), |
| 6047 | | .INTR, .CANCELED => continue, |
| 6046 | .INTR, .CANCELED => {}, |
| 6048 | 6047 | .ACCES => return error.AccessDenied, |
| 6049 | 6048 | .BADF => |err| return errnoBug(err), // File descriptor used after closed. |
| 6050 | 6049 | .FAULT => |err| return errnoBug(err), |
| ... | ... | @@ -6083,7 +6082,7 @@ fn utimensat( |
| 6083 | 6082 | try sync.cancel_region.await(.nothing); |
| 6084 | 6083 | switch (linux.errno(linux.utimensat(dir, path, times, flags))) { |
| 6085 | 6084 | .SUCCESS => return, |
| 6086 | | .INTR => continue, |
| 6085 | .INTR => {}, |
| 6087 | 6086 | .BADF => |err| return errnoBug(err), // always a race condition |
| 6088 | 6087 | .FAULT => |err| return errnoBug(err), |
| 6089 | 6088 | .INVAL => |err| return errnoBug(err), |
| ... | ... | @@ -6095,19 +6094,33 @@ fn utimensat( |
| 6095 | 6094 | } |
| 6096 | 6095 | } |
| 6097 | 6096 | |
| 6098 | | fn writeAll( |
| 6099 | | ev: *Evented, |
| 6100 | | cancel_region: *CancelRegion, |
| 6101 | | fd: fd_t, |
| 6102 | | buffer: []const u8, |
| 6103 | | ) (File.Writer.Error || error{EndOfStream})!void { |
| 6097 | fn writeAllSync(sync: *CancelRegion.Sync, fd: fd_t, buffer: []const u8) File.Writer.Error!void { |
| 6104 | 6098 | 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 | |
| 6102 | fn 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 | } |
| 6111 | 6124 | } |
| 6112 | 6125 | } |
| 6113 | 6126 | |