| author | |
| committer | |
| log | 59073484baf89072aa02f23fe9a09662e5def100 |
| tree | 80091d7af70172ec2c656dce9b407bc21119ae78 |
| parent | e5454ff780ae4571cfa71a2edb6f4287eb8cf4de |
5 files changed, 211 insertions(+), 58 deletions(-)
lib/std/Io.zig+28-1| ... | ... | @@ -257,6 +257,9 @@ pub const VTable = struct { |
| 257 | 257 | pub const Operation = union(enum) { |
| 258 | 258 | file_read_streaming: FileReadStreaming, |
| 259 | 259 | file_write_streaming: FileWriteStreaming, |
| 260 | /// On Windows this is NtDeviceIoControlFile. On POSIX this is ioctl. On | |
| 261 | /// other systems this tag is unreachable. | |
| 262 | device_io_control: DeviceIoControl, | |
| 260 | 263 | |
| 261 | 264 | pub const Tag = @typeInfo(Operation).@"union".tag_type.?; |
| 262 | 265 | |
| ... | ... | @@ -324,13 +327,37 @@ pub const Operation = union(enum) { |
| 324 | 327 | pub const Result = Error!usize; |
| 325 | 328 | }; |
| 326 | 329 | |
| 330 | pub const DeviceIoControl = switch (builtin.os.tag) { | |
| 331 | .wasi => noreturn, | |
| 332 | .windows => struct { | |
| 333 | file: File, | |
| 334 | IoControlCode: std.os.windows.CTL_CODE, | |
| 335 | InputBuffer: ?*const anyopaque, | |
| 336 | InputBufferLength: u32, | |
| 337 | OutputBuffer: ?*anyopaque, | |
| 338 | OutputBufferLength: u32, | |
| 339 | ||
| 340 | pub const Result = std.os.windows.IO_STATUS_BLOCK; | |
| 341 | }, | |
| 342 | else => struct { | |
| 343 | file: File, | |
| 344 | /// Device-dependent operation code. | |
| 345 | code: u32, | |
| 346 | arg: ?*anyopaque, | |
| 347 | ||
| 348 | /// Device and operation dependent result. Negative values are | |
| 349 | /// negative errno. | |
| 350 | pub const Result = i32; | |
| 351 | }, | |
| 352 | }; | |
| 353 | ||
| 327 | 354 | pub const Result = Result: { |
| 328 | 355 | const operation_fields = @typeInfo(Operation).@"union".fields; |
| 329 | 356 | var field_names: [operation_fields.len][]const u8 = undefined; |
| 330 | 357 | var field_types: [operation_fields.len]type = undefined; |
| 331 | 358 | for (operation_fields, &field_names, &field_types) |field, *field_name, *field_type| { |
| 332 | 359 | field_name.* = field.name; |
| 333 | field_type.* = field.type.Result; | |
| 360 | field_type.* = if (field.type == noreturn) noreturn else field.type.Result; | |
| 334 | 361 | } |
| 335 | 362 | break :Result @Union(.auto, Tag, &field_names, &field_types, &@splat(.{})); |
| 336 | 363 | }; |
lib/std/Io/Threaded.zig+163-25| ... | ... | @@ -2500,6 +2500,9 @@ fn operate(userdata: ?*anyopaque, operation: Io.Operation) Io.Cancelable!Io.Oper |
| 2500 | 2500 | else => |e| e, |
| 2501 | 2501 | }, |
| 2502 | 2502 | }, |
| 2503 | .device_io_control => |*o| return .{ | |
| 2504 | .device_io_control = try deviceIoControl(t, o), | |
| 2505 | }, | |
| 2503 | 2506 | } |
| 2504 | 2507 | } |
| 2505 | 2508 | |
| ... | ... | @@ -2531,6 +2534,14 @@ fn batchAwaitAsync(userdata: ?*anyopaque, b: *Io.Batch) Io.Cancelable!void { |
| 2531 | 2534 | poll_buffer[poll_len] = .{ .fd = o.file.handle, .events = posix.POLL.OUT, .revents = 0 }; |
| 2532 | 2535 | poll_len += 1; |
| 2533 | 2536 | }, |
| 2537 | .device_io_control => |o| { | |
| 2538 | poll_buffer[poll_len] = .{ | |
| 2539 | .fd = o.file.handle, | |
| 2540 | .events = posix.POLL.OUT | posix.POLL.IN | posix.POLL.ERR, | |
| 2541 | .revents = 0, | |
| 2542 | }; | |
| 2543 | poll_len += 1; | |
| 2544 | }, | |
| 2534 | 2545 | } |
| 2535 | 2546 | index = submission.node.next; |
| 2536 | 2547 | } |
| ... | ... | @@ -2696,6 +2707,7 @@ fn batchAwaitConcurrent(userdata: ?*anyopaque, b: *Io.Batch, timeout: Io.Timeout |
| 2696 | 2707 | switch (submission.operation) { |
| 2697 | 2708 | .file_read_streaming => |o| try poll_storage.add(o.file, posix.POLL.IN), |
| 2698 | 2709 | .file_write_streaming => |o| try poll_storage.add(o.file, posix.POLL.OUT), |
| 2710 | .device_io_control => |o| try poll_storage.add(o.file, posix.POLL.IN | posix.POLL.OUT | posix.POLL.ERR), | |
| 2699 | 2711 | } |
| 2700 | 2712 | index = submission.node.next; |
| 2701 | 2713 | } |
| ... | ... | @@ -2874,6 +2886,7 @@ fn batchApc(apc_context: ?*anyopaque, iosb: *windows.IO_STATUS_BLOCK, _: windows |
| 2874 | 2886 | const result: Io.Operation.Result = switch (pending.tag) { |
| 2875 | 2887 | .file_read_streaming => .{ .file_read_streaming = ntReadFileResult(iosb) }, |
| 2876 | 2888 | .file_write_streaming => .{ .file_write_streaming = ntWriteFileResult(iosb) }, |
| 2889 | .device_io_control => .{ .device_io_control = iosb.* }, | |
| 2877 | 2890 | }; |
| 2878 | 2891 | storage.* = .{ .completion = .{ .node = .{ .next = .none }, .result = result } }; |
| 2879 | 2892 | }, |
| ... | ... | @@ -3020,6 +3033,59 @@ fn batchAwaitWindows(b: *Io.Batch, concurrency: bool) error{ Canceled, Concurren |
| 3020 | 3033 | else => |status| { |
| 3021 | 3034 | syscall.finish(); |
| 3022 | 3035 | |
| 3036 | context.iosb.u.Status = status; | |
| 3037 | batchApc(b, &context.iosb, 0); | |
| 3038 | break; | |
| 3039 | }, | |
| 3040 | }; | |
| 3041 | } | |
| 3042 | }, | |
| 3043 | .device_io_control => |o| { | |
| 3044 | if (o.file.flags.nonblocking) { | |
| 3045 | context.file = o.file.handle; | |
| 3046 | switch (windows.ntdll.NtDeviceIoControlFile( | |
| 3047 | o.file.handle, | |
| 3048 | null, // event | |
| 3049 | &batchApc, | |
| 3050 | b, | |
| 3051 | &context.iosb, | |
| 3052 | o.IoControlCode, | |
| 3053 | o.InputBuffer, | |
| 3054 | o.InputBufferLength, | |
| 3055 | o.OutputBuffer, | |
| 3056 | o.OutputBufferLength, | |
| 3057 | )) { | |
| 3058 | .PENDING, .SUCCESS => {}, | |
| 3059 | .CANCELLED => unreachable, | |
| 3060 | else => |status| { | |
| 3061 | context.iosb.u.Status = status; | |
| 3062 | batchApc(b, &context.iosb, 0); | |
| 3063 | }, | |
| 3064 | } | |
| 3065 | } else { | |
| 3066 | if (concurrency) return error.ConcurrencyUnavailable; | |
| 3067 | ||
| 3068 | const syscall: Syscall = try .start(); | |
| 3069 | while (true) switch (windows.ntdll.NtDeviceIoControlFile( | |
| 3070 | o.file.handle, | |
| 3071 | null, // event | |
| 3072 | null, // APC routine | |
| 3073 | null, // APC context | |
| 3074 | &context.iosb, | |
| 3075 | o.IoControlCode, | |
| 3076 | o.InputBuffer, | |
| 3077 | o.InputBufferLength, | |
| 3078 | o.OutputBuffer, | |
| 3079 | o.OutputBufferLength, | |
| 3080 | )) { | |
| 3081 | .PENDING => unreachable, // unrecoverable: wrong File nonblocking flag | |
| 3082 | .CANCELLED => { | |
| 3083 | try syscall.checkCancel(); | |
| 3084 | continue; | |
| 3085 | }, | |
| 3086 | else => |status| { | |
| 3087 | syscall.finish(); | |
| 3088 | ||
| 3023 | 3089 | context.iosb.u.Status = status; |
| 3024 | 3090 | batchApc(b, &context.iosb, 0); |
| 3025 | 3091 | break; |
| ... | ... | @@ -12986,31 +13052,18 @@ fn netInterfaceNameResolve( |
| 12986 | 13052 | }; |
| 12987 | 13053 | |
| 12988 | 13054 | const syscall: Syscall = try .start(); |
| 12989 | while (true) { | |
| 12990 | switch (posix.errno(posix.system.ioctl(sock_fd, posix.SIOCGIFINDEX, @intFromPtr(&ifr)))) { | |
| 12991 | .SUCCESS => { | |
| 12992 | syscall.finish(); | |
| 12993 | return .{ .index = @bitCast(ifr.ifru.ivalue) }; | |
| 12994 | }, | |
| 12995 | .INTR => { | |
| 12996 | try syscall.checkCancel(); | |
| 12997 | continue; | |
| 12998 | }, | |
| 12999 | else => |e| { | |
| 13000 | syscall.finish(); | |
| 13001 | switch (e) { | |
| 13002 | .INVAL => |err| return errnoBug(err), // Bad parameters. | |
| 13003 | .NOTTY => |err| return errnoBug(err), | |
| 13004 | .NXIO => |err| return errnoBug(err), | |
| 13005 | .BADF => |err| return errnoBug(err), // File descriptor used after closed. | |
| 13006 | .FAULT => |err| return errnoBug(err), // Bad pointer parameter. | |
| 13007 | .IO => |err| return errnoBug(err), // sock_fd is not a file descriptor | |
| 13008 | .NODEV => return error.InterfaceNotFound, | |
| 13009 | else => |err| return posix.unexpectedErrno(err), | |
| 13010 | } | |
| 13011 | }, | |
| 13012 | } | |
| 13013 | } | |
| 13055 | while (true) switch (posix.errno(posix.system.ioctl(sock_fd, posix.SIOCGIFINDEX, @intFromPtr(&ifr)))) { | |
| 13056 | .SUCCESS => { | |
| 13057 | syscall.finish(); | |
| 13058 | return .{ .index = @bitCast(ifr.ifru.ivalue) }; | |
| 13059 | }, | |
| 13060 | .INTR => { | |
| 13061 | try syscall.checkCancel(); | |
| 13062 | continue; | |
| 13063 | }, | |
| 13064 | .NODEV => return syscall.fail(error.InterfaceNotFound), | |
| 13065 | else => |err| return syscall.unexpectedErrno(err), | |
| 13066 | }; | |
| 13014 | 13067 | } |
| 13015 | 13068 | |
| 13016 | 13069 | if (is_windows) { |
| ... | ... | @@ -17849,3 +17902,88 @@ fn mmSyncWrite(file: File, memory: []u8, offset: u64) File.WritePositionalError! |
| 17849 | 17902 | } |
| 17850 | 17903 | } |
| 17851 | 17904 | } |
| 17905 | ||
| 17906 | fn deviceIoControl(t: *Threaded, o: *const Io.Operation.DeviceIoControl) Io.Cancelable!Io.Operation.DeviceIoControl.Result { | |
| 17907 | _ = t; | |
| 17908 | if (is_windows) { | |
| 17909 | var iosb: windows.IO_STATUS_BLOCK = undefined; | |
| 17910 | if (o.file.flags.nonblocking) { | |
| 17911 | var done: bool = false; | |
| 17912 | switch (windows.ntdll.NtDeviceIoControlFile( | |
| 17913 | o.file.handle, | |
| 17914 | null, // event | |
| 17915 | flagApc, | |
| 17916 | &done, // APC context | |
| 17917 | &iosb, | |
| 17918 | o.IoControlCode, | |
| 17919 | o.InputBuffer, | |
| 17920 | o.InputBufferLength, | |
| 17921 | o.OutputBuffer, | |
| 17922 | o.OutputBufferLength, | |
| 17923 | )) { | |
| 17924 | // We must wait for the APC routine. | |
| 17925 | .PENDING, .SUCCESS => while (!done) { | |
| 17926 | // Once we get here we must not return from the function until the | |
| 17927 | // operation completes, thereby releasing reference to io_status_block. | |
| 17928 | const alertable_syscall = AlertableSyscall.start() catch |err| switch (err) { | |
| 17929 | error.Canceled => |e| { | |
| 17930 | var cancel_iosb: windows.IO_STATUS_BLOCK = undefined; | |
| 17931 | _ = windows.ntdll.NtCancelIoFileEx(o.file.handle, &iosb, &cancel_iosb); | |
| 17932 | while (!done) waitForApcOrAlert(); | |
| 17933 | return e; | |
| 17934 | }, | |
| 17935 | }; | |
| 17936 | waitForApcOrAlert(); | |
| 17937 | alertable_syscall.finish(); | |
| 17938 | }, | |
| 17939 | else => |status| iosb.u.Status = status, | |
| 17940 | } | |
| 17941 | } else { | |
| 17942 | const syscall: Syscall = try .start(); | |
| 17943 | while (true) switch (windows.ntdll.NtDeviceIoControlFile( | |
| 17944 | o.file.handle, | |
| 17945 | null, // event | |
| 17946 | null, // APC routine | |
| 17947 | null, // APC context | |
| 17948 | &iosb, | |
| 17949 | o.IoControlCode, | |
| 17950 | o.InputBuffer, | |
| 17951 | o.InputBufferLength, | |
| 17952 | o.OutputBuffer, | |
| 17953 | o.OutputBufferLength, | |
| 17954 | )) { | |
| 17955 | .PENDING => unreachable, // unrecoverable: wrong asynchronous flag | |
| 17956 | .CANCELLED => { | |
| 17957 | try syscall.checkCancel(); | |
| 17958 | continue; | |
| 17959 | }, | |
| 17960 | else => |status| { | |
| 17961 | syscall.finish(); | |
| 17962 | iosb.u.Status = status; | |
| 17963 | break; | |
| 17964 | }, | |
| 17965 | }; | |
| 17966 | } | |
| 17967 | return iosb; | |
| 17968 | } else { | |
| 17969 | const syscall: Syscall = try .start(); | |
| 17970 | while (true) { | |
| 17971 | const rc = posix.system.ioctl(o.file.handle, @bitCast(o.code), @intFromPtr(o.arg)); | |
| 17972 | switch (posix.errno(rc)) { | |
| 17973 | .SUCCESS => { | |
| 17974 | syscall.finish(); | |
| 17975 | if (@TypeOf(rc) == usize) return @bitCast(@as(u32, @truncate(rc))); | |
| 17976 | return rc; | |
| 17977 | }, | |
| 17978 | .INTR => { | |
| 17979 | try syscall.checkCancel(); | |
| 17980 | continue; | |
| 17981 | }, | |
| 17982 | else => |err| { | |
| 17983 | syscall.finish(); | |
| 17984 | return -@as(i32, @intFromEnum(err)); | |
| 17985 | }, | |
| 17986 | } | |
| 17987 | } | |
| 17988 | } | |
| 17989 | } |
lib/std/Progress.zig+14-9| ... | ... | @@ -573,7 +573,7 @@ fn updateTask(io: Io) void { |
| 573 | 573 | { |
| 574 | 574 | const resize_flag = wait(io, global_progress.initial_delay_ns); |
| 575 | 575 | if (@atomicLoad(bool, &global_progress.done, .monotonic)) return; |
| 576 | maybeUpdateSize(resize_flag); | |
| 576 | maybeUpdateSize(io, resize_flag) catch return; | |
| 577 | 577 | |
| 578 | 578 | const buffer, _ = computeRedraw(&serialized_buffer); |
| 579 | 579 | if (io.vtable.tryLockStderr(io.userdata, null) catch return) |locked_stderr| { |
| ... | ... | @@ -592,7 +592,7 @@ fn updateTask(io: Io) void { |
| 592 | 592 | return clearWrittenWithEscapeCodes(stderr.file_writer) catch {}; |
| 593 | 593 | } |
| 594 | 594 | |
| 595 | maybeUpdateSize(resize_flag); | |
| 595 | maybeUpdateSize(io, resize_flag) catch return; | |
| 596 | 596 | |
| 597 | 597 | const buffer, _ = computeRedraw(&serialized_buffer); |
| 598 | 598 | if (io.vtable.tryLockStderr(io.userdata, null) catch return) |locked_stderr| { |
| ... | ... | @@ -622,7 +622,7 @@ fn windowsApiUpdateTask(io: Io) void { |
| 622 | 622 | { |
| 623 | 623 | const resize_flag = wait(io, global_progress.initial_delay_ns); |
| 624 | 624 | if (@atomicLoad(bool, &global_progress.done, .monotonic)) return; |
| 625 | maybeUpdateSize(resize_flag); | |
| 625 | maybeUpdateSize(io, resize_flag) catch return; | |
| 626 | 626 | |
| 627 | 627 | const buffer, const nl_n = computeRedraw(&serialized_buffer); |
| 628 | 628 | if (io.vtable.tryLockStderr(io.userdata, null) catch return) |locked_stderr| { |
| ... | ... | @@ -643,7 +643,7 @@ fn windowsApiUpdateTask(io: Io) void { |
| 643 | 643 | return clearWrittenWindowsApi() catch {}; |
| 644 | 644 | } |
| 645 | 645 | |
| 646 | maybeUpdateSize(resize_flag); | |
| 646 | maybeUpdateSize(io, resize_flag) catch return; | |
| 647 | 647 | |
| 648 | 648 | const buffer, const nl_n = computeRedraw(&serialized_buffer); |
| 649 | 649 | if (io.vtable.tryLockStderr(io.userdata, null) catch return) |locked_stderr| { |
| ... | ... | @@ -1484,15 +1484,15 @@ fn writevNonblock(io: Io, file: Io.File, iov: [][]const u8) Io.File.Writer.Error |
| 1484 | 1484 | } |
| 1485 | 1485 | } |
| 1486 | 1486 | |
| 1487 | fn maybeUpdateSize(resize_flag: bool) void { | |
| 1487 | fn maybeUpdateSize(io: Io, resize_flag: bool) !void { | |
| 1488 | 1488 | if (!resize_flag) return; |
| 1489 | 1489 | |
| 1490 | const fd = global_progress.terminal.handle; | |
| 1490 | const file = global_progress.terminal; | |
| 1491 | 1491 | |
| 1492 | 1492 | if (is_windows) { |
| 1493 | 1493 | var info: windows.CONSOLE_SCREEN_BUFFER_INFO = undefined; |
| 1494 | 1494 | |
| 1495 | if (windows.kernel32.GetConsoleScreenBufferInfo(fd, &info) != windows.FALSE) { | |
| 1495 | if (windows.kernel32.GetConsoleScreenBufferInfo(file.handle, &info) != windows.FALSE) { | |
| 1496 | 1496 | // In the old Windows console, dwSize.Y is the line count of the |
| 1497 | 1497 | // entire scrollback buffer, so we use this instead so that we |
| 1498 | 1498 | // always get the size of the screen. |
| ... | ... | @@ -1512,8 +1512,13 @@ fn maybeUpdateSize(resize_flag: bool) void { |
| 1512 | 1512 | .ypixel = 0, |
| 1513 | 1513 | }; |
| 1514 | 1514 | |
| 1515 | const err = posix.system.ioctl(fd, posix.T.IOCGWINSZ, @intFromPtr(&winsize)); | |
| 1516 | if (posix.errno(err) == .SUCCESS) { | |
| 1515 | const err = (try io.operate(.{ .device_io_control = .{ | |
| 1516 | .file = file, | |
| 1517 | .code = posix.T.IOCGWINSZ, | |
| 1518 | .arg = &winsize, | |
| 1519 | } })).device_io_control; | |
| 1520 | ||
| 1521 | if (err >= 0) { | |
| 1517 | 1522 | global_progress.rows = winsize.row; |
| 1518 | 1523 | global_progress.cols = winsize.col; |
| 1519 | 1524 | } else { |
lib/std/c.zig+6-1| ... | ... | @@ -10731,7 +10731,6 @@ pub extern "c" fn sysctlnametomib(name: [*:0]const u8, mibp: ?*c_int, sizep: ?*u |
| 10731 | 10731 | pub extern "c" fn tcgetattr(fd: fd_t, termios_p: *termios) c_int; |
| 10732 | 10732 | pub extern "c" fn tcsetattr(fd: fd_t, optional_action: TCSA, termios_p: *const termios) c_int; |
| 10733 | 10733 | pub extern "c" fn fcntl(fd: fd_t, cmd: c_int, ...) c_int; |
| 10734 | pub extern "c" fn ioctl(fd: fd_t, request: c_int, ...) c_int; | |
| 10735 | 10734 | pub extern "c" fn uname(buf: *utsname) c_int; |
| 10736 | 10735 | |
| 10737 | 10736 | pub extern "c" fn gethostname(name: [*]u8, len: usize) c_int; |
| ... | ... | @@ -11108,6 +11107,11 @@ pub const clock_nanosleep = switch (native_os) { |
| 11108 | 11107 | else => {}, |
| 11109 | 11108 | }; |
| 11110 | 11109 | |
| 11110 | pub const ioctl = switch (native_os) { | |
| 11111 | .windows, .wasi => {}, | |
| 11112 | else => private.ioctl, | |
| 11113 | }; | |
| 11114 | ||
| 11111 | 11115 | // OS-specific bits. These are protected from being used on the wrong OS by |
| 11112 | 11116 | // comptime assertions inside each OS-specific file. |
| 11113 | 11117 | |
| ... | ... | @@ -11495,6 +11499,7 @@ const private = struct { |
| 11495 | 11499 | }; |
| 11496 | 11500 | extern "c" fn getrusage(who: c_int, usage: *rusage) c_int; |
| 11497 | 11501 | extern "c" fn gettimeofday(noalias tv: ?*timeval, noalias tz: ?*timezone) c_int; |
| 11502 | extern "c" fn ioctl(fd: fd_t, request: c_int, ...) c_int; | |
| 11498 | 11503 | extern "c" fn msync(addr: *align(page_size) const anyopaque, len: usize, flags: c_int) c_int; |
| 11499 | 11504 | extern "c" fn nanosleep(rqtp: *const timespec, rmtp: ?*timespec) c_int; |
| 11500 | 11505 | extern "c" fn clock_nanosleep(clockid: clockid_t, flags: TIMER, t: *const timespec, remain: ?*timespec) c_int; |
lib/std/posix.zig-22| ... | ... | @@ -1800,28 +1800,6 @@ pub fn name_to_handle_atZ( |
| 1800 | 1800 | } |
| 1801 | 1801 | } |
| 1802 | 1802 | |
| 1803 | pub const IoCtl_SIOCGIFINDEX_Error = error{ | |
| 1804 | FileSystem, | |
| 1805 | InterfaceNotFound, | |
| 1806 | } || UnexpectedError; | |
| 1807 | ||
| 1808 | pub fn ioctl_SIOCGIFINDEX(fd: fd_t, ifr: *ifreq) IoCtl_SIOCGIFINDEX_Error!void { | |
| 1809 | while (true) { | |
| 1810 | switch (errno(system.ioctl(fd, SIOCGIFINDEX, @intFromPtr(ifr)))) { | |
| 1811 | .SUCCESS => return, | |
| 1812 | .INVAL => unreachable, // Bad parameters. | |
| 1813 | .NOTTY => unreachable, | |
| 1814 | .NXIO => unreachable, | |
| 1815 | .BADF => unreachable, // Always a race condition. | |
| 1816 | .FAULT => unreachable, // Bad pointer parameter. | |
| 1817 | .INTR => continue, | |
| 1818 | .IO => return error.FileSystem, | |
| 1819 | .NODEV => return error.InterfaceNotFound, | |
| 1820 | else => |err| return unexpectedErrno(err), | |
| 1821 | } | |
| 1822 | } | |
| 1823 | } | |
| 1824 | ||
| 1825 | 1803 | pub const lfs64_abi = native_os == .linux and builtin.link_libc and (builtin.abi.isGnu() or builtin.abi.isAndroid()); |
| 1826 | 1804 | |
| 1827 | 1805 | /// Whether or not `error.Unexpected` will print its value and a stack trace. |