authorgravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2020-05-01 23:17:15-04:00
committergravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2020-05-01 23:17:28-04:00
log45bce27b8fecda4fba1c22dd191030af29ccbc6f
tree946b5080cbe75dd1ef150ac830522a1ce40c526b
parent988031c07c1959b05682f007ed3bc848a75a43d0

cleanup and fixes. behavior tests passing with evented I/O


10 files changed, 137 insertions(+), 119 deletions(-)

lib/std/child_process.zig+7-16
...@@ -433,26 +433,17 @@ pub const ChildProcess = struct {...@@ -433,26 +433,17 @@ pub const ChildProcess = struct {
433 // we are the parent433 // we are the parent
434 const pid = @intCast(i32, pid_result);434 const pid = @intCast(i32, pid_result);
435 if (self.stdin_behavior == StdIo.Pipe) {435 if (self.stdin_behavior == StdIo.Pipe) {
436 self.stdin = File{436 self.stdin = File{ .handle = stdin_pipe[1] };
437 .handle = stdin_pipe[1],
438 .io_mode = std.io.mode,
439 };
440 } else {437 } else {
441 self.stdin = null;438 self.stdin = null;
442 }439 }
443 if (self.stdout_behavior == StdIo.Pipe) {440 if (self.stdout_behavior == StdIo.Pipe) {
444 self.stdout = File{441 self.stdout = File{ .handle = stdout_pipe[0] };
445 .handle = stdout_pipe[0],
446 .io_mode = std.io.mode,
447 };
448 } else {442 } else {
449 self.stdout = null;443 self.stdout = null;
450 }444 }
451 if (self.stderr_behavior == StdIo.Pipe) {445 if (self.stderr_behavior == StdIo.Pipe) {
452 self.stderr = File{446 self.stderr = File{ .handle = stderr_pipe[0] };
453 .handle = stderr_pipe[0],
454 .io_mode = std.io.mode,
455 };
456 } else {447 } else {
457 self.stderr = null;448 self.stderr = null;
458 }449 }
...@@ -835,8 +826,8 @@ const ErrInt = std.meta.Int(false, @sizeOf(anyerror) * 8);...@@ -835,8 +826,8 @@ const ErrInt = std.meta.Int(false, @sizeOf(anyerror) * 8);
835fn writeIntFd(fd: i32, value: ErrInt) !void {826fn writeIntFd(fd: i32, value: ErrInt) !void {
836 const file = File{827 const file = File{
837 .handle = fd,828 .handle = fd,
838 .io_mode = .blocking,829 .capable_io_mode = .blocking,
839 .async_block_allowed = File.async_block_allowed_yes,830 .intended_io_mode = .blocking,
840 };831 };
841 file.outStream().writeIntNative(u64, @intCast(u64, value)) catch return error.SystemResources;832 file.outStream().writeIntNative(u64, @intCast(u64, value)) catch return error.SystemResources;
842}833}
...@@ -844,8 +835,8 @@ fn writeIntFd(fd: i32, value: ErrInt) !void {...@@ -844,8 +835,8 @@ fn writeIntFd(fd: i32, value: ErrInt) !void {
844fn readIntFd(fd: i32) !ErrInt {835fn readIntFd(fd: i32) !ErrInt {
845 const file = File{836 const file = File{
846 .handle = fd,837 .handle = fd,
847 .io_mode = .blocking,838 .capable_io_mode = .blocking,
848 .async_block_allowed = File.async_block_allowed_yes,839 .intended_io_mode = .blocking,
849 };840 };
850 return @intCast(ErrInt, file.inStream().readIntNative(u64) catch return error.SystemResources);841 return @intCast(ErrInt, file.inStream().readIntNative(u64) catch return error.SystemResources);
851}842}
lib/std/debug.zig+3-3
...@@ -667,7 +667,7 @@ pub fn openSelfDebugInfo(allocator: *mem.Allocator) anyerror!DebugInfo {...@@ -667,7 +667,7 @@ pub fn openSelfDebugInfo(allocator: *mem.Allocator) anyerror!DebugInfo {
667/// TODO resources https://github.com/ziglang/zig/issues/4353667/// TODO resources https://github.com/ziglang/zig/issues/4353
668fn openCoffDebugInfo(allocator: *mem.Allocator, coff_file_path: [:0]const u16) !ModuleDebugInfo {668fn openCoffDebugInfo(allocator: *mem.Allocator, coff_file_path: [:0]const u16) !ModuleDebugInfo {
669 noasync {669 noasync {
670 const coff_file = try std.fs.openFileAbsoluteW(coff_file_path.ptr, .{});670 const coff_file = try std.fs.openFileAbsoluteW(coff_file_path, .{ .intended_io_mode = .blocking });
671 errdefer coff_file.close();671 errdefer coff_file.close();
672672
673 const coff_obj = try allocator.create(coff.Coff);673 const coff_obj = try allocator.create(coff.Coff);
...@@ -1003,7 +1003,7 @@ fn openMachODebugInfo(allocator: *mem.Allocator, macho_file_path: []const u8) !M...@@ -1003,7 +1003,7 @@ fn openMachODebugInfo(allocator: *mem.Allocator, macho_file_path: []const u8) !M
1003fn printLineFromFileAnyOs(out_stream: var, line_info: LineInfo) !void {1003fn printLineFromFileAnyOs(out_stream: var, line_info: LineInfo) !void {
1004 // Need this to always block even in async I/O mode, because this could potentially1004 // Need this to always block even in async I/O mode, because this could potentially
1005 // be called from e.g. the event loop code crashing.1005 // be called from e.g. the event loop code crashing.
1006 var f = try fs.cwd().openFile(line_info.file_name, .{ .always_blocking = true });1006 var f = try fs.cwd().openFile(line_info.file_name, .{ .intended_io_mode = .blocking });
1007 defer f.close();1007 defer f.close();
1008 // TODO fstat and make sure that the file has the correct size1008 // TODO fstat and make sure that the file has the correct size
10091009
...@@ -1051,7 +1051,7 @@ const MachoSymbol = struct {...@@ -1051,7 +1051,7 @@ const MachoSymbol = struct {
10511051
1052fn mapWholeFile(path: []const u8) ![]align(mem.page_size) const u8 {1052fn mapWholeFile(path: []const u8) ![]align(mem.page_size) const u8 {
1053 noasync {1053 noasync {
1054 const file = try fs.cwd().openFile(path, .{ .always_blocking = true });1054 const file = try fs.cwd().openFile(path, .{ .intended_io_mode = .blocking });
1055 defer file.close();1055 defer file.close();
10561056
1057 const file_len = try math.cast(usize, try file.getEndPos());1057 const file_len = try math.cast(usize, try file.getEndPos());
lib/std/fs.zig+32-25
...@@ -8,6 +8,8 @@ const Allocator = std.mem.Allocator;...@@ -8,6 +8,8 @@ const Allocator = std.mem.Allocator;
8const assert = std.debug.assert;8const assert = std.debug.assert;
9const math = std.math;9const math = std.math;
1010
11const is_darwin = std.Target.current.os.tag.isDarwin();
12
11pub const path = @import("fs/path.zig");13pub const path = @import("fs/path.zig");
12pub const File = @import("fs/file.zig").File;14pub const File = @import("fs/file.zig").File;
1315
...@@ -597,8 +599,11 @@ pub const Dir = struct {...@@ -597,8 +599,11 @@ pub const Dir = struct {
597599
598 // Use the O_ locking flags if the os supports them600 // Use the O_ locking flags if the os supports them
599 // (Or if it's darwin, as darwin's `open` doesn't support the O_SYNC flag)601 // (Or if it's darwin, as darwin's `open` doesn't support the O_SYNC flag)
600 const has_flock_open_flags = @hasDecl(os, "O_EXLOCK") and !builtin.os.tag.isDarwin();602 const has_flock_open_flags = @hasDecl(os, "O_EXLOCK") and !is_darwin;
601 const nonblocking_lock_flag = if (has_flock_open_flags and flags.lock_nonblocking) (os.O_NONBLOCK | os.O_SYNC) else @as(u32, 0);603 const nonblocking_lock_flag = if (has_flock_open_flags and flags.lock_nonblocking)
604 os.O_NONBLOCK | os.O_SYNC
605 else
606 @as(u32, 0);
602 const lock_flag: u32 = if (has_flock_open_flags) switch (flags.lock) {607 const lock_flag: u32 = if (has_flock_open_flags) switch (flags.lock) {
603 .None => @as(u32, 0),608 .None => @as(u32, 0),
604 .Shared => os.O_SHLOCK | nonblocking_lock_flag,609 .Shared => os.O_SHLOCK | nonblocking_lock_flag,
...@@ -612,7 +617,7 @@ pub const Dir = struct {...@@ -612,7 +617,7 @@ pub const Dir = struct {
612 @as(u32, os.O_WRONLY)617 @as(u32, os.O_WRONLY)
613 else618 else
614 @as(u32, os.O_RDONLY);619 @as(u32, os.O_RDONLY);
615 const fd = if (need_async_thread and !flags.always_blocking)620 const fd = if (flags.intended_io_mode != .blocking)
616 try std.event.Loop.instance.?.openatZ(self.fd, sub_path, os_flags, 0)621 try std.event.Loop.instance.?.openatZ(self.fd, sub_path, os_flags, 0)
617 else622 else
618 try os.openatZ(self.fd, sub_path, os_flags, 0);623 try os.openatZ(self.fd, sub_path, os_flags, 0);
...@@ -629,11 +634,8 @@ pub const Dir = struct {...@@ -629,11 +634,8 @@ pub const Dir = struct {
629634
630 return File{635 return File{
631 .handle = fd,636 .handle = fd,
632 .io_mode = .blocking,637 .capable_io_mode = .blocking,
633 .async_block_allowed = if (flags.always_blocking)638 .intended_io_mode = flags.intended_io_mode,
634 File.async_block_allowed_yes
635 else
636 File.async_block_allowed_no,
637 };639 };
638 }640 }
639641
...@@ -648,19 +650,16 @@ pub const Dir = struct {...@@ -648,19 +650,16 @@ pub const Dir = struct {
648 (if (flags.read) @as(u32, w.GENERIC_READ) else 0) |650 (if (flags.read) @as(u32, w.GENERIC_READ) else 0) |
649 (if (flags.write) @as(u32, w.GENERIC_WRITE) else 0),651 (if (flags.write) @as(u32, w.GENERIC_WRITE) else 0),
650 .share_access = switch (flags.lock) {652 .share_access = switch (flags.lock) {
651 .None => @as(?w.ULONG, null),653 .None => w.FILE_SHARE_WRITE | w.FILE_SHARE_READ | w.FILE_SHARE_DELETE,
652 .Shared => w.FILE_SHARE_READ | w.FILE_SHARE_DELETE,654 .Shared => w.FILE_SHARE_READ | w.FILE_SHARE_DELETE,
653 .Exclusive => w.FILE_SHARE_DELETE,655 .Exclusive => w.FILE_SHARE_DELETE,
654 },656 },
655 .share_access_nonblocking = flags.lock_nonblocking,657 .share_access_nonblocking = flags.lock_nonblocking,
656 .creation = w.FILE_OPEN,658 .creation = w.FILE_OPEN,
657 .enable_async_io = std.io.is_async and !flags.always_blocking,659 .io_mode = flags.intended_io_mode,
658 }),660 }),
659 .io_mode = .blocking,661 .capable_io_mode = std.io.default_mode,
660 .async_block_allowed = if (flags.always_blocking)662 .intended_io_mode = flags.intended_io_mode,
661 File.async_block_allowed_yes
662 else
663 File.async_block_allowed_no,
664 });663 });
665 }664 }
666665
...@@ -687,8 +686,11 @@ pub const Dir = struct {...@@ -687,8 +686,11 @@ pub const Dir = struct {
687686
688 // Use the O_ locking flags if the os supports them687 // Use the O_ locking flags if the os supports them
689 // (Or if it's darwin, as darwin's `open` doesn't support the O_SYNC flag)688 // (Or if it's darwin, as darwin's `open` doesn't support the O_SYNC flag)
690 const has_flock_open_flags = @hasDecl(os, "O_EXLOCK") and !builtin.os.tag.isDarwin();689 const has_flock_open_flags = @hasDecl(os, "O_EXLOCK") and !is_darwin;
691 const nonblocking_lock_flag = if (has_flock_open_flags and flags.lock_nonblocking) (os.O_NONBLOCK | os.O_SYNC) else @as(u32, 0);690 const nonblocking_lock_flag: u32 = if (has_flock_open_flags and flags.lock_nonblocking)
691 os.O_NONBLOCK | os.O_SYNC
692 else
693 0;
692 const lock_flag: u32 = if (has_flock_open_flags) switch (flags.lock) {694 const lock_flag: u32 = if (has_flock_open_flags) switch (flags.lock) {
693 .None => @as(u32, 0),695 .None => @as(u32, 0),
694 .Shared => os.O_SHLOCK,696 .Shared => os.O_SHLOCK,
...@@ -700,7 +702,7 @@ pub const Dir = struct {...@@ -700,7 +702,7 @@ pub const Dir = struct {
700 (if (flags.truncate) @as(u32, os.O_TRUNC) else 0) |702 (if (flags.truncate) @as(u32, os.O_TRUNC) else 0) |
701 (if (flags.read) @as(u32, os.O_RDWR) else os.O_WRONLY) |703 (if (flags.read) @as(u32, os.O_RDWR) else os.O_WRONLY) |
702 (if (flags.exclusive) @as(u32, os.O_EXCL) else 0);704 (if (flags.exclusive) @as(u32, os.O_EXCL) else 0);
703 const fd = if (need_async_thread)705 const fd = if (flags.intended_io_mode != .blocking)
704 try std.event.Loop.instance.?.openatZ(self.fd, sub_path_c, os_flags, flags.mode)706 try std.event.Loop.instance.?.openatZ(self.fd, sub_path_c, os_flags, flags.mode)
705 else707 else
706 try os.openatZ(self.fd, sub_path_c, os_flags, flags.mode);708 try os.openatZ(self.fd, sub_path_c, os_flags, flags.mode);
...@@ -715,7 +717,11 @@ pub const Dir = struct {...@@ -715,7 +717,11 @@ pub const Dir = struct {
715 });717 });
716 }718 }
717719
718 return File{ .handle = fd, .io_mode = .blocking };720 return File{
721 .handle = fd,
722 .capable_io_mode = .blocking,
723 .intended_io_mode = flags.intended_io_mode,
724 };
719 }725 }
720726
721 /// Same as `createFile` but Windows-only and the path parameter is727 /// Same as `createFile` but Windows-only and the path parameter is
...@@ -739,9 +745,10 @@ pub const Dir = struct {...@@ -739,9 +745,10 @@ pub const Dir = struct {
739 @as(u32, w.FILE_OVERWRITE_IF)745 @as(u32, w.FILE_OVERWRITE_IF)
740 else746 else
741 @as(u32, w.FILE_OPEN_IF),747 @as(u32, w.FILE_OPEN_IF),
742 .enable_async_io = std.io.is_async,748 .io_mode = flags.intended_io_mode,
743 }),749 }),
744 .io_mode = .blocking,750 .capable_io_mode = std.io.default_mode,
751 .intended_io_mode = flags.intended_io_mode,
745 });752 });
746 }753 }
747754
...@@ -1257,7 +1264,7 @@ pub const Dir = struct {...@@ -1257,7 +1264,7 @@ pub const Dir = struct {
1257 @as(u32, os.W_OK)1264 @as(u32, os.W_OK)
1258 else1265 else
1259 @as(u32, os.F_OK);1266 @as(u32, os.F_OK);
1260 const result = if (need_async_thread)1267 const result = if (need_async_thread and flags.intended_io_mode != .blocking)
1261 std.event.Loop.instance.?.faccessatZ(self.fd, sub_path, os_mode, 0)1268 std.event.Loop.instance.?.faccessatZ(self.fd, sub_path, os_mode, 0)
1262 else1269 else
1263 os.faccessatZ(self.fd, sub_path, os_mode, 0);1270 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)...@@ -1399,8 +1406,8 @@ pub fn openFileAbsoluteZ(absolute_path_c: [*:0]const u8, flags: File.OpenFlags)
1399}1406}
14001407
1401/// Same as `openFileAbsolute` but the path parameter is WTF-16 encoded.1408/// Same as `openFileAbsolute` but the path parameter is WTF-16 encoded.
1402pub fn openFileAbsoluteW(absolute_path_w: [*:0]const u16, flags: File.OpenFlags) File.OpenError!File {1409pub fn openFileAbsoluteW(absolute_path_w: []const u16, flags: File.OpenFlags) File.OpenError!File {
1403 assert(path.isAbsoluteWindowsW(absolute_path_w));1410 assert(path.isAbsoluteWindowsWTF16(absolute_path_w));
1404 return cwd().openFileW(absolute_path_w, flags);1411 return cwd().openFileW(absolute_path_w, flags);
1405}1412}
14061413
...@@ -1617,7 +1624,7 @@ pub fn selfExePathAlloc(allocator: *Allocator) ![]u8 {...@@ -1617,7 +1624,7 @@ pub fn selfExePathAlloc(allocator: *Allocator) ![]u8 {
1617/// been deleted, the file path looks something like `/a/b/c/exe (deleted)`.1624/// been deleted, the file path looks something like `/a/b/c/exe (deleted)`.
1618/// TODO make the return type of this a null terminated pointer1625/// TODO make the return type of this a null terminated pointer
1619pub fn selfExePath(out_buffer: *[MAX_PATH_BYTES]u8) SelfExePathError![]u8 {1626pub fn selfExePath(out_buffer: *[MAX_PATH_BYTES]u8) SelfExePathError![]u8 {
1620 if (comptime std.Target.current.isDarwin()) {1627 if (is_darwin) {
1621 var u32_len: u32 = out_buffer.len;1628 var u32_len: u32 = out_buffer.len;
1622 const rc = std.c._NSGetExecutablePath(out_buffer, &u32_len);1629 const rc = std.c._NSGetExecutablePath(out_buffer, &u32_len);
1623 if (rc != 0) return error.NameTooLong;1630 if (rc != 0) return error.NameTooLong;
lib/std/fs/file.zig+33-44
...@@ -8,7 +8,6 @@ const assert = std.debug.assert;...@@ -8,7 +8,6 @@ const assert = std.debug.assert;
8const windows = os.windows;8const windows = os.windows;
9const Os = builtin.Os;9const Os = builtin.Os;
10const maxInt = std.math.maxInt;10const maxInt = std.math.maxInt;
11const need_async_thread = std.fs.need_async_thread;
1211
13pub const File = struct {12pub const File = struct {
14 /// The OS-specific file descriptor or file handle.13 /// The OS-specific file descriptor or file handle.
...@@ -17,15 +16,14 @@ pub const File = struct {...@@ -17,15 +16,14 @@ pub const File = struct {
17 /// On some systems, such as Linux, file system file descriptors are incapable of non-blocking I/O.16 /// On some systems, such as Linux, file system file descriptors are incapable of non-blocking I/O.
18 /// This forces us to perform asynchronous I/O on a dedicated thread, to achieve non-blocking17 /// This forces us to perform asynchronous I/O on a dedicated thread, to achieve non-blocking
19 /// file-system I/O. To do this, `File` must be aware of whether it is a file system file descriptor,18 /// file-system I/O. To do this, `File` must be aware of whether it is a file system file descriptor,
20 /// or, more specifically, whether the I/O is blocking.19 /// or, more specifically, whether the I/O is always blocking.
21 io_mode: io.Mode,20 capable_io_mode: io.ModeOverride = io.default_mode,
2221
23 /// Even when 'std.io.mode' is async, it is still sometimes desirable to perform blocking I/O, although22 /// Furthermore, even when `std.io.mode` is async, it is still sometimes desirable to perform blocking I/O,
24 /// not by default. For example, when printing a stack trace to stderr.23 /// although not by default. For example, when printing a stack trace to stderr.
25 async_block_allowed: @TypeOf(async_block_allowed_no) = async_block_allowed_no,24 /// This field tracks both by acting as an overriding I/O mode. When not building in async I/O mode,
2625 /// the type only has the `.blocking` tag, making it a zero-bit type.
27 pub const async_block_allowed_yes = if (io.is_async) true else {};26 intended_io_mode: io.ModeOverride = io.default_mode,
28 pub const async_block_allowed_no = if (io.is_async) false else {};
2927
30 pub const Mode = os.mode_t;28 pub const Mode = os.mode_t;
3129
...@@ -36,9 +34,7 @@ pub const File = struct {...@@ -36,9 +34,7 @@ pub const File = struct {
3634
37 pub const OpenError = windows.CreateFileError || os.OpenError || os.FlockError;35 pub const OpenError = windows.CreateFileError || os.OpenError || os.FlockError;
3836
39 pub const Lock = enum {37 pub const Lock = enum { None, Shared, Exclusive };
40 None, Shared, Exclusive
41 };
4238
43 /// TODO https://github.com/ziglang/zig/issues/380239 /// TODO https://github.com/ziglang/zig/issues/3802
44 pub const OpenFlags = struct {40 pub const OpenFlags = struct {
...@@ -62,15 +58,16 @@ pub const File = struct {...@@ -62,15 +58,16 @@ pub const File = struct {
6258
63 /// Sets whether or not to wait until the file is locked to return. If set to true,59 /// Sets whether or not to wait until the file is locked to return. If set to true,
64 /// `error.WouldBlock` will be returned. Otherwise, the file will wait until the file60 /// `error.WouldBlock` will be returned. Otherwise, the file will wait until the file
65 /// is available to proceed. In async I/O mode, non-blocking at the OS level is always61 /// is available to proceed.
66 /// used, and `true` means `error.WouldBlock` is returned, and `false` means62 /// In async I/O mode, non-blocking at the OS level is
67 /// `error.WouldBlock` is handled by the event loop.63 /// determined by `intended_io_mode`, and `true` means `error.WouldBlock` is returned,
64 /// and `false` means `error.WouldBlock` is handled by the event loop.
68 lock_nonblocking: bool = false,65 lock_nonblocking: bool = false,
6966
70 /// This prevents `O_NONBLOCK` from being passed even if `std.io.is_async`.67 /// Setting this to `.blocking` prevents `O_NONBLOCK` from being passed even
71 /// It allows the use of `noasync` when calling functions related to opening68 /// if `std.io.is_async`. It allows the use of `noasync` when calling functions
72 /// the file, reading, writing, as well as locking functionality.69 /// related to opening the file, reading, writing, and locking.
73 always_blocking: bool = false,70 intended_io_mode: io.ModeOverride = io.default_mode,
74 };71 };
7572
76 /// TODO https://github.com/ziglang/zig/issues/380273 /// TODO https://github.com/ziglang/zig/issues/3802
...@@ -104,17 +101,25 @@ pub const File = struct {...@@ -104,17 +101,25 @@ pub const File = struct {
104 /// Sets whether or not to wait until the file is locked to return. If set to true,101 /// Sets whether or not to wait until the file is locked to return. If set to true,
105 /// `error.WouldBlock` will be returned. Otherwise, the file will wait until the file102 /// `error.WouldBlock` will be returned. Otherwise, the file will wait until the file
106 /// is available to proceed.103 /// is available to proceed.
104 /// In async I/O mode, non-blocking at the OS level is
105 /// determined by `intended_io_mode`, and `true` means `error.WouldBlock` is returned,
106 /// and `false` means `error.WouldBlock` is handled by the event loop.
107 lock_nonblocking: bool = false,107 lock_nonblocking: bool = false,
108108
109 /// For POSIX systems this is the file system mode the file will109 /// For POSIX systems this is the file system mode the file will
110 /// be created with.110 /// be created with.
111 mode: Mode = default_mode,111 mode: Mode = default_mode,
112
113 /// Setting this to `.blocking` prevents `O_NONBLOCK` from being passed even
114 /// if `std.io.is_async`. It allows the use of `noasync` when calling functions
115 /// related to opening the file, reading, writing, and locking.
116 intended_io_mode: io.ModeOverride = io.default_mode,
112 };117 };
113118
114 /// Upon success, the stream is in an uninitialized state. To continue using it,119 /// Upon success, the stream is in an uninitialized state. To continue using it,
115 /// you must use the open() function.120 /// you must use the open() function.
116 pub fn close(self: File) void {121 pub fn close(self: File) void {
117 if (need_async_thread and self.io_mode == .blocking and !self.async_block_allowed) {122 if (self.capable_io_mode != self.intended_io_mode) {
118 std.event.Loop.instance.?.close(self.handle);123 std.event.Loop.instance.?.close(self.handle);
119 } else {124 } else {
120 os.close(self.handle);125 os.close(self.handle);
...@@ -297,11 +302,7 @@ pub const File = struct {...@@ -297,11 +302,7 @@ pub const File = struct {
297 pub const PReadError = os.PReadError;302 pub const PReadError = os.PReadError;
298303
299 pub fn read(self: File, buffer: []u8) ReadError!usize {304 pub fn read(self: File, buffer: []u8) ReadError!usize {
300 if (builtin.os.tag == .windows) {305 if (self.capable_io_mode != self.intended_io_mode) {
301 const enable_async_io = std.io.is_async and !self.async_block_allowed;
302 return windows.ReadFile(self.handle, buffer, null, enable_async_io);
303 }
304 if (need_async_thread and self.io_mode == .blocking and !self.async_block_allowed) {
305 return std.event.Loop.instance.?.read(self.handle, buffer);306 return std.event.Loop.instance.?.read(self.handle, buffer);
306 } else {307 } else {
307 return os.read(self.handle, buffer);308 return os.read(self.handle, buffer);
...@@ -321,11 +322,7 @@ pub const File = struct {...@@ -321,11 +322,7 @@ pub const File = struct {
321 }322 }
322323
323 pub fn pread(self: File, buffer: []u8, offset: u64) PReadError!usize {324 pub fn pread(self: File, buffer: []u8, offset: u64) PReadError!usize {
324 if (builtin.os.tag == .windows) {325 if (self.capable_io_mode != self.intended_io_mode) {
325 const enable_async_io = std.io.is_async and !self.async_block_allowed;
326 return windows.ReadFile(self.handle, buffer, offset, enable_async_io);
327 }
328 if (need_async_thread and self.io_mode == .blocking and !self.async_block_allowed) {
329 return std.event.Loop.instance.?.pread(self.handle, buffer, offset);326 return std.event.Loop.instance.?.pread(self.handle, buffer, offset);
330 } else {327 } else {
331 return os.pread(self.handle, buffer, offset);328 return os.pread(self.handle, buffer, offset);
...@@ -345,7 +342,7 @@ pub const File = struct {...@@ -345,7 +342,7 @@ pub const File = struct {
345 }342 }
346343
347 pub fn readv(self: File, iovecs: []const os.iovec) ReadError!usize {344 pub fn readv(self: File, iovecs: []const os.iovec) ReadError!usize {
348 if (need_async_thread and self.io_mode == .blocking and !self.async_block_allowed) {345 if (self.capable_io_mode != self.intended_io_mode) {
349 return std.event.Loop.instance.?.readv(self.handle, iovecs);346 return std.event.Loop.instance.?.readv(self.handle, iovecs);
350 } else {347 } else {
351 return os.readv(self.handle, iovecs);348 return os.readv(self.handle, iovecs);
...@@ -379,7 +376,7 @@ pub const File = struct {...@@ -379,7 +376,7 @@ pub const File = struct {
379 }376 }
380377
381 pub fn preadv(self: File, iovecs: []const os.iovec, offset: u64) PReadError!usize {378 pub fn preadv(self: File, iovecs: []const os.iovec, offset: u64) PReadError!usize {
382 if (need_async_thread and self.io_mode == .blocking and !self.async_block_allowed) {379 if (self.capable_io_mode != self.intended_io_mode) {
383 return std.event.Loop.instance.?.preadv(self.handle, iovecs, offset);380 return std.event.Loop.instance.?.preadv(self.handle, iovecs, offset);
384 } else {381 } else {
385 return os.preadv(self.handle, iovecs, offset);382 return os.preadv(self.handle, iovecs, offset);
...@@ -416,11 +413,7 @@ pub const File = struct {...@@ -416,11 +413,7 @@ pub const File = struct {
416 pub const PWriteError = os.PWriteError;413 pub const PWriteError = os.PWriteError;
417414
418 pub fn write(self: File, bytes: []const u8) WriteError!usize {415 pub fn write(self: File, bytes: []const u8) WriteError!usize {
419 if (builtin.os.tag == .windows) {416 if (self.capable_io_mode != self.intended_io_mode) {
420 const enable_async_io = std.io.is_async and !self.async_block_allowed;
421 return windows.WriteFile(self.handle, bytes, null, enable_async_io);
422 }
423 if (need_async_thread and self.io_mode == .blocking and !self.async_block_allowed) {
424 return std.event.Loop.instance.?.write(self.handle, bytes);417 return std.event.Loop.instance.?.write(self.handle, bytes);
425 } else {418 } else {
426 return os.write(self.handle, bytes);419 return os.write(self.handle, bytes);
...@@ -435,11 +428,7 @@ pub const File = struct {...@@ -435,11 +428,7 @@ pub const File = struct {
435 }428 }
436429
437 pub fn pwrite(self: File, bytes: []const u8, offset: u64) PWriteError!usize {430 pub fn pwrite(self: File, bytes: []const u8, offset: u64) PWriteError!usize {
438 if (builtin.os.tag == .windows) {431 if (self.capable_io_mode != self.intended_io_mode) {
439 const enable_async_io = std.io.is_async and !self.async_block_allowed;
440 return windows.WriteFile(self.handle, bytes, offset, enable_async_io);
441 }
442 if (need_async_thread and self.io_mode == .blocking and !self.async_block_allowed) {
443 return std.event.Loop.instance.?.pwrite(self.handle, bytes, offset);432 return std.event.Loop.instance.?.pwrite(self.handle, bytes, offset);
444 } else {433 } else {
445 return os.pwrite(self.handle, bytes, offset);434 return os.pwrite(self.handle, bytes, offset);
...@@ -454,7 +443,7 @@ pub const File = struct {...@@ -454,7 +443,7 @@ pub const File = struct {
454 }443 }
455444
456 pub fn writev(self: File, iovecs: []const os.iovec_const) WriteError!usize {445 pub fn writev(self: File, iovecs: []const os.iovec_const) WriteError!usize {
457 if (need_async_thread and self.io_mode == .blocking and !self.async_block_allowed) {446 if (self.capable_io_mode != self.intended_io_mode) {
458 return std.event.Loop.instance.?.writev(self.handle, iovecs);447 return std.event.Loop.instance.?.writev(self.handle, iovecs);
459 } else {448 } else {
460 return os.writev(self.handle, iovecs);449 return os.writev(self.handle, iovecs);
...@@ -480,7 +469,7 @@ pub const File = struct {...@@ -480,7 +469,7 @@ pub const File = struct {
480 }469 }
481470
482 pub fn pwritev(self: File, iovecs: []os.iovec_const, offset: usize) PWriteError!usize {471 pub fn pwritev(self: File, iovecs: []os.iovec_const, offset: usize) PWriteError!usize {
483 if (need_async_thread and self.io_mode == .blocking and !self.async_block_allowed) {472 if (self.capable_io_mode != self.intended_io_mode) {
484 return std.event.Loop.instance.?.pwritev(self.handle, iovecs, offset);473 return std.event.Loop.instance.?.pwritev(self.handle, iovecs, offset);
485 } else {474 } else {
486 return os.pwritev(self.handle, iovecs, offset);475 return os.pwritev(self.handle, iovecs, offset);
lib/std/fs/test.zig+11-1
...@@ -27,7 +27,12 @@ test "open file with exclusive nonblocking lock twice" {...@@ -27,7 +27,12 @@ test "open file with exclusive nonblocking lock twice" {
27}27}
2828
29test "open file with lock twice, make sure it wasn't open at the same time" {29test "open file with lock twice, make sure it wasn't open at the same time" {
30 if (builtin.single_threaded) return;30 if (builtin.single_threaded) return error.SkipZigTest;
31
32 if (std.io.is_async) {
33 // This test starts its own threads and is not compatible with async I/O.
34 return error.SkipZigTest;
35 }
3136
32 const filename = "file_lock_test.txt";37 const filename = "file_lock_test.txt";
3338
...@@ -58,6 +63,11 @@ test "open file with lock twice, make sure it wasn't open at the same time" {...@@ -58,6 +63,11 @@ test "open file with lock twice, make sure it wasn't open at the same time" {
58test "create file, lock and read from multiple process at once" {63test "create file, lock and read from multiple process at once" {
59 if (builtin.single_threaded) return error.SkipZigTest;64 if (builtin.single_threaded) return error.SkipZigTest;
6065
66 if (std.io.is_async) {
67 // This test starts its own threads and is not compatible with async I/O.
68 return error.SkipZigTest;
69 }
70
61 if (true) {71 if (true) {
62 // https://github.com/ziglang/zig/issues/500672 // https://github.com/ziglang/zig/issues/5006
63 return error.SkipZigTest;73 return error.SkipZigTest;
lib/std/io.zig+17-8
...@@ -30,6 +30,11 @@ else...@@ -30,6 +30,11 @@ else
30 Mode.blocking;30 Mode.blocking;
31pub const is_async = mode != .blocking;31pub const is_async = mode != .blocking;
3232
33/// This is an enum value to use for I/O mode at runtime, since it takes up zero bytes at runtime,
34/// and makes expressions comptime-known when `is_async` is `false`.
35pub const ModeOverride = if (is_async) Mode else enum { blocking };
36pub const default_mode: ModeOverride = if (is_async) Mode.evented else .blocking;
37
33fn getStdOutHandle() os.fd_t {38fn getStdOutHandle() os.fd_t {
34 if (builtin.os.tag == .windows) {39 if (builtin.os.tag == .windows) {
35 return os.windows.peb().ProcessParameters.hStdOutput;40 return os.windows.peb().ProcessParameters.hStdOutput;
...@@ -42,12 +47,13 @@ fn getStdOutHandle() os.fd_t {...@@ -42,12 +47,13 @@ fn getStdOutHandle() os.fd_t {
42 return os.STDOUT_FILENO;47 return os.STDOUT_FILENO;
43}48}
4449
45// TODO: async stdout on windows (https://github.com/ziglang/zig/pull/4816#issuecomment-604521023)50/// TODO: async stdout on windows without a dedicated thread.
51/// https://github.com/ziglang/zig/pull/4816#issuecomment-604521023
46pub fn getStdOut() File {52pub fn getStdOut() File {
47 return File{53 return File{
48 .handle = getStdOutHandle(),54 .handle = getStdOutHandle(),
49 .io_mode = .blocking,55 .capable_io_mode = .blocking,
50 .async_block_allowed = if (builtin.os.tag == .windows) File.async_block_allowed_yes else File.async_block_allowed_no,56 .intended_io_mode = default_mode,
51 };57 };
52}58}
5359
...@@ -63,11 +69,13 @@ fn getStdErrHandle() os.fd_t {...@@ -63,11 +69,13 @@ fn getStdErrHandle() os.fd_t {
63 return os.STDERR_FILENO;69 return os.STDERR_FILENO;
64}70}
6571
72/// This returns a `File` that is configured to block with every write, in order
73/// to facilitate better debugging. This can be changed by modifying the `intended_io_mode` field.
66pub fn getStdErr() File {74pub fn getStdErr() File {
67 return File{75 return File{
68 .handle = getStdErrHandle(),76 .handle = getStdErrHandle(),
69 .io_mode = .blocking,77 .capable_io_mode = .blocking,
70 .async_block_allowed = File.async_block_allowed_yes,78 .intended_io_mode = .blocking,
71 };79 };
72}80}
7381
...@@ -83,12 +91,13 @@ fn getStdInHandle() os.fd_t {...@@ -83,12 +91,13 @@ fn getStdInHandle() os.fd_t {
83 return os.STDIN_FILENO;91 return os.STDIN_FILENO;
84}92}
8593
86// TODO: async stdin on windows (https://github.com/ziglang/zig/pull/4816#issuecomment-604521023)94/// TODO: async stdin on windows without a dedicated thread.
95/// https://github.com/ziglang/zig/pull/4816#issuecomment-604521023
87pub fn getStdIn() File {96pub fn getStdIn() File {
88 return File{97 return File{
89 .handle = getStdInHandle(),98 .handle = getStdInHandle(),
90 .io_mode = .blocking,99 .capable_io_mode = .blocking,
91 .async_block_allowed = if (builtin.os.tag == .windows) File.async_block_allowed_yes else File.async_block_allowed_no,100 .intended_io_mode = default_mode,
92 };101 };
93}102}
94103
lib/std/net.zig+2-5
...@@ -412,7 +412,7 @@ pub fn tcpConnectToAddress(address: Address) !fs.File {...@@ -412,7 +412,7 @@ pub fn tcpConnectToAddress(address: Address) !fs.File {
412 errdefer os.close(sockfd);412 errdefer os.close(sockfd);
413 try os.connect(sockfd, &address.any, address.getOsSockLen());413 try os.connect(sockfd, &address.any, address.getOsSockLen());
414414
415 return fs.File{ .handle = sockfd, .io_mode = std.io.mode };415 return fs.File{ .handle = sockfd };
416}416}
417417
418/// Call `AddressList.deinit` on the result.418/// Call `AddressList.deinit` on the result.
...@@ -1381,10 +1381,7 @@ pub const StreamServer = struct {...@@ -1381,10 +1381,7 @@ pub const StreamServer = struct {
1381 var adr_len: os.socklen_t = @sizeOf(Address);1381 var adr_len: os.socklen_t = @sizeOf(Address);
1382 if (os.accept4(self.sockfd.?, &accepted_addr.any, &adr_len, accept_flags)) |fd| {1382 if (os.accept4(self.sockfd.?, &accepted_addr.any, &adr_len, accept_flags)) |fd| {
1383 return Connection{1383 return Connection{
1384 .file = fs.File{1384 .file = fs.File{ .handle = fd },
1385 .handle = fd,
1386 .io_mode = std.io.mode,
1387 },
1388 .address = accepted_addr,1385 .address = accepted_addr,
1389 };1386 };
1390 } else |err| switch (err) {1387 } else |err| switch (err) {
lib/std/os.zig+6-6
...@@ -173,8 +173,8 @@ fn getRandomBytesDevURandom(buf: []u8) !void {...@@ -173,8 +173,8 @@ fn getRandomBytesDevURandom(buf: []u8) !void {
173173
174 const file = std.fs.File{174 const file = std.fs.File{
175 .handle = fd,175 .handle = fd,
176 .io_mode = .blocking,176 .capable_io_mode = .blocking,
177 .async_block_allowed = std.fs.File.async_block_allowed_yes,177 .intended_io_mode = .blocking,
178 };178 };
179 const stream = file.inStream();179 const stream = file.inStream();
180 stream.readNoEof(buf) catch return error.Unexpected;180 stream.readNoEof(buf) catch return error.Unexpected;
...@@ -305,7 +305,7 @@ pub const ReadError = error{...@@ -305,7 +305,7 @@ pub const ReadError = error{
305/// For POSIX the limit is `math.maxInt(isize)`.305/// For POSIX the limit is `math.maxInt(isize)`.
306pub fn read(fd: fd_t, buf: []u8) ReadError!usize {306pub fn read(fd: fd_t, buf: []u8) ReadError!usize {
307 if (builtin.os.tag == .windows) {307 if (builtin.os.tag == .windows) {
308 return windows.ReadFile(fd, buf, null, false);308 return windows.ReadFile(fd, buf, null, std.io.default_mode);
309 }309 }
310310
311 if (builtin.os.tag == .wasi and !builtin.link_libc) {311 if (builtin.os.tag == .wasi and !builtin.link_libc) {
...@@ -408,7 +408,7 @@ pub const PReadError = ReadError || error{Unseekable};...@@ -408,7 +408,7 @@ pub const PReadError = ReadError || error{Unseekable};
408/// used to perform the I/O. `error.WouldBlock` is not possible on Windows.408/// used to perform the I/O. `error.WouldBlock` is not possible on Windows.
409pub fn pread(fd: fd_t, buf: []u8, offset: u64) PReadError!usize {409pub fn pread(fd: fd_t, buf: []u8, offset: u64) PReadError!usize {
410 if (builtin.os.tag == .windows) {410 if (builtin.os.tag == .windows) {
411 return windows.ReadFile(fd, buf, offset, false);411 return windows.ReadFile(fd, buf, offset, std.io.default_mode);
412 }412 }
413413
414 while (true) {414 while (true) {
...@@ -584,7 +584,7 @@ pub const WriteError = error{...@@ -584,7 +584,7 @@ pub const WriteError = error{
584/// The corresponding POSIX limit is `math.maxInt(isize)`.584/// The corresponding POSIX limit is `math.maxInt(isize)`.
585pub fn write(fd: fd_t, bytes: []const u8) WriteError!usize {585pub fn write(fd: fd_t, bytes: []const u8) WriteError!usize {
586 if (builtin.os.tag == .windows) {586 if (builtin.os.tag == .windows) {
587 return windows.WriteFile(fd, bytes, null, false);587 return windows.WriteFile(fd, bytes, null, std.io.default_mode);
588 }588 }
589589
590 if (builtin.os.tag == .wasi and !builtin.link_libc) {590 if (builtin.os.tag == .wasi and !builtin.link_libc) {
...@@ -709,7 +709,7 @@ pub const PWriteError = WriteError || error{Unseekable};...@@ -709,7 +709,7 @@ pub const PWriteError = WriteError || error{Unseekable};
709/// The corresponding POSIX limit is `math.maxInt(isize)`.709/// The corresponding POSIX limit is `math.maxInt(isize)`.
710pub fn pwrite(fd: fd_t, bytes: []const u8, offset: u64) PWriteError!usize {710pub fn pwrite(fd: fd_t, bytes: []const u8, offset: u64) PWriteError!usize {
711 if (std.Target.current.os.tag == .windows) {711 if (std.Target.current.os.tag == .windows) {
712 return windows.WriteFile(fd, bytes, offset, false);712 return windows.WriteFile(fd, bytes, offset, std.io.default_mode);
713 }713 }
714714
715 // Prevent EINVAL.715 // Prevent EINVAL.
lib/std/os/windows.zig+25-10
...@@ -110,7 +110,7 @@ pub const OpenFileOptions = struct {...@@ -110,7 +110,7 @@ pub const OpenFileOptions = struct {
110 share_access: ULONG = FILE_SHARE_WRITE | FILE_SHARE_READ | FILE_SHARE_DELETE,110 share_access: ULONG = FILE_SHARE_WRITE | FILE_SHARE_READ | FILE_SHARE_DELETE,
111 share_access_nonblocking: bool = false,111 share_access_nonblocking: bool = false,
112 creation: ULONG,112 creation: ULONG,
113 enable_async_io: bool = std.io.is_async,113 io_mode: std.io.ModeOverride,
114};114};
115115
116/// TODO when share_access_nonblocking is false, this implementation uses116/// 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...@@ -145,7 +145,7 @@ pub fn OpenFile(sub_path_w: []const u16, options: OpenFileOptions) OpenError!HAN
145145
146 var delay: usize = 1;146 var delay: usize = 1;
147 while (true) {147 while (true) {
148 const blocking_flag: ULONG = if (!options.enable_async_io) FILE_SYNCHRONOUS_IO_NONALERT else 0;148 const blocking_flag: ULONG = if (options.io_mode == .blocking) FILE_SYNCHRONOUS_IO_NONALERT else 0;
149 const rc = ntdll.NtCreateFile(149 const rc = ntdll.NtCreateFile(
150 &result,150 &result,
151 options.access_mask,151 options.access_mask,
...@@ -451,11 +451,11 @@ pub const ReadFileError = error{...@@ -451,11 +451,11 @@ pub const ReadFileError = error{
451451
452/// If buffer's length exceeds what a Windows DWORD integer can hold, it will be broken into452/// If buffer's length exceeds what a Windows DWORD integer can hold, it will be broken into
453/// multiple non-atomic reads.453/// multiple non-atomic reads.
454pub fn ReadFile(in_hFile: HANDLE, buffer: []u8, offset: ?u64, enable_async_io: bool) ReadFileError!usize {454pub fn ReadFile(in_hFile: HANDLE, buffer: []u8, offset: ?u64, io_mode: std.io.ModeOverride) ReadFileError!usize {
455 if (std.event.Loop.instance != null and enable_async_io) {455 if (io_mode != .blocking) {
456 const loop = std.event.Loop.instance.?;456 const loop = std.event.Loop.instance.?;
457 // TODO support async ReadFile with no offset457 // TODO make getting the file position non-blocking
458 const off = if (offset == null) 0 else offset.?;458 const off = if (offset) |o| o else try SetFilePointerEx_CURRENT_get(in_hFile);
459 var resume_node = std.event.Loop.ResumeNode.Basic{459 var resume_node = std.event.Loop.ResumeNode.Basic{
460 .base = .{460 .base = .{
461 .id = .Basic,461 .id = .Basic,
...@@ -486,6 +486,11 @@ pub fn ReadFile(in_hFile: HANDLE, buffer: []u8, offset: ?u64, enable_async_io: b...@@ -486,6 +486,11 @@ pub fn ReadFile(in_hFile: HANDLE, buffer: []u8, offset: ?u64, enable_async_io: b
486 else => |err| return unexpectedError(err),486 else => |err| return unexpectedError(err),
487 }487 }
488 }488 }
489 if (offset == null) {
490 // TODO make setting the file position non-blocking
491 const new_off = off + bytes_transferred;
492 try SetFilePointerEx_CURRENT(in_hFile, @bitCast(i64, new_off));
493 }
489 return @as(usize, bytes_transferred);494 return @as(usize, bytes_transferred);
490 } else {495 } else {
491 var index: usize = 0;496 var index: usize = 0;
...@@ -525,11 +530,16 @@ pub const WriteFileError = error{...@@ -525,11 +530,16 @@ pub const WriteFileError = error{
525 Unexpected,530 Unexpected,
526};531};
527532
528pub fn WriteFile(handle: HANDLE, bytes: []const u8, offset: ?u64, enable_async_io: bool) WriteFileError!usize {533pub fn WriteFile(
529 if (std.event.Loop.instance != null and enable_async_io) {534 handle: HANDLE,
535 bytes: []const u8,
536 offset: ?u64,
537 io_mode: std.io.ModeOverride,
538) WriteFileError!usize {
539 if (std.event.Loop.instance != null and io_mode != .blocking) {
530 const loop = std.event.Loop.instance.?;540 const loop = std.event.Loop.instance.?;
531 // TODO support async WriteFile with no offset541 // TODO make getting the file position non-blocking
532 const off = if (offset == null) 0 else offset.?;542 const off = if (offset) |o| o else try SetFilePointerEx_CURRENT_get(handle);
533 var resume_node = std.event.Loop.ResumeNode.Basic{543 var resume_node = std.event.Loop.ResumeNode.Basic{
534 .base = .{544 .base = .{
535 .id = .Basic,545 .id = .Basic,
...@@ -562,6 +572,11 @@ pub fn WriteFile(handle: HANDLE, bytes: []const u8, offset: ?u64, enable_async_i...@@ -562,6 +572,11 @@ pub fn WriteFile(handle: HANDLE, bytes: []const u8, offset: ?u64, enable_async_i
562 else => |err| return unexpectedError(err),572 else => |err| return unexpectedError(err),
563 }573 }
564 }574 }
575 if (offset == null) {
576 // TODO make setting the file position non-blocking
577 const new_off = off + bytes_transferred;
578 try SetFilePointerEx_CURRENT(handle, @bitCast(i64, new_off));
579 }
565 return bytes_transferred;580 return bytes_transferred;
566 } else {581 } else {
567 var bytes_written: DWORD = undefined;582 var bytes_written: DWORD = undefined;
lib/std/pdb.zig+1-1
...@@ -470,7 +470,7 @@ pub const Pdb = struct {...@@ -470,7 +470,7 @@ pub const Pdb = struct {
470 msf: Msf,470 msf: Msf,
471471
472 pub fn openFile(self: *Pdb, coff_ptr: *coff.Coff, file_name: []u8) !void {472 pub fn openFile(self: *Pdb, coff_ptr: *coff.Coff, file_name: []u8) !void {
473 self.in_file = try fs.cwd().openFile(file_name, .{ .always_blocking = true });473 self.in_file = try fs.cwd().openFile(file_name, .{ .intended_io_mode = .blocking });
474 self.allocator = coff_ptr.allocator;474 self.allocator = coff_ptr.allocator;
475 self.coff = coff_ptr;475 self.coff = coff_ptr;
476476