From 45bce27b8fecda4fba1c22dd191030af29ccbc6f Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Fri, 1 May 2020 23:17:15 -0400 Subject: [PATCH] cleanup and fixes. behavior tests passing with evented I/O --- lib/std/child_process.zig | 23 ++++-------- lib/std/debug.zig | 6 +-- lib/std/fs.zig | 57 ++++++++++++++++------------- lib/std/fs/file.zig | 77 +++++++++++++++++---------------------- lib/std/fs/test.zig | 12 +++++- lib/std/io.zig | 25 +++++++++---- lib/std/net.zig | 7 +--- lib/std/os.zig | 12 +++--- lib/std/os/windows.zig | 35 +++++++++++++----- lib/std/pdb.zig | 2 +- 10 files changed, 137 insertions(+), 119 deletions(-) diff --git a/lib/std/child_process.zig b/lib/std/child_process.zig index 12b76143ee..267c32a860 100644 --- a/lib/std/child_process.zig +++ b/lib/std/child_process.zig @@ -433,26 +433,17 @@ pub const ChildProcess = struct { // we are the parent const pid = @intCast(i32, pid_result); if (self.stdin_behavior == StdIo.Pipe) { - self.stdin = File{ - .handle = stdin_pipe[1], - .io_mode = std.io.mode, - }; + self.stdin = File{ .handle = stdin_pipe[1] }; } else { self.stdin = null; } if (self.stdout_behavior == StdIo.Pipe) { - self.stdout = File{ - .handle = stdout_pipe[0], - .io_mode = std.io.mode, - }; + self.stdout = File{ .handle = stdout_pipe[0] }; } else { self.stdout = null; } if (self.stderr_behavior == StdIo.Pipe) { - self.stderr = File{ - .handle = stderr_pipe[0], - .io_mode = std.io.mode, - }; + self.stderr = File{ .handle = stderr_pipe[0] }; } else { self.stderr = null; } @@ -835,8 +826,8 @@ const ErrInt = std.meta.Int(false, @sizeOf(anyerror) * 8); fn writeIntFd(fd: i32, value: ErrInt) !void { const file = File{ .handle = fd, - .io_mode = .blocking, - .async_block_allowed = File.async_block_allowed_yes, + .capable_io_mode = .blocking, + .intended_io_mode = .blocking, }; file.outStream().writeIntNative(u64, @intCast(u64, value)) catch return error.SystemResources; } @@ -844,8 +835,8 @@ fn writeIntFd(fd: i32, value: ErrInt) !void { fn readIntFd(fd: i32) !ErrInt { const file = File{ .handle = fd, - .io_mode = .blocking, - .async_block_allowed = File.async_block_allowed_yes, + .capable_io_mode = .blocking, + .intended_io_mode = .blocking, }; return @intCast(ErrInt, file.inStream().readIntNative(u64) catch return error.SystemResources); } diff --git a/lib/std/debug.zig b/lib/std/debug.zig index d3497b4dc8..97aa2b6e97 100644 --- a/lib/std/debug.zig +++ b/lib/std/debug.zig @@ -667,7 +667,7 @@ pub fn openSelfDebugInfo(allocator: *mem.Allocator) anyerror!DebugInfo { /// TODO resources https://github.com/ziglang/zig/issues/4353 fn openCoffDebugInfo(allocator: *mem.Allocator, coff_file_path: [:0]const u16) !ModuleDebugInfo { noasync { - const coff_file = try std.fs.openFileAbsoluteW(coff_file_path.ptr, .{}); + const coff_file = try std.fs.openFileAbsoluteW(coff_file_path, .{ .intended_io_mode = .blocking }); errdefer coff_file.close(); const coff_obj = try allocator.create(coff.Coff); @@ -1003,7 +1003,7 @@ fn openMachODebugInfo(allocator: *mem.Allocator, macho_file_path: []const u8) !M fn printLineFromFileAnyOs(out_stream: var, line_info: LineInfo) !void { // Need this to always block even in async I/O mode, because this could potentially // be called from e.g. the event loop code crashing. - var f = try fs.cwd().openFile(line_info.file_name, .{ .always_blocking = true }); + var f = try fs.cwd().openFile(line_info.file_name, .{ .intended_io_mode = .blocking }); defer f.close(); // TODO fstat and make sure that the file has the correct size @@ -1051,7 +1051,7 @@ const MachoSymbol = struct { fn mapWholeFile(path: []const u8) ![]align(mem.page_size) const u8 { noasync { - const file = try fs.cwd().openFile(path, .{ .always_blocking = true }); + const file = try fs.cwd().openFile(path, .{ .intended_io_mode = .blocking }); defer file.close(); const file_len = try math.cast(usize, try file.getEndPos()); diff --git a/lib/std/fs.zig b/lib/std/fs.zig index fc8a584a68..99b43a66e3 100644 --- a/lib/std/fs.zig +++ b/lib/std/fs.zig @@ -8,6 +8,8 @@ const Allocator = std.mem.Allocator; const assert = std.debug.assert; const math = std.math; +const is_darwin = std.Target.current.os.tag.isDarwin(); + pub const path = @import("fs/path.zig"); pub const File = @import("fs/file.zig").File; @@ -597,8 +599,11 @@ pub const Dir = struct { // Use the O_ locking flags if the os supports them // (Or if it's darwin, as darwin's `open` doesn't support the O_SYNC flag) - const has_flock_open_flags = @hasDecl(os, "O_EXLOCK") and !builtin.os.tag.isDarwin(); - const nonblocking_lock_flag = if (has_flock_open_flags and flags.lock_nonblocking) (os.O_NONBLOCK | os.O_SYNC) else @as(u32, 0); + const has_flock_open_flags = @hasDecl(os, "O_EXLOCK") and !is_darwin; + const nonblocking_lock_flag = if (has_flock_open_flags and flags.lock_nonblocking) + os.O_NONBLOCK | os.O_SYNC + else + @as(u32, 0); const lock_flag: u32 = if (has_flock_open_flags) switch (flags.lock) { .None => @as(u32, 0), .Shared => os.O_SHLOCK | nonblocking_lock_flag, @@ -612,7 +617,7 @@ pub const Dir = struct { @as(u32, os.O_WRONLY) else @as(u32, os.O_RDONLY); - const fd = if (need_async_thread and !flags.always_blocking) + const fd = if (flags.intended_io_mode != .blocking) try std.event.Loop.instance.?.openatZ(self.fd, sub_path, os_flags, 0) else try os.openatZ(self.fd, sub_path, os_flags, 0); @@ -629,11 +634,8 @@ pub const Dir = struct { return File{ .handle = fd, - .io_mode = .blocking, - .async_block_allowed = if (flags.always_blocking) - File.async_block_allowed_yes - else - File.async_block_allowed_no, + .capable_io_mode = .blocking, + .intended_io_mode = flags.intended_io_mode, }; } @@ -648,19 +650,16 @@ pub const Dir = struct { (if (flags.read) @as(u32, w.GENERIC_READ) else 0) | (if (flags.write) @as(u32, w.GENERIC_WRITE) else 0), .share_access = switch (flags.lock) { - .None => @as(?w.ULONG, null), + .None => w.FILE_SHARE_WRITE | w.FILE_SHARE_READ | w.FILE_SHARE_DELETE, .Shared => w.FILE_SHARE_READ | w.FILE_SHARE_DELETE, .Exclusive => w.FILE_SHARE_DELETE, }, .share_access_nonblocking = flags.lock_nonblocking, .creation = w.FILE_OPEN, - .enable_async_io = std.io.is_async and !flags.always_blocking, + .io_mode = flags.intended_io_mode, }), - .io_mode = .blocking, - .async_block_allowed = if (flags.always_blocking) - File.async_block_allowed_yes - else - File.async_block_allowed_no, + .capable_io_mode = std.io.default_mode, + .intended_io_mode = flags.intended_io_mode, }); } @@ -687,8 +686,11 @@ pub const Dir = struct { // Use the O_ locking flags if the os supports them // (Or if it's darwin, as darwin's `open` doesn't support the O_SYNC flag) - const has_flock_open_flags = @hasDecl(os, "O_EXLOCK") and !builtin.os.tag.isDarwin(); - const nonblocking_lock_flag = if (has_flock_open_flags and flags.lock_nonblocking) (os.O_NONBLOCK | os.O_SYNC) else @as(u32, 0); + const has_flock_open_flags = @hasDecl(os, "O_EXLOCK") and !is_darwin; + const nonblocking_lock_flag: u32 = if (has_flock_open_flags and flags.lock_nonblocking) + os.O_NONBLOCK | os.O_SYNC + else + 0; const lock_flag: u32 = if (has_flock_open_flags) switch (flags.lock) { .None => @as(u32, 0), .Shared => os.O_SHLOCK, @@ -700,7 +702,7 @@ pub const Dir = struct { (if (flags.truncate) @as(u32, os.O_TRUNC) else 0) | (if (flags.read) @as(u32, os.O_RDWR) else os.O_WRONLY) | (if (flags.exclusive) @as(u32, os.O_EXCL) else 0); - const fd = if (need_async_thread) + const fd = if (flags.intended_io_mode != .blocking) try std.event.Loop.instance.?.openatZ(self.fd, sub_path_c, os_flags, flags.mode) else try os.openatZ(self.fd, sub_path_c, os_flags, flags.mode); @@ -715,7 +717,11 @@ pub const Dir = struct { }); } - return File{ .handle = fd, .io_mode = .blocking }; + return File{ + .handle = fd, + .capable_io_mode = .blocking, + .intended_io_mode = flags.intended_io_mode, + }; } /// Same as `createFile` but Windows-only and the path parameter is @@ -739,9 +745,10 @@ pub const Dir = struct { @as(u32, w.FILE_OVERWRITE_IF) else @as(u32, w.FILE_OPEN_IF), - .enable_async_io = std.io.is_async, + .io_mode = flags.intended_io_mode, }), - .io_mode = .blocking, + .capable_io_mode = std.io.default_mode, + .intended_io_mode = flags.intended_io_mode, }); } @@ -1257,7 +1264,7 @@ pub const Dir = struct { @as(u32, os.W_OK) else @as(u32, os.F_OK); - const result = if (need_async_thread) + const result = if (need_async_thread and flags.intended_io_mode != .blocking) std.event.Loop.instance.?.faccessatZ(self.fd, sub_path, os_mode, 0) else os.faccessatZ(self.fd, sub_path, os_mode, 0); @@ -1399,8 +1406,8 @@ pub fn openFileAbsoluteZ(absolute_path_c: [*:0]const u8, flags: File.OpenFlags) } /// Same as `openFileAbsolute` but the path parameter is WTF-16 encoded. -pub fn openFileAbsoluteW(absolute_path_w: [*:0]const u16, flags: File.OpenFlags) File.OpenError!File { - assert(path.isAbsoluteWindowsW(absolute_path_w)); +pub fn openFileAbsoluteW(absolute_path_w: []const u16, flags: File.OpenFlags) File.OpenError!File { + assert(path.isAbsoluteWindowsWTF16(absolute_path_w)); return cwd().openFileW(absolute_path_w, flags); } @@ -1617,7 +1624,7 @@ pub fn selfExePathAlloc(allocator: *Allocator) ![]u8 { /// been deleted, the file path looks something like `/a/b/c/exe (deleted)`. /// TODO make the return type of this a null terminated pointer pub fn selfExePath(out_buffer: *[MAX_PATH_BYTES]u8) SelfExePathError![]u8 { - if (comptime std.Target.current.isDarwin()) { + if (is_darwin) { var u32_len: u32 = out_buffer.len; const rc = std.c._NSGetExecutablePath(out_buffer, &u32_len); if (rc != 0) return error.NameTooLong; diff --git a/lib/std/fs/file.zig b/lib/std/fs/file.zig index 4ff8d47287..08ae1bffca 100644 --- a/lib/std/fs/file.zig +++ b/lib/std/fs/file.zig @@ -8,7 +8,6 @@ const assert = std.debug.assert; const windows = os.windows; const Os = builtin.Os; const maxInt = std.math.maxInt; -const need_async_thread = std.fs.need_async_thread; pub const File = struct { /// The OS-specific file descriptor or file handle. @@ -17,15 +16,14 @@ pub const File = struct { /// On some systems, such as Linux, file system file descriptors are incapable of non-blocking I/O. /// This forces us to perform asynchronous I/O on a dedicated thread, to achieve non-blocking /// file-system I/O. To do this, `File` must be aware of whether it is a file system file descriptor, - /// or, more specifically, whether the I/O is blocking. - io_mode: io.Mode, + /// or, more specifically, whether the I/O is always blocking. + capable_io_mode: io.ModeOverride = io.default_mode, - /// Even when 'std.io.mode' is async, it is still sometimes desirable to perform blocking I/O, although - /// not by default. For example, when printing a stack trace to stderr. - async_block_allowed: @TypeOf(async_block_allowed_no) = async_block_allowed_no, - - pub const async_block_allowed_yes = if (io.is_async) true else {}; - pub const async_block_allowed_no = if (io.is_async) false else {}; + /// Furthermore, even when `std.io.mode` is async, it is still sometimes desirable to perform blocking I/O, + /// although not by default. For example, when printing a stack trace to stderr. + /// This field tracks both by acting as an overriding I/O mode. When not building in async I/O mode, + /// the type only has the `.blocking` tag, making it a zero-bit type. + intended_io_mode: io.ModeOverride = io.default_mode, pub const Mode = os.mode_t; @@ -36,9 +34,7 @@ pub const File = struct { pub const OpenError = windows.CreateFileError || os.OpenError || os.FlockError; - pub const Lock = enum { - None, Shared, Exclusive - }; + pub const Lock = enum { None, Shared, Exclusive }; /// TODO https://github.com/ziglang/zig/issues/3802 pub const OpenFlags = struct { @@ -62,15 +58,16 @@ pub const File = struct { /// Sets whether or not to wait until the file is locked to return. If set to true, /// `error.WouldBlock` will be returned. Otherwise, the file will wait until the file - /// is available to proceed. In async I/O mode, non-blocking at the OS level is always - /// used, and `true` means `error.WouldBlock` is returned, and `false` means - /// `error.WouldBlock` is handled by the event loop. + /// is available to proceed. + /// In async I/O mode, non-blocking at the OS level is + /// determined by `intended_io_mode`, and `true` means `error.WouldBlock` is returned, + /// and `false` means `error.WouldBlock` is handled by the event loop. lock_nonblocking: bool = false, - /// This prevents `O_NONBLOCK` from being passed even if `std.io.is_async`. - /// It allows the use of `noasync` when calling functions related to opening - /// the file, reading, writing, as well as locking functionality. - always_blocking: bool = false, + /// Setting this to `.blocking` prevents `O_NONBLOCK` from being passed even + /// if `std.io.is_async`. It allows the use of `noasync` when calling functions + /// related to opening the file, reading, writing, and locking. + intended_io_mode: io.ModeOverride = io.default_mode, }; /// TODO https://github.com/ziglang/zig/issues/3802 @@ -104,17 +101,25 @@ pub const File = struct { /// Sets whether or not to wait until the file is locked to return. If set to true, /// `error.WouldBlock` will be returned. Otherwise, the file will wait until the file /// is available to proceed. + /// In async I/O mode, non-blocking at the OS level is + /// determined by `intended_io_mode`, and `true` means `error.WouldBlock` is returned, + /// and `false` means `error.WouldBlock` is handled by the event loop. lock_nonblocking: bool = false, /// For POSIX systems this is the file system mode the file will /// be created with. mode: Mode = default_mode, + + /// Setting this to `.blocking` prevents `O_NONBLOCK` from being passed even + /// if `std.io.is_async`. It allows the use of `noasync` when calling functions + /// related to opening the file, reading, writing, and locking. + intended_io_mode: io.ModeOverride = io.default_mode, }; /// Upon success, the stream is in an uninitialized state. To continue using it, /// you must use the open() function. pub fn close(self: File) void { - if (need_async_thread and self.io_mode == .blocking and !self.async_block_allowed) { + if (self.capable_io_mode != self.intended_io_mode) { std.event.Loop.instance.?.close(self.handle); } else { os.close(self.handle); @@ -297,11 +302,7 @@ pub const File = struct { pub const PReadError = os.PReadError; pub fn read(self: File, buffer: []u8) ReadError!usize { - if (builtin.os.tag == .windows) { - const enable_async_io = std.io.is_async and !self.async_block_allowed; - return windows.ReadFile(self.handle, buffer, null, enable_async_io); - } - if (need_async_thread and self.io_mode == .blocking and !self.async_block_allowed) { + if (self.capable_io_mode != self.intended_io_mode) { return std.event.Loop.instance.?.read(self.handle, buffer); } else { return os.read(self.handle, buffer); @@ -321,11 +322,7 @@ pub const File = struct { } pub fn pread(self: File, buffer: []u8, offset: u64) PReadError!usize { - if (builtin.os.tag == .windows) { - const enable_async_io = std.io.is_async and !self.async_block_allowed; - return windows.ReadFile(self.handle, buffer, offset, enable_async_io); - } - if (need_async_thread and self.io_mode == .blocking and !self.async_block_allowed) { + if (self.capable_io_mode != self.intended_io_mode) { return std.event.Loop.instance.?.pread(self.handle, buffer, offset); } else { return os.pread(self.handle, buffer, offset); @@ -345,7 +342,7 @@ pub const File = struct { } pub fn readv(self: File, iovecs: []const os.iovec) ReadError!usize { - if (need_async_thread and self.io_mode == .blocking and !self.async_block_allowed) { + if (self.capable_io_mode != self.intended_io_mode) { return std.event.Loop.instance.?.readv(self.handle, iovecs); } else { return os.readv(self.handle, iovecs); @@ -379,7 +376,7 @@ pub const File = struct { } pub fn preadv(self: File, iovecs: []const os.iovec, offset: u64) PReadError!usize { - if (need_async_thread and self.io_mode == .blocking and !self.async_block_allowed) { + if (self.capable_io_mode != self.intended_io_mode) { return std.event.Loop.instance.?.preadv(self.handle, iovecs, offset); } else { return os.preadv(self.handle, iovecs, offset); @@ -416,11 +413,7 @@ pub const File = struct { pub const PWriteError = os.PWriteError; pub fn write(self: File, bytes: []const u8) WriteError!usize { - if (builtin.os.tag == .windows) { - const enable_async_io = std.io.is_async and !self.async_block_allowed; - return windows.WriteFile(self.handle, bytes, null, enable_async_io); - } - if (need_async_thread and self.io_mode == .blocking and !self.async_block_allowed) { + if (self.capable_io_mode != self.intended_io_mode) { return std.event.Loop.instance.?.write(self.handle, bytes); } else { return os.write(self.handle, bytes); @@ -435,11 +428,7 @@ pub const File = struct { } pub fn pwrite(self: File, bytes: []const u8, offset: u64) PWriteError!usize { - if (builtin.os.tag == .windows) { - const enable_async_io = std.io.is_async and !self.async_block_allowed; - return windows.WriteFile(self.handle, bytes, offset, enable_async_io); - } - if (need_async_thread and self.io_mode == .blocking and !self.async_block_allowed) { + if (self.capable_io_mode != self.intended_io_mode) { return std.event.Loop.instance.?.pwrite(self.handle, bytes, offset); } else { return os.pwrite(self.handle, bytes, offset); @@ -454,7 +443,7 @@ pub const File = struct { } pub fn writev(self: File, iovecs: []const os.iovec_const) WriteError!usize { - if (need_async_thread and self.io_mode == .blocking and !self.async_block_allowed) { + if (self.capable_io_mode != self.intended_io_mode) { return std.event.Loop.instance.?.writev(self.handle, iovecs); } else { return os.writev(self.handle, iovecs); @@ -480,7 +469,7 @@ pub const File = struct { } pub fn pwritev(self: File, iovecs: []os.iovec_const, offset: usize) PWriteError!usize { - if (need_async_thread and self.io_mode == .blocking and !self.async_block_allowed) { + if (self.capable_io_mode != self.intended_io_mode) { return std.event.Loop.instance.?.pwritev(self.handle, iovecs, offset); } else { return os.pwritev(self.handle, iovecs, offset); diff --git a/lib/std/fs/test.zig b/lib/std/fs/test.zig index da2d2c0d0c..77c4018a6a 100644 --- a/lib/std/fs/test.zig +++ b/lib/std/fs/test.zig @@ -27,7 +27,12 @@ test "open file with exclusive nonblocking lock twice" { } test "open file with lock twice, make sure it wasn't open at the same time" { - if (builtin.single_threaded) return; + if (builtin.single_threaded) return error.SkipZigTest; + + if (std.io.is_async) { + // This test starts its own threads and is not compatible with async I/O. + return error.SkipZigTest; + } const filename = "file_lock_test.txt"; @@ -58,6 +63,11 @@ test "open file with lock twice, make sure it wasn't open at the same time" { test "create file, lock and read from multiple process at once" { if (builtin.single_threaded) return error.SkipZigTest; + if (std.io.is_async) { + // This test starts its own threads and is not compatible with async I/O. + return error.SkipZigTest; + } + if (true) { // https://github.com/ziglang/zig/issues/5006 return error.SkipZigTest; diff --git a/lib/std/io.zig b/lib/std/io.zig index b9ab3ecc8e..f6c3325716 100644 --- a/lib/std/io.zig +++ b/lib/std/io.zig @@ -30,6 +30,11 @@ else Mode.blocking; pub const is_async = mode != .blocking; +/// This is an enum value to use for I/O mode at runtime, since it takes up zero bytes at runtime, +/// and makes expressions comptime-known when `is_async` is `false`. +pub const ModeOverride = if (is_async) Mode else enum { blocking }; +pub const default_mode: ModeOverride = if (is_async) Mode.evented else .blocking; + fn getStdOutHandle() os.fd_t { if (builtin.os.tag == .windows) { return os.windows.peb().ProcessParameters.hStdOutput; @@ -42,12 +47,13 @@ fn getStdOutHandle() os.fd_t { return os.STDOUT_FILENO; } -// TODO: async stdout on windows (https://github.com/ziglang/zig/pull/4816#issuecomment-604521023) +/// TODO: async stdout on windows without a dedicated thread. +/// https://github.com/ziglang/zig/pull/4816#issuecomment-604521023 pub fn getStdOut() File { return File{ .handle = getStdOutHandle(), - .io_mode = .blocking, - .async_block_allowed = if (builtin.os.tag == .windows) File.async_block_allowed_yes else File.async_block_allowed_no, + .capable_io_mode = .blocking, + .intended_io_mode = default_mode, }; } @@ -63,11 +69,13 @@ fn getStdErrHandle() os.fd_t { return os.STDERR_FILENO; } +/// This returns a `File` that is configured to block with every write, in order +/// to facilitate better debugging. This can be changed by modifying the `intended_io_mode` field. pub fn getStdErr() File { return File{ .handle = getStdErrHandle(), - .io_mode = .blocking, - .async_block_allowed = File.async_block_allowed_yes, + .capable_io_mode = .blocking, + .intended_io_mode = .blocking, }; } @@ -83,12 +91,13 @@ fn getStdInHandle() os.fd_t { return os.STDIN_FILENO; } -// TODO: async stdin on windows (https://github.com/ziglang/zig/pull/4816#issuecomment-604521023) +/// TODO: async stdin on windows without a dedicated thread. +/// https://github.com/ziglang/zig/pull/4816#issuecomment-604521023 pub fn getStdIn() File { return File{ .handle = getStdInHandle(), - .io_mode = .blocking, - .async_block_allowed = if (builtin.os.tag == .windows) File.async_block_allowed_yes else File.async_block_allowed_no, + .capable_io_mode = .blocking, + .intended_io_mode = default_mode, }; } diff --git a/lib/std/net.zig b/lib/std/net.zig index 9e57aa500b..1c7355bcb2 100644 --- a/lib/std/net.zig +++ b/lib/std/net.zig @@ -412,7 +412,7 @@ pub fn tcpConnectToAddress(address: Address) !fs.File { errdefer os.close(sockfd); try os.connect(sockfd, &address.any, address.getOsSockLen()); - return fs.File{ .handle = sockfd, .io_mode = std.io.mode }; + return fs.File{ .handle = sockfd }; } /// Call `AddressList.deinit` on the result. @@ -1381,10 +1381,7 @@ pub const StreamServer = struct { var adr_len: os.socklen_t = @sizeOf(Address); if (os.accept4(self.sockfd.?, &accepted_addr.any, &adr_len, accept_flags)) |fd| { return Connection{ - .file = fs.File{ - .handle = fd, - .io_mode = std.io.mode, - }, + .file = fs.File{ .handle = fd }, .address = accepted_addr, }; } else |err| switch (err) { diff --git a/lib/std/os.zig b/lib/std/os.zig index 489afe43b7..05d8b60a2d 100644 --- a/lib/std/os.zig +++ b/lib/std/os.zig @@ -173,8 +173,8 @@ fn getRandomBytesDevURandom(buf: []u8) !void { const file = std.fs.File{ .handle = fd, - .io_mode = .blocking, - .async_block_allowed = std.fs.File.async_block_allowed_yes, + .capable_io_mode = .blocking, + .intended_io_mode = .blocking, }; const stream = file.inStream(); stream.readNoEof(buf) catch return error.Unexpected; @@ -305,7 +305,7 @@ pub const ReadError = error{ /// For POSIX the limit is `math.maxInt(isize)`. pub fn read(fd: fd_t, buf: []u8) ReadError!usize { if (builtin.os.tag == .windows) { - return windows.ReadFile(fd, buf, null, false); + return windows.ReadFile(fd, buf, null, std.io.default_mode); } if (builtin.os.tag == .wasi and !builtin.link_libc) { @@ -408,7 +408,7 @@ pub const PReadError = ReadError || error{Unseekable}; /// used to perform the I/O. `error.WouldBlock` is not possible on Windows. pub fn pread(fd: fd_t, buf: []u8, offset: u64) PReadError!usize { if (builtin.os.tag == .windows) { - return windows.ReadFile(fd, buf, offset, false); + return windows.ReadFile(fd, buf, offset, std.io.default_mode); } while (true) { @@ -584,7 +584,7 @@ pub const WriteError = error{ /// The corresponding POSIX limit is `math.maxInt(isize)`. pub fn write(fd: fd_t, bytes: []const u8) WriteError!usize { if (builtin.os.tag == .windows) { - return windows.WriteFile(fd, bytes, null, false); + return windows.WriteFile(fd, bytes, null, std.io.default_mode); } if (builtin.os.tag == .wasi and !builtin.link_libc) { @@ -709,7 +709,7 @@ pub const PWriteError = WriteError || error{Unseekable}; /// The corresponding POSIX limit is `math.maxInt(isize)`. pub fn pwrite(fd: fd_t, bytes: []const u8, offset: u64) PWriteError!usize { if (std.Target.current.os.tag == .windows) { - return windows.WriteFile(fd, bytes, offset, false); + return windows.WriteFile(fd, bytes, offset, std.io.default_mode); } // Prevent EINVAL. diff --git a/lib/std/os/windows.zig b/lib/std/os/windows.zig index 7f273c141a..3e55a825de 100644 --- a/lib/std/os/windows.zig +++ b/lib/std/os/windows.zig @@ -110,7 +110,7 @@ pub const OpenFileOptions = struct { share_access: ULONG = FILE_SHARE_WRITE | FILE_SHARE_READ | FILE_SHARE_DELETE, share_access_nonblocking: bool = false, creation: ULONG, - enable_async_io: bool = std.io.is_async, + io_mode: std.io.ModeOverride, }; /// TODO when share_access_nonblocking is false, this implementation uses @@ -145,7 +145,7 @@ pub fn OpenFile(sub_path_w: []const u16, options: OpenFileOptions) OpenError!HAN var delay: usize = 1; while (true) { - const blocking_flag: ULONG = if (!options.enable_async_io) FILE_SYNCHRONOUS_IO_NONALERT else 0; + const blocking_flag: ULONG = if (options.io_mode == .blocking) FILE_SYNCHRONOUS_IO_NONALERT else 0; const rc = ntdll.NtCreateFile( &result, options.access_mask, @@ -451,11 +451,11 @@ pub const ReadFileError = error{ /// If buffer's length exceeds what a Windows DWORD integer can hold, it will be broken into /// multiple non-atomic reads. -pub fn ReadFile(in_hFile: HANDLE, buffer: []u8, offset: ?u64, enable_async_io: bool) ReadFileError!usize { - if (std.event.Loop.instance != null and enable_async_io) { +pub fn ReadFile(in_hFile: HANDLE, buffer: []u8, offset: ?u64, io_mode: std.io.ModeOverride) ReadFileError!usize { + if (io_mode != .blocking) { const loop = std.event.Loop.instance.?; - // TODO support async ReadFile with no offset - const off = if (offset == null) 0 else offset.?; + // TODO make getting the file position non-blocking + const off = if (offset) |o| o else try SetFilePointerEx_CURRENT_get(in_hFile); var resume_node = std.event.Loop.ResumeNode.Basic{ .base = .{ .id = .Basic, @@ -486,6 +486,11 @@ pub fn ReadFile(in_hFile: HANDLE, buffer: []u8, offset: ?u64, enable_async_io: b else => |err| return unexpectedError(err), } } + if (offset == null) { + // TODO make setting the file position non-blocking + const new_off = off + bytes_transferred; + try SetFilePointerEx_CURRENT(in_hFile, @bitCast(i64, new_off)); + } return @as(usize, bytes_transferred); } else { var index: usize = 0; @@ -525,11 +530,16 @@ pub const WriteFileError = error{ Unexpected, }; -pub fn WriteFile(handle: HANDLE, bytes: []const u8, offset: ?u64, enable_async_io: bool) WriteFileError!usize { - if (std.event.Loop.instance != null and enable_async_io) { +pub fn WriteFile( + handle: HANDLE, + bytes: []const u8, + offset: ?u64, + io_mode: std.io.ModeOverride, +) WriteFileError!usize { + if (std.event.Loop.instance != null and io_mode != .blocking) { const loop = std.event.Loop.instance.?; - // TODO support async WriteFile with no offset - const off = if (offset == null) 0 else offset.?; + // TODO make getting the file position non-blocking + const off = if (offset) |o| o else try SetFilePointerEx_CURRENT_get(handle); var resume_node = std.event.Loop.ResumeNode.Basic{ .base = .{ .id = .Basic, @@ -562,6 +572,11 @@ pub fn WriteFile(handle: HANDLE, bytes: []const u8, offset: ?u64, enable_async_i else => |err| return unexpectedError(err), } } + if (offset == null) { + // TODO make setting the file position non-blocking + const new_off = off + bytes_transferred; + try SetFilePointerEx_CURRENT(handle, @bitCast(i64, new_off)); + } return bytes_transferred; } else { var bytes_written: DWORD = undefined; diff --git a/lib/std/pdb.zig b/lib/std/pdb.zig index 0943bc44f3..75589b71ff 100644 --- a/lib/std/pdb.zig +++ b/lib/std/pdb.zig @@ -470,7 +470,7 @@ pub const Pdb = struct { msf: Msf, pub fn openFile(self: *Pdb, coff_ptr: *coff.Coff, file_name: []u8) !void { - self.in_file = try fs.cwd().openFile(file_name, .{ .always_blocking = true }); + self.in_file = try fs.cwd().openFile(file_name, .{ .intended_io_mode = .blocking }); self.allocator = coff_ptr.allocator; self.coff = coff_ptr;