| ... | ... | @@ -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 }, |
| ... | ... | @@ -175,7 +174,6 @@ const Fiber = struct { |
| 175 | 174 | const Awaiting = enum(u31) { |
| 176 | 175 | nothing = std.math.maxInt(u31), |
| 177 | 176 | group = std.math.maxInt(u31) - 1, |
| 178 | | select = std.math.maxInt(u31) - 2, |
| 179 | 177 | /// An io_uring fd. |
| 180 | 178 | _, |
| 181 | 179 | |
| ... | ... | @@ -186,14 +184,14 @@ const Fiber = struct { |
| 186 | 184 | fn fromIoUringFd(fd: fd_t) Awaiting { |
| 187 | 185 | const awaiting: Awaiting = @enumFromInt(fd); |
| 188 | 186 | switch (awaiting) { |
| 189 | | .nothing, .group, .select => unreachable, |
| 187 | .nothing, .group => unreachable, |
| 190 | 188 | _ => return awaiting, |
| 191 | 189 | } |
| 192 | 190 | } |
| 193 | 191 | |
| 194 | 192 | fn toIoUringFd(awaiting: Awaiting) fd_t { |
| 195 | 193 | switch (awaiting) { |
| 196 | | .nothing, .group, .select => unreachable, |
| 194 | .nothing, .group => unreachable, |
| 197 | 195 | _ => return @intFromEnum(awaiting), |
| 198 | 196 | } |
| 199 | 197 | } |
| ... | ... | @@ -376,9 +374,6 @@ const Fiber = struct { |
| 376 | 374 | _ = ev.schedule(.current(), .{ .head = fiber, .tail = fiber }); |
| 377 | 375 | } |
| 378 | 376 | }, |
| 379 | | .select => if (@atomicRmw(i32, &fiber.await_count, .Add, 1, .monotonic) == -1) { |
| 380 | | _ = ev.schedule(.current(), .{ .head = fiber, .tail = fiber }); |
| 381 | | }, |
| 382 | 377 | _ => |awaiting| { |
| 383 | 378 | const awaiting_io_uring_fd = awaiting.toIoUringFd(); |
| 384 | 379 | const thread: *Thread = .current(); |
| ... | ... | @@ -684,8 +679,6 @@ pub fn io(ev: *Evented) Io { |
| 684 | 679 | .swapCancelProtection = swapCancelProtection, |
| 685 | 680 | .checkCancel = checkCancel, |
| 686 | 681 | |
| 687 | | .select = select, |
| 688 | | |
| 689 | 682 | .futexWait = futexWait, |
| 690 | 683 | .futexWaitUncancelable = futexWaitUncancelable, |
| 691 | 684 | .futexWake = futexWake, |
| ... | ... | @@ -862,7 +855,6 @@ pub fn init(ev: *Evented, backing_allocator: Allocator, options: InitOptions) !v |
| 862 | 855 | main_fiber.* = .{ |
| 863 | 856 | .required_align = {}, |
| 864 | 857 | .context = undefined, |
| 865 | | .await_count = 0, |
| 866 | 858 | .link = .{ .awaiter = null }, |
| 867 | 859 | .status = .{ .queue_next = null }, |
| 868 | 860 | .cancel_status = .unrequested, |
| ... | ... | @@ -1266,7 +1258,7 @@ const SwitchMessage = struct { |
| 1266 | 1258 | const PendingTask = union(enum) { |
| 1267 | 1259 | nothing, |
| 1268 | 1260 | reschedule, |
| 1269 | | await: u31, |
| 1261 | await: *Fiber, |
| 1270 | 1262 | group_await: Group, |
| 1271 | 1263 | group_cancel: Group, |
| 1272 | 1264 | batch_await: *Io.Batch, |
| ... | ... | @@ -1290,10 +1282,11 @@ const SwitchMessage = struct { |
| 1290 | 1282 | assert(fiber.status.queue_next == null); |
| 1291 | 1283 | _ = ev.schedule(thread, .{ .head = fiber, .tail = fiber }); |
| 1292 | 1284 | }, |
| 1293 | | .await => |count| { |
| 1294 | | const fiber: *Fiber = @alignCast(@fieldParentPtr("context", message.contexts.old)); |
| 1295 | | if (@atomicRmw(i32, &fiber.await_count, .Sub, count, .monotonic) > 0) |
| 1296 | | _ = 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 }); |
| 1297 | 1290 | }, |
| 1298 | 1291 | .group_await => |group| { |
| 1299 | 1292 | const fiber: *Fiber = @alignCast(@fieldParentPtr("context", message.contexts.old)); |
| ... | ... | @@ -1367,7 +1360,7 @@ fn crashHandler(userdata: ?*anyopaque) void { |
| 1367 | 1360 | } |
| 1368 | 1361 | |
| 1369 | 1362 | const AsyncClosure = struct { |
| 1370 | | ev: *Evented, |
| 1363 | evented: *Evented, |
| 1371 | 1364 | fiber: *Fiber, |
| 1372 | 1365 | start: *const fn (context: *const anyopaque, result: *anyopaque) void, |
| 1373 | 1366 | result_align: Alignment, |
| ... | ... | @@ -1404,16 +1397,11 @@ const AsyncClosure = struct { |
| 1404 | 1397 | closure: *AsyncClosure, |
| 1405 | 1398 | message: *const SwitchMessage, |
| 1406 | 1399 | ) callconv(.withStackAlign(.c, @alignOf(AsyncClosure))) noreturn { |
| 1407 | | message.handle(closure.ev); |
| 1400 | const ev = closure.evented; |
| 1408 | 1401 | const fiber = closure.fiber; |
| 1402 | message.handle(ev); |
| 1409 | 1403 | closure.start(closure.contextPointer(), fiber.resultBytes(closure.result_align)); |
| 1410 | | closure.ev.yield( |
| 1411 | | if (@atomicRmw(?*Fiber, &fiber.link.awaiter, .Xchg, Fiber.finished, .acq_rel)) |awaiter| |
| 1412 | | if (@atomicRmw(i32, &awaiter.await_count, .Add, 1, .monotonic) == -1) awaiter else null |
| 1413 | | else |
| 1414 | | null, |
| 1415 | | .nothing, |
| 1416 | | ); |
| 1404 | ev.yield(@atomicRmw(?*Fiber, &fiber.link.awaiter, .Xchg, Fiber.finished, .acq_rel), .nothing); |
| 1417 | 1405 | unreachable; // switched to dead fiber |
| 1418 | 1406 | } |
| 1419 | 1407 | }; |
| ... | ... | @@ -1467,7 +1455,6 @@ fn concurrent( |
| 1467 | 1455 | }, |
| 1468 | 1456 | else => |arch| @compileError("unimplemented architecture: " ++ @tagName(arch)), |
| 1469 | 1457 | }, |
| 1470 | | .await_count = 0, |
| 1471 | 1458 | .link = .{ .awaiter = null }, |
| 1472 | 1459 | .status = .{ .queue_next = null }, |
| 1473 | 1460 | .cancel_status = .unrequested, |
| ... | ... | @@ -1485,7 +1472,7 @@ fn concurrent( |
| 1485 | 1472 | }, |
| 1486 | 1473 | }; |
| 1487 | 1474 | closure.* = .{ |
| 1488 | | .ev = ev, |
| 1475 | .evented = ev, |
| 1489 | 1476 | .fiber = fiber, |
| 1490 | 1477 | .start = start, |
| 1491 | 1478 | .result_align = result_alignment, |
| ... | ... | @@ -1504,18 +1491,11 @@ fn await( |
| 1504 | 1491 | result_alignment: Alignment, |
| 1505 | 1492 | ) void { |
| 1506 | 1493 | const ev: *Evented = @ptrCast(@alignCast(userdata)); |
| 1507 | | const fiber = Thread.current().currentFiber(); |
| 1508 | | const future_fiber: *Fiber = @ptrCast(@alignCast(future)); |
| 1509 | | if (@atomicRmw(?*Fiber, &future_fiber.link.awaiter, .Xchg, fiber, .acq_rel)) |awaiter| { |
| 1510 | | assert(awaiter == Fiber.finished); |
| 1511 | | } else while (true) { |
| 1512 | | ev.yield(null, .{ .await = 1 }); |
| 1513 | | const awaiter = @atomicLoad(?*Fiber, &future_fiber.link.awaiter, .acquire); |
| 1514 | | if (awaiter == Fiber.finished) break; |
| 1515 | | assert(awaiter == fiber); // spurious wakeup |
| 1516 | | } |
| 1517 | | @memcpy(result, future_fiber.resultBytes(result_alignment)); |
| 1518 | | 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(); |
| 1519 | 1499 | } |
| 1520 | 1500 | |
| 1521 | 1501 | fn cancel( |
| ... | ... | @@ -1732,7 +1712,7 @@ const Group = struct { |
| 1732 | 1712 | } |
| 1733 | 1713 | |
| 1734 | 1714 | const AsyncClosure = struct { |
| 1735 | | ev: *Evented, |
| 1715 | evented: *Evented, |
| 1736 | 1716 | group: Group, |
| 1737 | 1717 | fiber: *Fiber, |
| 1738 | 1718 | start: *const fn (context: *const anyopaque) Io.Cancelable!void, |
| ... | ... | @@ -1771,19 +1751,16 @@ const Group = struct { |
| 1771 | 1751 | closure: *Group.AsyncClosure, |
| 1772 | 1752 | message: *const SwitchMessage, |
| 1773 | 1753 | ) callconv(.withStackAlign(.c, @alignOf(Group.AsyncClosure))) noreturn { |
| 1774 | | message.handle(closure.ev); |
| 1775 | | assert(closure.fiber.status.queue_next == null); |
| 1776 | | const result = closure.start(closure.contextPointer()); |
| 1777 | | const ev = closure.ev; |
| 1778 | | const group = closure.group; |
| 1754 | const ev = closure.evented; |
| 1779 | 1755 | const fiber = closure.fiber; |
| 1780 | | const cancel_acknowledged = fiber.cancel_protection.acknowledged; |
| 1781 | | if (result) { |
| 1782 | | 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` |
| 1783 | 1760 | } else |err| switch (err) { |
| 1784 | | 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 |
| 1785 | 1762 | } |
| 1786 | | ev.yield(group.removeFiber(ev, fiber), .destroy); |
| 1763 | ev.yield(closure.group.removeFiber(ev, fiber), .destroy); |
| 1787 | 1764 | unreachable; // switched to dead fiber |
| 1788 | 1765 | } |
| 1789 | 1766 | }; |
| ... | ... | @@ -1851,7 +1828,6 @@ fn groupConcurrent( |
| 1851 | 1828 | }, |
| 1852 | 1829 | else => |arch| @compileError("unimplemented architecture: " ++ @tagName(arch)), |
| 1853 | 1830 | }, |
| 1854 | | .await_count = 0, |
| 1855 | 1831 | .link = .{ .group = .{ .prev = null, .next = null } }, |
| 1856 | 1832 | .status = .{ .queue_next = null }, |
| 1857 | 1833 | .cancel_status = .unrequested, |
| ... | ... | @@ -1869,7 +1845,7 @@ fn groupConcurrent( |
| 1869 | 1845 | }, |
| 1870 | 1846 | }; |
| 1871 | 1847 | closure.* = .{ |
| 1872 | | .ev = ev, |
| 1848 | .evented = ev, |
| 1873 | 1849 | .group = group, |
| 1874 | 1850 | .fiber = fiber, |
| 1875 | 1851 | .start = start, |
| ... | ... | @@ -1928,57 +1904,6 @@ fn checkCancel(userdata: ?*anyopaque) Io.Cancelable!void { |
| 1928 | 1904 | } |
| 1929 | 1905 | } |
| 1930 | 1906 | |
| 1931 | | fn select(userdata: ?*anyopaque, futures: []const *Io.AnyFuture) Io.Cancelable!usize { |
| 1932 | | const ev: *Evented = @ptrCast(@alignCast(userdata)); |
| 1933 | | var cancel_region: CancelRegion = .init(); |
| 1934 | | defer cancel_region.deinit(); |
| 1935 | | var await_count: u31, var result = for (futures, 0..) |future, future_index| { |
| 1936 | | const future_fiber: *Fiber = @ptrCast(@alignCast(future)); |
| 1937 | | if (@atomicRmw( |
| 1938 | | ?*Fiber, |
| 1939 | | &future_fiber.link.awaiter, |
| 1940 | | .Xchg, |
| 1941 | | cancel_region.fiber, |
| 1942 | | .acq_rel, |
| 1943 | | )) |awaiter| { |
| 1944 | | assert(awaiter == Fiber.finished); |
| 1945 | | break .{ @intCast(future_index), future_index }; |
| 1946 | | } |
| 1947 | | } else result: { |
| 1948 | | const await_count: u31 = @intCast(futures.len); |
| 1949 | | cancel_region.await(.select) catch |err| switch (err) { |
| 1950 | | error.Canceled => |e| break :result .{ await_count + 1, e }, |
| 1951 | | }; |
| 1952 | | ev.yield(null, .{ .await = 1 }); |
| 1953 | | cancel_region.await(.nothing) catch |err| switch (err) { |
| 1954 | | error.Canceled => |e| break :result .{ await_count, e }, |
| 1955 | | }; |
| 1956 | | break :result .{ await_count - 1, futures.len }; |
| 1957 | | }; |
| 1958 | | for (futures[0 .. result catch futures.len], 0..) |future, future_index| { |
| 1959 | | const future_fiber: *Fiber = @ptrCast(@alignCast(future)); |
| 1960 | | const awaiter = @atomicRmw(?*Fiber, &future_fiber.link.awaiter, .Xchg, null, .monotonic); |
| 1961 | | if (awaiter == Fiber.finished) { |
| 1962 | | @atomicStore(?*Fiber, &future_fiber.link.awaiter, Fiber.finished, .monotonic); |
| 1963 | | result = if (result) |finished_index| @min(future_index, finished_index) else |e| e; |
| 1964 | | } else { |
| 1965 | | assert(awaiter == cancel_region.fiber); |
| 1966 | | await_count -= 1; |
| 1967 | | } |
| 1968 | | } |
| 1969 | | // Equivalent to `ev.yield(null, .{ .await = await_count });`, |
| 1970 | | // but avoiding a context switch in the common case. |
| 1971 | | switch (std.math.order( |
| 1972 | | @atomicRmw(i32, &cancel_region.fiber.await_count, .Sub, await_count, .monotonic), |
| 1973 | | await_count, |
| 1974 | | )) { |
| 1975 | | .lt => ev.yield(null, .{ .await = 0 }), |
| 1976 | | .eq => {}, |
| 1977 | | .gt => unreachable, |
| 1978 | | } |
| 1979 | | return result; |
| 1980 | | } |
| 1981 | | |
| 1982 | 1907 | fn futexWait( |
| 1983 | 1908 | userdata: ?*anyopaque, |
| 1984 | 1909 | ptr: *const u32, |
| ... | ... | @@ -2230,7 +2155,7 @@ fn deviceIoControl( |
| 2230 | 2155 | const rc = linux.ioctl(o.file.handle, @bitCast(o.code), @intFromPtr(o.arg)); |
| 2231 | 2156 | switch (linux.errno(rc)) { |
| 2232 | 2157 | .SUCCESS => return @bitCast(@as(u32, @truncate(rc))), |
| 2233 | | .INTR => continue, |
| 2158 | .INTR => {}, |
| 2234 | 2159 | else => |err| return -@as(i32, @intFromEnum(err)), |
| 2235 | 2160 | } |
| 2236 | 2161 | } |
| ... | ... | @@ -2628,7 +2553,7 @@ fn dirCreateDir( |
| 2628 | 2553 | ev.yield(null, .nothing); |
| 2629 | 2554 | switch (cancel_region.errno()) { |
| 2630 | 2555 | .SUCCESS => return, |
| 2631 | | .INTR, .CANCELED => continue, |
| 2556 | .INTR, .CANCELED => {}, |
| 2632 | 2557 | .ACCES => return error.AccessDenied, |
| 2633 | 2558 | .BADF => |err| return errnoBug(err), // File descriptor used after closed. |
| 2634 | 2559 | .PERM => return error.PermissionDenied, |
| ... | ... | @@ -2711,7 +2636,7 @@ fn filePathKind(ev: *Evented, dir: Dir, sub_path: []const u8) !File.Kind { |
| 2711 | 2636 | if (!statx_buf.mask.TYPE) return error.Unexpected; |
| 2712 | 2637 | return statxKind(statx_buf.mode); |
| 2713 | 2638 | }, |
| 2714 | | .INTR, .CANCELED => continue, |
| 2639 | .INTR, .CANCELED => {}, |
| 2715 | 2640 | .ACCES => |err| return errnoBug(err), |
| 2716 | 2641 | .BADF => |err| return errnoBug(err), // File descriptor used after closed. |
| 2717 | 2642 | .FAULT => |err| return errnoBug(err), |
| ... | ... | @@ -2824,7 +2749,7 @@ fn dirAccess( |
| 2824 | 2749 | try sync.cancel_region.await(.nothing); |
| 2825 | 2750 | switch (linux.errno(linux.faccessat(dir.handle, sub_path_posix, mode, flags))) { |
| 2826 | 2751 | .SUCCESS => return, |
| 2827 | | .INTR => continue, |
| 2752 | .INTR => {}, |
| 2828 | 2753 | .ACCES => return error.AccessDenied, |
| 2829 | 2754 | .PERM => return error.PermissionDenied, |
| 2830 | 2755 | .ROFS => return error.ReadOnlyFileSystem, |
| ... | ... | @@ -2863,7 +2788,7 @@ fn dirCreateFile( |
| 2863 | 2788 | .EXCL = flags.exclusive, |
| 2864 | 2789 | .CLOEXEC = true, |
| 2865 | 2790 | }, flags.permissions.toMode()); |
| 2866 | | errdefer ev.close(fd); |
| 2791 | errdefer ev.closeAsync(fd); |
| 2867 | 2792 | |
| 2868 | 2793 | switch (flags.lock) { |
| 2869 | 2794 | .none => {}, |
| ... | ... | @@ -3051,7 +2976,7 @@ fn dirOpenFile( |
| 3051 | 2976 | .CLOEXEC = true, |
| 3052 | 2977 | .PATH = flags.path_only, |
| 3053 | 2978 | }, 0); |
| 3054 | | errdefer ev.close(fd); |
| 2979 | errdefer ev.closeAsync(fd); |
| 3055 | 2980 | |
| 3056 | 2981 | if (!flags.allow_directory) { |
| 3057 | 2982 | const is_dir = is_dir: { |
| ... | ... | @@ -3105,7 +3030,7 @@ fn dirRead(userdata: ?*anyopaque, dr: *Dir.Reader, buffer: []Dir.Entry) Dir.Read |
| 3105 | 3030 | const rc = linux.getdents64(dr.dir.handle, dr.buffer.ptr, dr.buffer.len); |
| 3106 | 3031 | switch (linux.errno(rc)) { |
| 3107 | 3032 | .SUCCESS => break rc, |
| 3108 | | .INTR => continue, |
| 3033 | .INTR => {}, |
| 3109 | 3034 | .BADF => |err| return errnoBug(err), // Dir is invalid or was opened without iteration ability. |
| 3110 | 3035 | .FAULT => |err| return errnoBug(err), |
| 3111 | 3036 | .NOTDIR => |err| return errnoBug(err), |
| ... | ... | @@ -3198,7 +3123,7 @@ fn dirRealPathFile( |
| 3198 | 3123 | error.FileLocksUnsupported => return errnoBug(.OPNOTSUPP), // Not asking for locks. |
| 3199 | 3124 | else => |e| return e, |
| 3200 | 3125 | }; |
| 3201 | | defer ev.close(fd); |
| 3126 | defer ev.closeAsync(fd); |
| 3202 | 3127 | return ev.realPath(try maybe_sync.enterSync(ev), fd, out_buffer); |
| 3203 | 3128 | } |
| 3204 | 3129 | |
| ... | ... | @@ -3231,7 +3156,7 @@ fn dirDeleteFile(userdata: ?*anyopaque, dir: Dir, sub_path: []const u8) Dir.Dele |
| 3231 | 3156 | ev.yield(null, .nothing); |
| 3232 | 3157 | switch (cancel_region.errno()) { |
| 3233 | 3158 | .SUCCESS => return, |
| 3234 | | .INTR, .CANCELED => continue, |
| 3159 | .INTR, .CANCELED => {}, |
| 3235 | 3160 | .PERM => return error.PermissionDenied, |
| 3236 | 3161 | .ACCES => return error.AccessDenied, |
| 3237 | 3162 | .BUSY => return error.FileBusy, |
| ... | ... | @@ -3283,7 +3208,7 @@ fn dirDeleteDir(userdata: ?*anyopaque, dir: Dir, sub_path: []const u8) Dir.Delet |
| 3283 | 3208 | ev.yield(null, .nothing); |
| 3284 | 3209 | switch (cancel_region.errno()) { |
| 3285 | 3210 | .SUCCESS => return, |
| 3286 | | .INTR, .CANCELED => continue, |
| 3211 | .INTR, .CANCELED => {}, |
| 3287 | 3212 | .ACCES => return error.AccessDenied, |
| 3288 | 3213 | .PERM => return error.PermissionDenied, |
| 3289 | 3214 | .BUSY => return error.FileBusy, |
| ... | ... | @@ -3399,7 +3324,7 @@ fn dirSymLink( |
| 3399 | 3324 | ev.yield(null, .nothing); |
| 3400 | 3325 | switch (cancel_region.errno()) { |
| 3401 | 3326 | .SUCCESS => return, |
| 3402 | | .INTR, .CANCELED => continue, |
| 3327 | .INTR, .CANCELED => {}, |
| 3403 | 3328 | .FAULT => |err| return errnoBug(err), |
| 3404 | 3329 | .INVAL => |err| return errnoBug(err), |
| 3405 | 3330 | .ACCES => return error.AccessDenied, |
| ... | ... | @@ -3438,7 +3363,7 @@ fn dirReadLink( |
| 3438 | 3363 | const rc = linux.readlinkat(dir.handle, sub_path_posix, buffer.ptr, buffer.len); |
| 3439 | 3364 | switch (linux.errno(rc)) { |
| 3440 | 3365 | .SUCCESS => return @bitCast(rc), |
| 3441 | | .INTR => continue, |
| 3366 | .INTR => {}, |
| 3442 | 3367 | .ACCES => return error.AccessDenied, |
| 3443 | 3368 | .FAULT => |err| return errnoBug(err), |
| 3444 | 3369 | .INVAL => return error.NotLink, |
| ... | ... | @@ -3628,7 +3553,7 @@ fn fileLength(userdata: ?*anyopaque, file: File) File.LengthError!u64 { |
| 3628 | 3553 | if (!statx_buf.mask.SIZE) return error.Unexpected; |
| 3629 | 3554 | return statx_buf.size; |
| 3630 | 3555 | }, |
| 3631 | | .INTR, .CANCELED => continue, |
| 3556 | .INTR, .CANCELED => {}, |
| 3632 | 3557 | .ACCES => |err| return errnoBug(err), |
| 3633 | 3558 | .BADF => |err| return errnoBug(err), // File descriptor used after closed. |
| 3634 | 3559 | .FAULT => |err| return errnoBug(err), |
| ... | ... | @@ -3811,7 +3736,7 @@ fn fileSync(userdata: ?*anyopaque, file: File) File.SyncError!void { |
| 3811 | 3736 | ev.yield(null, .nothing); |
| 3812 | 3737 | switch (cancel_region.errno()) { |
| 3813 | 3738 | .SUCCESS => return, |
| 3814 | | .INTR, .CANCELED => continue, |
| 3739 | .INTR, .CANCELED => {}, |
| 3815 | 3740 | .BADF => |err| return errnoBug(err), |
| 3816 | 3741 | .INVAL => |err| return errnoBug(err), |
| 3817 | 3742 | .ROFS => |err| return errnoBug(err), |
| ... | ... | @@ -3833,7 +3758,7 @@ fn fileIsTty(userdata: ?*anyopaque, file: File) Io.Cancelable!bool { |
| 3833 | 3758 | const rc = linux.ioctl(file.handle, linux.T.IOCGWINSZ, @intFromPtr(&wsz)); |
| 3834 | 3759 | switch (linux.errno(rc)) { |
| 3835 | 3760 | .SUCCESS => return true, |
| 3836 | | .INTR => continue, |
| 3761 | .INTR => {}, |
| 3837 | 3762 | else => return false, |
| 3838 | 3763 | } |
| 3839 | 3764 | } |
| ... | ... | @@ -3869,7 +3794,7 @@ fn fileSetLength(userdata: ?*anyopaque, file: File, length: u64) File.SetLengthE |
| 3869 | 3794 | ev.yield(null, .nothing); |
| 3870 | 3795 | switch (cancel_region.errno()) { |
| 3871 | 3796 | .SUCCESS => return, |
| 3872 | | .INTR, .CANCELED => continue, |
| 3797 | .INTR, .CANCELED => {}, |
| 3873 | 3798 | .FBIG => return error.FileTooBig, |
| 3874 | 3799 | .IO => return error.InputOutput, |
| 3875 | 3800 | .PERM => return error.PermissionDenied, |
| ... | ... | @@ -4051,7 +3976,7 @@ fn fileMemoryMapCreate( |
| 4051 | 3976 | const rc = linux.mmap(null, options.len, prot, flags, file.handle, casted_offset); |
| 4052 | 3977 | switch (linux.errno(rc)) { |
| 4053 | 3978 | .SUCCESS => break @as([*]align(page_align) u8, @ptrFromInt(rc))[0..options.len], |
| 4054 | | .INTR => continue, |
| 3979 | .INTR => {}, |
| 4055 | 3980 | .ACCES => return error.AccessDenied, |
| 4056 | 3981 | .AGAIN => return error.LockedMemoryLimitExceeded, |
| 4057 | 3982 | .MFILE => return error.ProcessFdQuotaExceeded, |
| ... | ... | @@ -4111,7 +4036,7 @@ fn fileMemoryMapSetLength( |
| 4111 | 4036 | const rc = linux.mremap(old_memory.ptr, old_memory.len, new_len, flags, addr_hint); |
| 4112 | 4037 | switch (linux.errno(rc)) { |
| 4113 | 4038 | .SUCCESS => break @as([*]align(page_align) u8, @ptrFromInt(rc))[0..new_len], |
| 4114 | | .INTR => continue, |
| 4039 | .INTR => {}, |
| 4115 | 4040 | .AGAIN => return error.LockedMemoryLimitExceeded, |
| 4116 | 4041 | .NOMEM => return error.OutOfMemory, |
| 4117 | 4042 | .INVAL => |err| return errnoBug(err), |
| ... | ... | @@ -4220,7 +4145,7 @@ fn processCurrentPath(userdata: ?*anyopaque, buffer: []u8) process.CurrentPathEr |
| 4220 | 4145 | try sync.cancel_region.await(.nothing); |
| 4221 | 4146 | switch (linux.errno(linux.getcwd(buffer.ptr, buffer.len))) { |
| 4222 | 4147 | .SUCCESS => return std.mem.findScalar(u8, buffer, 0).?, |
| 4223 | | .INTR => continue, |
| 4148 | .INTR => {}, |
| 4224 | 4149 | .NOENT => return error.CurrentDirUnlinked, |
| 4225 | 4150 | .RANGE => return error.NameTooLong, |
| 4226 | 4151 | .FAULT => |err| return errnoBug(err), |
| ... | ... | @@ -4235,7 +4160,7 @@ fn processSetCurrentDir(userdata: ?*anyopaque, dir: Dir) process.SetCurrentDirEr |
| 4235 | 4160 | if (dir.handle == linux.AT.FDCWD) return; |
| 4236 | 4161 | var sync: CancelRegion.Sync = try .init(ev); |
| 4237 | 4162 | defer sync.deinit(ev); |
| 4238 | | return ev.fchdir(&sync, dir.handle); |
| 4163 | return fchdir(&sync, dir.handle); |
| 4239 | 4164 | } |
| 4240 | 4165 | |
| 4241 | 4166 | fn processSetCurrentPath(userdata: ?*anyopaque, dir_path: []const u8) ChdirError!void { |
| ... | ... | @@ -4272,7 +4197,7 @@ fn processReplace(userdata: ?*anyopaque, options: process.ReplaceOptions) proces |
| 4272 | 4197 | |
| 4273 | 4198 | var sync: CancelRegion.Sync = try .init(ev); |
| 4274 | 4199 | defer sync.deinit(ev); |
| 4275 | | 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); |
| 4276 | 4201 | } |
| 4277 | 4202 | |
| 4278 | 4203 | fn processReplacePath( |
| ... | ... | @@ -4292,7 +4217,7 @@ fn processSpawn(userdata: ?*anyopaque, options: process.SpawnOptions) process.Sp |
| 4292 | 4217 | const spawned = try ev.spawn(options); |
| 4293 | 4218 | var cancel_region: CancelRegion = .initBlocked(); |
| 4294 | 4219 | defer cancel_region.deinit(); |
| 4295 | | defer ev.close(spawned.err_fd); |
| 4220 | defer ev.closeAsync(spawned.err_fd); |
| 4296 | 4221 | |
| 4297 | 4222 | // Wait for the child to report any errors in or before `execvpe`. |
| 4298 | 4223 | var child_err: ForkBailError = undefined; |
| ... | ... | @@ -4434,8 +4359,9 @@ fn spawn(ev: *Evented, options: process.SpawnOptions) process.SpawnError!Spawned |
| 4434 | 4359 | |
| 4435 | 4360 | if (pid_result == 0) { |
| 4436 | 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`. |
| 4437 | 4363 | var sync: CancelRegion.Sync = .{ .cancel_region = .initBlocked() }; |
| 4438 | | const err = ev.setUpChild(&sync, .{ |
| 4364 | const err = setUpChild(&sync, .{ |
| 4439 | 4365 | .stdin_pipe = stdin_pipe[0], |
| 4440 | 4366 | .stdout_pipe = stdout_pipe[1], |
| 4441 | 4367 | .stderr_pipe = stderr_pipe[1], |
| ... | ... | @@ -4446,7 +4372,7 @@ fn spawn(ev: *Evented, options: process.SpawnOptions) process.SpawnError!Spawned |
| 4446 | 4372 | .PATH = PATH, |
| 4447 | 4373 | .spawn = options, |
| 4448 | 4374 | }); |
| 4449 | | ev.writeAll(&sync.cancel_region, err_pipe[1], @ptrCast(&err)) catch {}; |
| 4375 | writeAllSync(&sync, err_pipe[1], @ptrCast(&err)) catch {}; |
| 4450 | 4376 | const exit = if (builtin.single_threaded) linux.exit else linux.exit_group; |
| 4451 | 4377 | exit(1); |
| 4452 | 4378 | } |
| ... | ... | @@ -4454,13 +4380,13 @@ fn spawn(ev: *Evented, options: process.SpawnOptions) process.SpawnError!Spawned |
| 4454 | 4380 | const pid: pid_t = @intCast(pid_result); // We are the parent. |
| 4455 | 4381 | errdefer comptime unreachable; // The child is forked; we must not error from now on |
| 4456 | 4382 | |
| 4457 | | 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 |
| 4458 | 4384 | |
| 4459 | | if (options.stdin == .pipe) ev.close(stdin_pipe[0]); |
| 4460 | | if (options.stdout == .pipe) ev.close(stdout_pipe[1]); |
| 4461 | | 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]); |
| 4462 | 4388 | |
| 4463 | | if (prog_pipe[1] != -1) ev.close(prog_pipe[1]); |
| 4389 | if (prog_pipe[1] != -1) ev.closeAsync(prog_pipe[1]); |
| 4464 | 4390 | |
| 4465 | 4391 | options.progress_node.setIpcFile(ev, .{ .handle = prog_pipe[0], .flags = .{ .nonblocking = true } }); |
| 4466 | 4392 | |
| ... | ... | @@ -4497,14 +4423,14 @@ pub fn pipe2(flags: linux.O) PipeError![2]fd_t { |
| 4497 | 4423 | } |
| 4498 | 4424 | } |
| 4499 | 4425 | fn destroyPipe(ev: *Evented, pipe: [2]fd_t) void { |
| 4500 | | if (pipe[0] != -1) ev.close(pipe[0]); |
| 4501 | | 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]); |
| 4502 | 4428 | } |
| 4503 | 4429 | |
| 4504 | 4430 | /// Errors that can occur between fork() and execv() |
| 4505 | 4431 | const ForkBailError = process.SetCurrentDirError || ChdirError || |
| 4506 | 4432 | process.SpawnError || process.ReplaceError; |
| 4507 | | fn setUpChild(ev: *Evented, sync: *CancelRegion.Sync, options: struct { |
| 4433 | fn setUpChild(sync: *CancelRegion.Sync, options: struct { |
| 4508 | 4434 | stdin_pipe: fd_t, |
| 4509 | 4435 | stdout_pipe: fd_t, |
| 4510 | 4436 | stderr_pipe: fd_t, |
| ... | ... | @@ -4515,21 +4441,21 @@ fn setUpChild(ev: *Evented, sync: *CancelRegion.Sync, options: struct { |
| 4515 | 4441 | PATH: []const u8, |
| 4516 | 4442 | spawn: process.SpawnOptions, |
| 4517 | 4443 | }) ForkBailError { |
| 4518 | | try ev.setUpChildIo( |
| 4444 | try setUpChildIo( |
| 4519 | 4445 | sync, |
| 4520 | 4446 | options.spawn.stdin, |
| 4521 | 4447 | options.stdin_pipe, |
| 4522 | 4448 | linux.STDIN_FILENO, |
| 4523 | 4449 | options.dev_null_fd, |
| 4524 | 4450 | ); |
| 4525 | | try ev.setUpChildIo( |
| 4451 | try setUpChildIo( |
| 4526 | 4452 | sync, |
| 4527 | 4453 | options.spawn.stdout, |
| 4528 | 4454 | options.stdout_pipe, |
| 4529 | 4455 | linux.STDOUT_FILENO, |
| 4530 | 4456 | options.dev_null_fd, |
| 4531 | 4457 | ); |
| 4532 | | try ev.setUpChildIo( |
| 4458 | try setUpChildIo( |
| 4533 | 4459 | sync, |
| 4534 | 4460 | options.spawn.stderr, |
| 4535 | 4461 | options.stderr_pipe, |
| ... | ... | @@ -4539,17 +4465,17 @@ fn setUpChild(ev: *Evented, sync: *CancelRegion.Sync, options: struct { |
| 4539 | 4465 | |
| 4540 | 4466 | switch (options.spawn.cwd) { |
| 4541 | 4467 | .inherit => {}, |
| 4542 | | .dir => |cwd_dir| try ev.fchdir(sync, cwd_dir.handle), |
| 4468 | .dir => |cwd_dir| try fchdir(sync, cwd_dir.handle), |
| 4543 | 4469 | .path => |cwd_path| { |
| 4544 | 4470 | var cwd_path_buffer: [PATH_MAX]u8 = undefined; |
| 4545 | 4471 | const cwd_path_posix = try pathToPosix(cwd_path, &cwd_path_buffer); |
| 4546 | | try ev.chdir(sync, cwd_path_posix); |
| 4472 | try chdir(sync, cwd_path_posix); |
| 4547 | 4473 | }, |
| 4548 | 4474 | } |
| 4549 | 4475 | |
| 4550 | 4476 | // Must happen after fchdir above, the cwd file descriptor might be |
| 4551 | 4477 | // equal to prog_fileno and be clobbered by this dup2 call. |
| 4552 | | 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); |
| 4553 | 4479 | |
| 4554 | 4480 | if (options.spawn.gid) |gid| { |
| 4555 | 4481 | switch (linux.errno(linux.setregid(gid, gid))) { |
| ... | ... | @@ -4589,7 +4515,7 @@ fn setUpChild(ev: *Evented, sync: *CancelRegion.Sync, options: struct { |
| 4589 | 4515 | } |
| 4590 | 4516 | } |
| 4591 | 4517 | |
| 4592 | | return ev.execv( |
| 4518 | return execv( |
| 4593 | 4519 | sync, |
| 4594 | 4520 | options.spawn.expand_arg0, |
| 4595 | 4521 | options.argv_buf.ptr[0].?, |
| ... | ... | @@ -4600,7 +4526,6 @@ fn setUpChild(ev: *Evented, sync: *CancelRegion.Sync, options: struct { |
| 4600 | 4526 | } |
| 4601 | 4527 | |
| 4602 | 4528 | fn setUpChildIo( |
| 4603 | | ev: *Evented, |
| 4604 | 4529 | sync: *CancelRegion.Sync, |
| 4605 | 4530 | stdio: process.SpawnOptions.StdIo, |
| 4606 | 4531 | pipe_fd: fd_t, |
| ... | ... | @@ -4608,13 +4533,13 @@ fn setUpChildIo( |
| 4608 | 4533 | dev_null_fd: fd_t, |
| 4609 | 4534 | ) !void { |
| 4610 | 4535 | switch (stdio) { |
| 4611 | | .pipe => try ev.dup2(sync, pipe_fd, std_fileno), |
| 4536 | .pipe => try dup2(sync, pipe_fd, std_fileno), |
| 4612 | 4537 | .close => _ = linux.close(std_fileno), |
| 4613 | 4538 | .inherit => {}, |
| 4614 | | .ignore => try ev.dup2(sync, dev_null_fd, std_fileno), |
| 4539 | .ignore => try dup2(sync, dev_null_fd, std_fileno), |
| 4615 | 4540 | .file => |file| { |
| 4616 | 4541 | if (file.flags.nonblocking) @panic("TODO implement setUpChildIo when nonblocking file is used"); |
| 4617 | | try ev.dup2(sync, file.handle, std_fileno); |
| 4542 | try dup2(sync, file.handle, std_fileno); |
| 4618 | 4543 | }, |
| 4619 | 4544 | } |
| 4620 | 4545 | } |
| ... | ... | @@ -4623,13 +4548,12 @@ pub const DupError = error{ |
| 4623 | 4548 | ProcessFdQuotaExceeded, |
| 4624 | 4549 | SystemResources, |
| 4625 | 4550 | } || Io.UnexpectedError || Io.Cancelable; |
| 4626 | | pub fn dup2(ev: *Evented, sync: *CancelRegion.Sync, old_fd: fd_t, new_fd: fd_t) DupError!void { |
| 4627 | | _ = ev; |
| 4551 | pub fn dup2(sync: *CancelRegion.Sync, old_fd: fd_t, new_fd: fd_t) DupError!void { |
| 4628 | 4552 | while (true) { |
| 4629 | 4553 | try sync.cancel_region.await(.nothing); |
| 4630 | 4554 | switch (linux.errno(linux.dup2(old_fd, new_fd))) { |
| 4631 | 4555 | .SUCCESS => {}, |
| 4632 | | .BUSY, .INTR => continue, |
| 4556 | .BUSY, .INTR => {}, |
| 4633 | 4557 | .INVAL => |err| return errnoBug(err), // invalid parameters |
| 4634 | 4558 | .BADF => |err| return errnoBug(err), // use after free |
| 4635 | 4559 | .MFILE => return error.ProcessFdQuotaExceeded, |
| ... | ... | @@ -4640,7 +4564,6 @@ pub fn dup2(ev: *Evented, sync: *CancelRegion.Sync, old_fd: fd_t, new_fd: fd_t) |
| 4640 | 4564 | } |
| 4641 | 4565 | |
| 4642 | 4566 | fn execv( |
| 4643 | | ev: *Evented, |
| 4644 | 4567 | sync: *CancelRegion.Sync, |
| 4645 | 4568 | arg0_expand: process.ArgExpansion, |
| 4646 | 4569 | file: [*:0]const u8, |
| ... | ... | @@ -4649,7 +4572,8 @@ fn execv( |
| 4649 | 4572 | PATH: []const u8, |
| 4650 | 4573 | ) process.ReplaceError { |
| 4651 | 4574 | const file_slice = std.mem.sliceTo(file, 0); |
| 4652 | | 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); |
| 4653 | 4577 | |
| 4654 | 4578 | // Use of PATH_MAX here is valid as the path_buf will be passed |
| 4655 | 4579 | // directly to the operating system in posixExecvPath. |
| ... | ... | @@ -4677,7 +4601,7 @@ fn execv( |
| 4677 | 4601 | .expand => child_argv[0] = full_path, |
| 4678 | 4602 | .no_expand => {}, |
| 4679 | 4603 | } |
| 4680 | | err = ev.execvPath(sync, full_path, child_argv, env_block); |
| 4604 | err = execvPath(sync, full_path, child_argv, env_block); |
| 4681 | 4605 | switch (err) { |
| 4682 | 4606 | error.AccessDenied => seen_eacces = true, |
| 4683 | 4607 | error.FileNotFound, error.NotDir => {}, |
| ... | ... | @@ -4689,13 +4613,11 @@ fn execv( |
| 4689 | 4613 | } |
| 4690 | 4614 | /// This function ignores PATH environment variable. |
| 4691 | 4615 | pub fn execvPath( |
| 4692 | | ev: *Evented, |
| 4693 | 4616 | sync: *CancelRegion.Sync, |
| 4694 | 4617 | path: [*:0]const u8, |
| 4695 | 4618 | child_argv: [*:null]const ?[*:0]const u8, |
| 4696 | 4619 | env_block: process.Environ.PosixBlock, |
| 4697 | 4620 | ) process.ReplaceError { |
| 4698 | | _ = ev; |
| 4699 | 4621 | try sync.cancel_region.await(.nothing); |
| 4700 | 4622 | switch (linux.errno(linux.execve(path, child_argv, env_block.slice.ptr))) { |
| 4701 | 4623 | .FAULT => |err| return errnoBug(err), // Bad pointer parameter. |
| ... | ... | @@ -4766,7 +4688,7 @@ fn childWait(userdata: ?*anyopaque, child: *process.Child) process.Child.WaitErr |
| 4766 | 4688 | child.resource_usage_statistics.rusage = rusage; |
| 4767 | 4689 | break; |
| 4768 | 4690 | }, |
| 4769 | | .INTR, .CANCELED => continue, |
| 4691 | .INTR, .CANCELED => {}, |
| 4770 | 4692 | .CHILD => |err| return errnoBug(err), // Double-free. |
| 4771 | 4693 | else => |err| return unexpectedErrno(err), |
| 4772 | 4694 | } |
| ... | ... | @@ -4781,7 +4703,7 @@ fn childWait(userdata: ?*anyopaque, child: *process.Child) process.Child.WaitErr |
| 4781 | 4703 | _, .CONTINUED => .{ .unknown = status }, |
| 4782 | 4704 | }; |
| 4783 | 4705 | }, |
| 4784 | | .INTR, .CANCELED => continue, |
| 4706 | .INTR, .CANCELED => {}, |
| 4785 | 4707 | .CHILD => |err| return errnoBug(err), // Double-free. |
| 4786 | 4708 | else => |err| return unexpectedErrno(err), |
| 4787 | 4709 | } |
| ... | ... | @@ -4798,7 +4720,7 @@ fn childKill(userdata: ?*anyopaque, child: *process.Child) void { |
| 4798 | 4720 | const pid = child.id.?; |
| 4799 | 4721 | while (true) switch (linux.errno(linux.kill(pid, .TERM))) { |
| 4800 | 4722 | .SUCCESS => break, |
| 4801 | | .INTR => continue, |
| 4723 | .INTR => {}, |
| 4802 | 4724 | .PERM => return, |
| 4803 | 4725 | .INVAL => |err| return errnoBug(err) catch {}, |
| 4804 | 4726 | .SRCH => |err| return errnoBug(err) catch {}, |
| ... | ... | @@ -4830,7 +4752,7 @@ fn childKill(userdata: ?*anyopaque, child: *process.Child) void { |
| 4830 | 4752 | ev.yield(null, .nothing); |
| 4831 | 4753 | switch (maybe_sync.cancel_region.errno()) { |
| 4832 | 4754 | .SUCCESS => return, |
| 4833 | | .INTR, .CANCELED => continue, |
| 4755 | .INTR, .CANCELED => {}, |
| 4834 | 4756 | .CHILD => |err| return errnoBug(err) catch {}, // Double-free. |
| 4835 | 4757 | else => |err| return unexpectedErrno(err) catch {}, |
| 4836 | 4758 | } |
| ... | ... | @@ -4839,15 +4761,15 @@ fn childKill(userdata: ?*anyopaque, child: *process.Child) void { |
| 4839 | 4761 | |
| 4840 | 4762 | fn childCleanup(ev: *Evented, child: *process.Child) void { |
| 4841 | 4763 | if (child.stdin) |*stdin| { |
| 4842 | | ev.close(stdin.handle); |
| 4764 | ev.closeAsync(stdin.handle); |
| 4843 | 4765 | child.stdin = null; |
| 4844 | 4766 | } |
| 4845 | 4767 | if (child.stdout) |*stdout| { |
| 4846 | | ev.close(stdout.handle); |
| 4768 | ev.closeAsync(stdout.handle); |
| 4847 | 4769 | child.stdout = null; |
| 4848 | 4770 | } |
| 4849 | 4771 | if (child.stderr) |*stderr| { |
| 4850 | | ev.close(stderr.handle); |
| 4772 | ev.closeAsync(stderr.handle); |
| 4851 | 4773 | child.stderr = null; |
| 4852 | 4774 | } |
| 4853 | 4775 | child.id = null; |
| ... | ... | @@ -4953,14 +4875,10 @@ fn sleep(userdata: ?*anyopaque, timeout: Io.Timeout) Io.Cancelable!void { |
| 4953 | 4875 | .resv = 0, |
| 4954 | 4876 | }; |
| 4955 | 4877 | ev.yield(null, .nothing); |
| 4956 | | switch (cancel_region.errno()) { |
| 4957 | | // Handles SUCCESS as well as clock not available and unexpected |
| 4958 | | // errors. The user had a chance to check clock resolution before |
| 4959 | | // getting here, which would have reported 0, making this a legal |
| 4960 | | // amount of time to sleep. |
| 4961 | | else => return, |
| 4962 | | .INTR, .CANCELED => return error.Canceled, |
| 4963 | | } |
| 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. |
| 4964 | 4882 | } |
| 4965 | 4883 | |
| 4966 | 4884 | fn random(userdata: ?*anyopaque, buffer: []u8) void { |
| ... | ... | @@ -5037,7 +4955,7 @@ fn netBindIp( |
| 5037 | 4955 | var maybe_sync: CancelRegion.Sync.Maybe = .{ .cancel_region = .init() }; |
| 5038 | 4956 | defer maybe_sync.deinit(ev); |
| 5039 | 4957 | const socket_fd = try ev.socket(&maybe_sync.cancel_region, family, options); |
| 5040 | | errdefer ev.close(socket_fd); |
| 4958 | errdefer ev.closeAsync(socket_fd); |
| 5041 | 4959 | var storage: PosixAddress = undefined; |
| 5042 | 4960 | var addr_len = addressToPosix(address, &storage); |
| 5043 | 4961 | try ev.bind(&maybe_sync.cancel_region, socket_fd, &storage.any, addr_len); |
| ... | ... | @@ -5204,23 +5122,19 @@ fn netReceive( |
| 5204 | 5122 | .data = data, |
| 5205 | 5123 | .control = if (msg.control) |ptr| @as([*]u8, @ptrCast(ptr))[0..msg.controllen] else message.control, |
| 5206 | 5124 | .flags = .{ |
| 5207 | | .eor = (msg.flags & linux.MSG.EOR) != 0, |
| 5208 | | .trunc = (msg.flags & linux.MSG.TRUNC) != 0, |
| 5209 | | .ctrunc = (msg.flags & linux.MSG.CTRUNC) != 0, |
| 5210 | | .oob = (msg.flags & linux.MSG.OOB) != 0, |
| 5211 | | .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, |
| 5212 | 5130 | }, |
| 5213 | 5131 | }; |
| 5214 | 5132 | message_i += 1; |
| 5215 | 5133 | continue; |
| 5216 | 5134 | }, |
| 5217 | 5135 | .AGAIN => unreachable, |
| 5218 | | .INTR, .CANCELED => { |
| 5219 | | if (deadline) |d| { |
| 5220 | | if (now(ev, d.clock).nanoseconds >= d.raw.nanoseconds) return .{ error.Timeout, message_i }; |
| 5221 | | } |
| 5222 | | continue; |
| 5223 | | }, |
| 5136 | .INTR, .CANCELED => if (deadline) |d| if (now(ev, d.clock).nanoseconds >= d.raw.nanoseconds) |
| 5137 | return .{ error.Timeout, message_i }, |
| 5224 | 5138 | |
| 5225 | 5139 | .BADF => |err| return .{ errnoBug(err), message_i }, |
| 5226 | 5140 | .NFILE => return .{ error.SystemFdQuotaExceeded, message_i }, |
| ... | ... | @@ -5323,7 +5237,7 @@ fn netShutdown( |
| 5323 | 5237 | ev.yield(null, .nothing); |
| 5324 | 5238 | switch (cancel_region.errno()) { |
| 5325 | 5239 | .SUCCESS => return, |
| 5326 | | .INTR, .CANCELED => continue, |
| 5240 | .INTR, .CANCELED => {}, |
| 5327 | 5241 | .BADF, .NOTSOCK, .INVAL => |err| return errnoBug(err), |
| 5328 | 5242 | .NOTCONN => return error.SocketUnconnected, |
| 5329 | 5243 | .NOBUFS => return error.SystemResources, |
| ... | ... | @@ -5393,7 +5307,7 @@ fn bind( |
| 5393 | 5307 | ev.yield(null, .nothing); |
| 5394 | 5308 | switch (cancel_region.errno()) { |
| 5395 | 5309 | .SUCCESS => return, |
| 5396 | | .INTR, .CANCELED => continue, |
| 5310 | .INTR, .CANCELED => {}, |
| 5397 | 5311 | .ADDRINUSE => return error.AddressInUse, |
| 5398 | 5312 | .BADF => |err| return errnoBug(err), // File descriptor used after closed. |
| 5399 | 5313 | .INVAL => |err| return errnoBug(err), // invalid parameters |
| ... | ... | @@ -5407,13 +5321,12 @@ fn bind( |
| 5407 | 5321 | } |
| 5408 | 5322 | } |
| 5409 | 5323 | |
| 5410 | | fn chdir(ev: *Evented, sync: *CancelRegion.Sync, path: [*:0]const u8) ChdirError!void { |
| 5411 | | _ = ev; |
| 5324 | fn chdir(sync: *CancelRegion.Sync, path: [*:0]const u8) ChdirError!void { |
| 5412 | 5325 | while (true) { |
| 5413 | 5326 | try sync.cancel_region.await(.nothing); |
| 5414 | 5327 | switch (linux.errno(linux.chdir(path))) { |
| 5415 | 5328 | .SUCCESS => return, |
| 5416 | | .INTR => continue, |
| 5329 | .INTR => {}, |
| 5417 | 5330 | .ACCES => return error.AccessDenied, |
| 5418 | 5331 | .IO => return error.FileSystem, |
| 5419 | 5332 | .LOOP => return error.SymLinkLoop, |
| ... | ... | @@ -5429,6 +5342,36 @@ fn chdir(ev: *Evented, sync: *CancelRegion.Sync, path: [*:0]const u8) ChdirError |
| 5429 | 5342 | } |
| 5430 | 5343 | |
| 5431 | 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 { |
| 5432 | 5375 | _ = ev; |
| 5433 | 5376 | const thread: *Thread = .current(); |
| 5434 | 5377 | thread.enqueue().* = .{ |
| ... | ... | @@ -5449,14 +5392,13 @@ fn close(ev: *Evented, fd: fd_t) void { |
| 5449 | 5392 | }; |
| 5450 | 5393 | } |
| 5451 | 5394 | |
| 5452 | | fn fchdir(ev: *Evented, sync: *CancelRegion.Sync, dir: fd_t) process.SetCurrentDirError!void { |
| 5453 | | _ = ev; |
| 5395 | fn fchdir(sync: *CancelRegion.Sync, dir: fd_t) process.SetCurrentDirError!void { |
| 5454 | 5396 | if (dir == linux.AT.FDCWD) return; |
| 5455 | 5397 | while (true) { |
| 5456 | 5398 | try sync.cancel_region.await(.nothing); |
| 5457 | 5399 | switch (linux.errno(linux.fchdir(dir))) { |
| 5458 | 5400 | .SUCCESS => return, |
| 5459 | | .INTR => continue, |
| 5401 | .INTR => {}, |
| 5460 | 5402 | .ACCES => return error.AccessDenied, |
| 5461 | 5403 | .NOTDIR => return error.NotDir, |
| 5462 | 5404 | .IO => return error.FileSystem, |
| ... | ... | @@ -5479,7 +5421,7 @@ fn fchmodat( |
| 5479 | 5421 | try sync.cancel_region.await(.nothing); |
| 5480 | 5422 | switch (linux.errno(linux.fchmodat2(dir, path, mode, flags))) { |
| 5481 | 5423 | .SUCCESS => return, |
| 5482 | | .INTR => continue, |
| 5424 | .INTR => {}, |
| 5483 | 5425 | .BADF => |err| return errnoBug(err), |
| 5484 | 5426 | .FAULT => |err| return errnoBug(err), |
| 5485 | 5427 | .INVAL => |err| return errnoBug(err), |
| ... | ... | @@ -5511,7 +5453,7 @@ fn fchownat( |
| 5511 | 5453 | try sync.cancel_region.await(.nothing); |
| 5512 | 5454 | switch (linux.errno(linux.fchownat(dir, path, owner, group, flags))) { |
| 5513 | 5455 | .SUCCESS => return, |
| 5514 | | .INTR => continue, |
| 5456 | .INTR => {}, |
| 5515 | 5457 | .BADF => |err| return errnoBug(err), // likely fd refers to directory opened without `Dir.OpenOptions.iterate` |
| 5516 | 5458 | .FAULT => |err| return errnoBug(err), |
| 5517 | 5459 | .INVAL => |err| return errnoBug(err), |
| ... | ... | @@ -5543,7 +5485,7 @@ fn flock( |
| 5543 | 5485 | .exclusive => LOCK.EX, |
| 5544 | 5486 | })))) { |
| 5545 | 5487 | .SUCCESS => return, |
| 5546 | | .INTR => continue, |
| 5488 | .INTR => {}, |
| 5547 | 5489 | .BADF => |err| return errnoBug(err), |
| 5548 | 5490 | .INVAL => |err| return errnoBug(err), // invalid parameters |
| 5549 | 5491 | .NOLCK => return error.SystemResources, |
| ... | ... | @@ -5593,7 +5535,7 @@ fn getsockname( |
| 5593 | 5535 | try sync.cancel_region.await(.nothing); |
| 5594 | 5536 | switch (linux.errno(linux.getsockname(socket_fd, addr, addr_len))) { |
| 5595 | 5537 | .SUCCESS => return, |
| 5596 | | .INTR => continue, |
| 5538 | .INTR => {}, |
| 5597 | 5539 | .BADF => |err| return errnoBug(err), // File descriptor used after closed. |
| 5598 | 5540 | .FAULT => |err| return errnoBug(err), |
| 5599 | 5541 | .INVAL => |err| return errnoBug(err), // invalid parameters |
| ... | ... | @@ -5634,7 +5576,7 @@ fn linkat( |
| 5634 | 5576 | ev.yield(null, .nothing); |
| 5635 | 5577 | switch (cancel_region.errno()) { |
| 5636 | 5578 | .SUCCESS => return, |
| 5637 | | .INTR, .CANCELED => continue, |
| 5579 | .INTR, .CANCELED => {}, |
| 5638 | 5580 | .ACCES => return error.AccessDenied, |
| 5639 | 5581 | .DQUOT => return error.DiskQuota, |
| 5640 | 5582 | .EXIST => return error.PathAlreadyExists, |
| ... | ... | @@ -5674,7 +5616,7 @@ fn lseek( |
| 5674 | 5616 | 8 => linux.lseek(fd, @bitCast(offset), whence), |
| 5675 | 5617 | })) { |
| 5676 | 5618 | .SUCCESS => return, |
| 5677 | | .INTR => continue, |
| 5619 | .INTR => {}, |
| 5678 | 5620 | .BADF => |err| return errnoBug(err), // File descriptor used after closed. |
| 5679 | 5621 | .INVAL => return error.Unseekable, |
| 5680 | 5622 | .OVERFLOW => return error.Unseekable, |
| ... | ... | @@ -5717,7 +5659,7 @@ fn openat( |
| 5717 | 5659 | const completion = cancel_region.completion(); |
| 5718 | 5660 | switch (completion.errno()) { |
| 5719 | 5661 | .SUCCESS => return completion.result, |
| 5720 | | .INTR, .CANCELED => continue, |
| 5662 | .INTR, .CANCELED => {}, |
| 5721 | 5663 | .FAULT => |err| return errnoBug(err), |
| 5722 | 5664 | .INVAL => return error.BadPathName, |
| 5723 | 5665 | .BADF => |err| return errnoBug(err), // File descriptor used after closed. |
| ... | ... | @@ -5779,7 +5721,7 @@ fn preadv( |
| 5779 | 5721 | const completion = cancel_region.completion(); |
| 5780 | 5722 | switch (completion.errno()) { |
| 5781 | 5723 | .SUCCESS => return @as(u32, @bitCast(completion.result)), |
| 5782 | | .INTR, .CANCELED => continue, |
| 5724 | .INTR, .CANCELED => {}, |
| 5783 | 5725 | .INVAL => |err| return errnoBug(err), |
| 5784 | 5726 | .FAULT => |err| return errnoBug(err), |
| 5785 | 5727 | .AGAIN => return error.WouldBlock, |
| ... | ... | @@ -5826,7 +5768,7 @@ fn pwritev( |
| 5826 | 5768 | const completion = cancel_region.completion(); |
| 5827 | 5769 | switch (completion.errno()) { |
| 5828 | 5770 | .SUCCESS => return @as(u32, @bitCast(completion.result)), |
| 5829 | | .INTR, .CANCELED => continue, |
| 5771 | .INTR, .CANCELED => {}, |
| 5830 | 5772 | .INVAL => |err| return errnoBug(err), |
| 5831 | 5773 | .FAULT => |err| return errnoBug(err), |
| 5832 | 5774 | .AGAIN => return error.WouldBlock, |
| ... | ... | @@ -5876,7 +5818,7 @@ fn realPath( |
| 5876 | 5818 | const rc = linux.readlink(proc_path, out_buffer.ptr, out_buffer.len); |
| 5877 | 5819 | switch (linux.errno(rc)) { |
| 5878 | 5820 | .SUCCESS => return rc, |
| 5879 | | .INTR => continue, |
| 5821 | .INTR => {}, |
| 5880 | 5822 | .ACCES => return error.AccessDenied, |
| 5881 | 5823 | .FAULT => |err| return errnoBug(err), |
| 5882 | 5824 | .IO => return error.FileSystem, |
| ... | ... | @@ -5921,7 +5863,7 @@ fn renameat( |
| 5921 | 5863 | ev.yield(null, .nothing); |
| 5922 | 5864 | switch (cancel_region.errno()) { |
| 5923 | 5865 | .SUCCESS => return, |
| 5924 | | .INTR, .CANCELED => continue, |
| 5866 | .INTR, .CANCELED => {}, |
| 5925 | 5867 | .ACCES => return error.AccessDenied, |
| 5926 | 5868 | .PERM => return error.PermissionDenied, |
| 5927 | 5869 | .BUSY => return error.FileBusy, |
| ... | ... | @@ -5988,7 +5930,7 @@ fn setsockopt( |
| 5988 | 5930 | ev.yield(null, .nothing); |
| 5989 | 5931 | switch (cancel_region.errno()) { |
| 5990 | 5932 | .SUCCESS => return, |
| 5991 | | .INTR, .CANCELED => continue, |
| 5933 | .INTR, .CANCELED => {}, |
| 5992 | 5934 | .BADF => |err| return errnoBug(err), // File descriptor used after closed. |
| 5993 | 5935 | .NOTSOCK => |err| return errnoBug(err), |
| 5994 | 5936 | .INVAL => |err| return errnoBug(err), |
| ... | ... | @@ -6039,7 +5981,7 @@ fn socket( |
| 6039 | 5981 | const completion = cancel_region.completion(); |
| 6040 | 5982 | switch (completion.errno()) { |
| 6041 | 5983 | .SUCCESS => break completion.result, |
| 6042 | | .INTR, .CANCELED => continue, |
| 5984 | .INTR, .CANCELED => {}, |
| 6043 | 5985 | .AFNOSUPPORT => return error.AddressFamilyUnsupported, |
| 6044 | 5986 | .INVAL => return error.ProtocolUnsupportedBySystem, |
| 6045 | 5987 | .MFILE => return error.ProcessFdQuotaExceeded, |
| ... | ... | @@ -6051,7 +5993,7 @@ fn socket( |
| 6051 | 5993 | else => |err| return unexpectedErrno(err), |
| 6052 | 5994 | } |
| 6053 | 5995 | }; |
| 6054 | | errdefer ev.close(socket_fd); |
| 5996 | errdefer ev.closeAsync(socket_fd); |
| 6055 | 5997 | |
| 6056 | 5998 | if (options.ip6_only) { |
| 6057 | 5999 | if (linux.IPV6 == void) return error.OptionUnsupported; |
| ... | ... | @@ -6101,7 +6043,7 @@ fn statx( |
| 6101 | 6043 | ev.yield(null, .nothing); |
| 6102 | 6044 | switch (cancel_region.errno()) { |
| 6103 | 6045 | .SUCCESS => return statFromLinux(&statx_buf), |
| 6104 | | .INTR, .CANCELED => continue, |
| 6046 | .INTR, .CANCELED => {}, |
| 6105 | 6047 | .ACCES => return error.AccessDenied, |
| 6106 | 6048 | .BADF => |err| return errnoBug(err), // File descriptor used after closed. |
| 6107 | 6049 | .FAULT => |err| return errnoBug(err), |
| ... | ... | @@ -6140,7 +6082,7 @@ fn utimensat( |
| 6140 | 6082 | try sync.cancel_region.await(.nothing); |
| 6141 | 6083 | switch (linux.errno(linux.utimensat(dir, path, times, flags))) { |
| 6142 | 6084 | .SUCCESS => return, |
| 6143 | | .INTR => continue, |
| 6085 | .INTR => {}, |
| 6144 | 6086 | .BADF => |err| return errnoBug(err), // always a race condition |
| 6145 | 6087 | .FAULT => |err| return errnoBug(err), |
| 6146 | 6088 | .INVAL => |err| return errnoBug(err), |
| ... | ... | @@ -6152,19 +6094,33 @@ fn utimensat( |
| 6152 | 6094 | } |
| 6153 | 6095 | } |
| 6154 | 6096 | |
| 6155 | | fn writeAll( |
| 6156 | | ev: *Evented, |
| 6157 | | cancel_region: *CancelRegion, |
| 6158 | | fd: fd_t, |
| 6159 | | buffer: []const u8, |
| 6160 | | ) (File.Writer.Error || error{EndOfStream})!void { |
| 6097 | fn writeAllSync(sync: *CancelRegion.Sync, fd: fd_t, buffer: []const u8) File.Writer.Error!void { |
| 6161 | 6098 | var index: usize = 0; |
| 6162 | | while (buffer.len - index != 0) { |
| 6163 | | const len = try ev.pwritev(cancel_region, fd, &.{ |
| 6164 | | .{ .base = buffer[index..].ptr, .len = buffer.len - index }, |
| 6165 | | }, null); |
| 6166 | | if (len == 0) return error.EndOfStream; |
| 6167 | | 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 | } |
| 6168 | 6124 | } |
| 6169 | 6125 | } |
| 6170 | 6126 | |