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 {
433433 // we are the parent
434434 const pid = @intCast(i32, pid_result);
435435 if (self.stdin_behavior == StdIo.Pipe) {
436 self.stdin = File{
437 .handle = stdin_pipe[1],
438 .io_mode = std.io.mode,
439 };
436 self.stdin = File{ .handle = stdin_pipe[1] };
440437 } else {
441438 self.stdin = null;
442439 }
443440 if (self.stdout_behavior == StdIo.Pipe) {
444 self.stdout = File{
445 .handle = stdout_pipe[0],
446 .io_mode = std.io.mode,
447 };
441 self.stdout = File{ .handle = stdout_pipe[0] };
448442 } else {
449443 self.stdout = null;
450444 }
451445 if (self.stderr_behavior == StdIo.Pipe) {
452 self.stderr = File{
453 .handle = stderr_pipe[0],
454 .io_mode = std.io.mode,
455 };
446 self.stderr = File{ .handle = stderr_pipe[0] };
456447 } else {
457448 self.stderr = null;
458449 }
......@@ -835,8 +826,8 @@ const ErrInt = std.meta.Int(false, @sizeOf(anyerror) * 8);
835826fn writeIntFd(fd: i32, value: ErrInt) !void {
836827 const file = File{
837828 .handle = fd,
838 .io_mode = .blocking,
839 .async_block_allowed = File.async_block_allowed_yes,
829 .capable_io_mode = .blocking,
830 .intended_io_mode = .blocking,
840831 };
841832 file.outStream().writeIntNative(u64, @intCast(u64, value)) catch return error.SystemResources;
842833}
......@@ -844,8 +835,8 @@ fn writeIntFd(fd: i32, value: ErrInt) !void {
844835fn readIntFd(fd: i32) !ErrInt {
845836 const file = File{
846837 .handle = fd,
847 .io_mode = .blocking,
848 .async_block_allowed = File.async_block_allowed_yes,
838 .capable_io_mode = .blocking,
839 .intended_io_mode = .blocking,
849840 };
850841 return @intCast(ErrInt, file.inStream().readIntNative(u64) catch return error.SystemResources);
851842}
lib/std/debug.zig+3-3
......@@ -667,7 +667,7 @@ pub fn openSelfDebugInfo(allocator: *mem.Allocator) anyerror!DebugInfo {
667667/// TODO resources https://github.com/ziglang/zig/issues/4353
668668fn openCoffDebugInfo(allocator: *mem.Allocator, coff_file_path: [:0]const u16) !ModuleDebugInfo {
669669 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 });
671671 errdefer coff_file.close();
672672
673673 const coff_obj = try allocator.create(coff.Coff);
......@@ -1003,7 +1003,7 @@ fn openMachODebugInfo(allocator: *mem.Allocator, macho_file_path: []const u8) !M
10031003fn printLineFromFileAnyOs(out_stream: var, line_info: LineInfo) !void {
10041004 // Need this to always block even in async I/O mode, because this could potentially
10051005 // 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 });
10071007 defer f.close();
10081008 // TODO fstat and make sure that the file has the correct size
10091009
......@@ -1051,7 +1051,7 @@ const MachoSymbol = struct {
10511051
10521052fn mapWholeFile(path: []const u8) ![]align(mem.page_size) const u8 {
10531053 noasync {
1054 const file = try fs.cwd().openFile(path, .{ .always_blocking = true });
1054 const file = try fs.cwd().openFile(path, .{ .intended_io_mode = .blocking });
10551055 defer file.close();
10561056
10571057 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;
88const assert = std.debug.assert;
99const math = std.math;
1010
11const is_darwin = std.Target.current.os.tag.isDarwin();
12
1113pub const path = @import("fs/path.zig");
1214pub const File = @import("fs/file.zig").File;
1315
......@@ -597,8 +599,11 @@ pub const Dir = struct {
597599
598600 // Use the O_ locking flags if the os supports them
599601 // (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();
601 const nonblocking_lock_flag = if (has_flock_open_flags and flags.lock_nonblocking) (os.O_NONBLOCK | os.O_SYNC) else @as(u32, 0);
602 const has_flock_open_flags = @hasDecl(os, "O_EXLOCK") and !is_darwin;
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);
602607 const lock_flag: u32 = if (has_flock_open_flags) switch (flags.lock) {
603608 .None => @as(u32, 0),
604609 .Shared => os.O_SHLOCK | nonblocking_lock_flag,
......@@ -612,7 +617,7 @@ pub const Dir = struct {
612617 @as(u32, os.O_WRONLY)
613618 else
614619 @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)
616621 try std.event.Loop.instance.?.openatZ(self.fd, sub_path, os_flags, 0)
617622 else
618623 try os.openatZ(self.fd, sub_path, os_flags, 0);
......@@ -629,11 +634,8 @@ pub const Dir = struct {
629634
630635 return File{
631636 .handle = fd,
632 .io_mode = .blocking,
633 .async_block_allowed = if (flags.always_blocking)
634 File.async_block_allowed_yes
635 else
636 File.async_block_allowed_no,
637 .capable_io_mode = .blocking,
638 .intended_io_mode = flags.intended_io_mode,
637639 };
638640 }
639641
......@@ -648,19 +650,16 @@ pub const Dir = struct {
648650 (if (flags.read) @as(u32, w.GENERIC_READ) else 0) |
649651 (if (flags.write) @as(u32, w.GENERIC_WRITE) else 0),
650652 .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,
652654 .Shared => w.FILE_SHARE_READ | w.FILE_SHARE_DELETE,
653655 .Exclusive => w.FILE_SHARE_DELETE,
654656 },
655657 .share_access_nonblocking = flags.lock_nonblocking,
656658 .creation = w.FILE_OPEN,
657 .enable_async_io = std.io.is_async and !flags.always_blocking,
659 .io_mode = flags.intended_io_mode,
658660 }),
659 .io_mode = .blocking,
660 .async_block_allowed = if (flags.always_blocking)
661 File.async_block_allowed_yes
662 else
663 File.async_block_allowed_no,
661 .capable_io_mode = std.io.default_mode,
662 .intended_io_mode = flags.intended_io_mode,
664663 });
665664 }
666665
......@@ -687,8 +686,11 @@ pub const Dir = struct {
687686
688687 // Use the O_ locking flags if the os supports them
689688 // (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();
691 const nonblocking_lock_flag = if (has_flock_open_flags and flags.lock_nonblocking) (os.O_NONBLOCK | os.O_SYNC) else @as(u32, 0);
689 const has_flock_open_flags = @hasDecl(os, "O_EXLOCK") and !is_darwin;
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;
692694 const lock_flag: u32 = if (has_flock_open_flags) switch (flags.lock) {
693695 .None => @as(u32, 0),
694696 .Shared => os.O_SHLOCK,
......@@ -700,7 +702,7 @@ pub const Dir = struct {
700702 (if (flags.truncate) @as(u32, os.O_TRUNC) else 0) |
701703 (if (flags.read) @as(u32, os.O_RDWR) else os.O_WRONLY) |
702704 (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)
704706 try std.event.Loop.instance.?.openatZ(self.fd, sub_path_c, os_flags, flags.mode)
705707 else
706708 try os.openatZ(self.fd, sub_path_c, os_flags, flags.mode);
......@@ -715,7 +717,11 @@ pub const Dir = struct {
715717 });
716718 }
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 };
719725 }
720726
721727 /// Same as `createFile` but Windows-only and the path parameter is
......@@ -739,9 +745,10 @@ pub const Dir = struct {
739745 @as(u32, w.FILE_OVERWRITE_IF)
740746 else
741747 @as(u32, w.FILE_OPEN_IF),
742 .enable_async_io = std.io.is_async,
748 .io_mode = flags.intended_io_mode,
743749 }),
744 .io_mode = .blocking,
750 .capable_io_mode = std.io.default_mode,
751 .intended_io_mode = flags.intended_io_mode,
745752 });
746753 }
747754
......@@ -1257,7 +1264,7 @@ pub const Dir = struct {
12571264 @as(u32, os.W_OK)
12581265 else
12591266 @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)
12611268 std.event.Loop.instance.?.faccessatZ(self.fd, sub_path, os_mode, 0)
12621269 else
12631270 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)
13991406}
14001407
14011408/// 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 {
1403 assert(path.isAbsoluteWindowsW(absolute_path_w));
1409pub fn openFileAbsoluteW(absolute_path_w: []const u16, flags: File.OpenFlags) File.OpenError!File {
1410 assert(path.isAbsoluteWindowsWTF16(absolute_path_w));
14041411 return cwd().openFileW(absolute_path_w, flags);
14051412}
14061413
......@@ -1617,7 +1624,7 @@ pub fn selfExePathAlloc(allocator: *Allocator) ![]u8 {
16171624/// been deleted, the file path looks something like `/a/b/c/exe (deleted)`.
16181625/// TODO make the return type of this a null terminated pointer
16191626pub fn selfExePath(out_buffer: *[MAX_PATH_BYTES]u8) SelfExePathError![]u8 {
1620 if (comptime std.Target.current.isDarwin()) {
1627 if (is_darwin) {
16211628 var u32_len: u32 = out_buffer.len;
16221629 const rc = std.c._NSGetExecutablePath(out_buffer, &u32_len);
16231630 if (rc != 0) return error.NameTooLong;
lib/std/fs/file.zig+33-44
......@@ -8,7 +8,6 @@ const assert = std.debug.assert;
88const windows = os.windows;
99const Os = builtin.Os;
1010const maxInt = std.math.maxInt;
11const need_async_thread = std.fs.need_async_thread;
1211
1312pub const File = struct {
1413 /// The OS-specific file descriptor or file handle.
......@@ -17,15 +16,14 @@ pub const File = struct {
1716 /// On some systems, such as Linux, file system file descriptors are incapable of non-blocking I/O.
1817 /// This forces us to perform asynchronous I/O on a dedicated thread, to achieve non-blocking
1918 /// 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.
21 io_mode: io.Mode,
19 /// or, more specifically, whether the I/O is always blocking.
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, although
24 /// 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,
26
27 pub const async_block_allowed_yes = if (io.is_async) true else {};
28 pub const async_block_allowed_no = if (io.is_async) false else {};
22 /// Furthermore, even when `std.io.mode` is async, it is still sometimes desirable to perform blocking I/O,
23 /// although not by default. For example, when printing a stack trace to stderr.
24 /// This field tracks both by acting as an overriding I/O mode. When not building in async I/O mode,
25 /// the type only has the `.blocking` tag, making it a zero-bit type.
26 intended_io_mode: io.ModeOverride = io.default_mode,
2927
3028 pub const Mode = os.mode_t;
3129
......@@ -36,9 +34,7 @@ pub const File = struct {
3634
3735 pub const OpenError = windows.CreateFileError || os.OpenError || os.FlockError;
3836
39 pub const Lock = enum {
40 None, Shared, Exclusive
41 };
37 pub const Lock = enum { None, Shared, Exclusive };
4238
4339 /// TODO https://github.com/ziglang/zig/issues/3802
4440 pub const OpenFlags = struct {
......@@ -62,15 +58,16 @@ pub const File = struct {
6258
6359 /// Sets whether or not to wait until the file is locked to return. If set to true,
6460 /// `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 always
66 /// used, and `true` means `error.WouldBlock` is returned, and `false` means
67 /// `error.WouldBlock` is handled by the event loop.
61 /// is available to proceed.
62 /// In async I/O mode, non-blocking at the OS level is
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.
6865 lock_nonblocking: bool = false,
6966
70 /// This prevents `O_NONBLOCK` from being passed even if `std.io.is_async`.
71 /// It allows the use of `noasync` when calling functions related to opening
72 /// the file, reading, writing, as well as locking functionality.
73 always_blocking: bool = false,
67 /// Setting this to `.blocking` prevents `O_NONBLOCK` from being passed even
68 /// if `std.io.is_async`. It allows the use of `noasync` when calling functions
69 /// related to opening the file, reading, writing, and locking.
70 intended_io_mode: io.ModeOverride = io.default_mode,
7471 };
7572
7673 /// TODO https://github.com/ziglang/zig/issues/3802
......@@ -104,17 +101,25 @@ pub const File = struct {
104101 /// Sets whether or not to wait until the file is locked to return. If set to true,
105102 /// `error.WouldBlock` will be returned. Otherwise, the file will wait until the file
106103 /// 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.
107107 lock_nonblocking: bool = false,
108108
109109 /// For POSIX systems this is the file system mode the file will
110110 /// be created with.
111111 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,
112117 };
113118
114119 /// Upon success, the stream is in an uninitialized state. To continue using it,
115120 /// you must use the open() function.
116121 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) {
118123 std.event.Loop.instance.?.close(self.handle);
119124 } else {
120125 os.close(self.handle);
......@@ -297,11 +302,7 @@ pub const File = struct {
297302 pub const PReadError = os.PReadError;
298303
299304 pub fn read(self: File, buffer: []u8) ReadError!usize {
300 if (builtin.os.tag == .windows) {
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 if (self.capable_io_mode != self.intended_io_mode) {
305306 return std.event.Loop.instance.?.read(self.handle, buffer);
306307 } else {
307308 return os.read(self.handle, buffer);
......@@ -321,11 +322,7 @@ pub const File = struct {
321322 }
322323
323324 pub fn pread(self: File, buffer: []u8, offset: u64) PReadError!usize {
324 if (builtin.os.tag == .windows) {
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) {
325 if (self.capable_io_mode != self.intended_io_mode) {
329326 return std.event.Loop.instance.?.pread(self.handle, buffer, offset);
330327 } else {
331328 return os.pread(self.handle, buffer, offset);
......@@ -345,7 +342,7 @@ pub const File = struct {
345342 }
346343
347344 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) {
349346 return std.event.Loop.instance.?.readv(self.handle, iovecs);
350347 } else {
351348 return os.readv(self.handle, iovecs);
......@@ -379,7 +376,7 @@ pub const File = struct {
379376 }
380377
381378 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) {
383380 return std.event.Loop.instance.?.preadv(self.handle, iovecs, offset);
384381 } else {
385382 return os.preadv(self.handle, iovecs, offset);
......@@ -416,11 +413,7 @@ pub const File = struct {
416413 pub const PWriteError = os.PWriteError;
417414
418415 pub fn write(self: File, bytes: []const u8) WriteError!usize {
419 if (builtin.os.tag == .windows) {
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) {
416 if (self.capable_io_mode != self.intended_io_mode) {
424417 return std.event.Loop.instance.?.write(self.handle, bytes);
425418 } else {
426419 return os.write(self.handle, bytes);
......@@ -435,11 +428,7 @@ pub const File = struct {
435428 }
436429
437430 pub fn pwrite(self: File, bytes: []const u8, offset: u64) PWriteError!usize {
438 if (builtin.os.tag == .windows) {
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) {
431 if (self.capable_io_mode != self.intended_io_mode) {
443432 return std.event.Loop.instance.?.pwrite(self.handle, bytes, offset);
444433 } else {
445434 return os.pwrite(self.handle, bytes, offset);
......@@ -454,7 +443,7 @@ pub const File = struct {
454443 }
455444
456445 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) {
458447 return std.event.Loop.instance.?.writev(self.handle, iovecs);
459448 } else {
460449 return os.writev(self.handle, iovecs);
......@@ -480,7 +469,7 @@ pub const File = struct {
480469 }
481470
482471 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) {
484473 return std.event.Loop.instance.?.pwritev(self.handle, iovecs, offset);
485474 } else {
486475 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" {
2727}
2828
2929test "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
3237 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" {
5863test "create file, lock and read from multiple process at once" {
5964 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
6171 if (true) {
6272 // https://github.com/ziglang/zig/issues/5006
6373 return error.SkipZigTest;
lib/std/io.zig+17-8
......@@ -30,6 +30,11 @@ else
3030 Mode.blocking;
3131pub 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
3338fn getStdOutHandle() os.fd_t {
3439 if (builtin.os.tag == .windows) {
3540 return os.windows.peb().ProcessParameters.hStdOutput;
......@@ -42,12 +47,13 @@ fn getStdOutHandle() os.fd_t {
4247 return os.STDOUT_FILENO;
4348}
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
4652pub fn getStdOut() File {
4753 return File{
4854 .handle = getStdOutHandle(),
49 .io_mode = .blocking,
50 .async_block_allowed = if (builtin.os.tag == .windows) File.async_block_allowed_yes else File.async_block_allowed_no,
55 .capable_io_mode = .blocking,
56 .intended_io_mode = default_mode,
5157 };
5258}
5359
......@@ -63,11 +69,13 @@ fn getStdErrHandle() os.fd_t {
6369 return os.STDERR_FILENO;
6470}
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.
6674pub fn getStdErr() File {
6775 return File{
6876 .handle = getStdErrHandle(),
69 .io_mode = .blocking,
70 .async_block_allowed = File.async_block_allowed_yes,
77 .capable_io_mode = .blocking,
78 .intended_io_mode = .blocking,
7179 };
7280}
7381
......@@ -83,12 +91,13 @@ fn getStdInHandle() os.fd_t {
8391 return os.STDIN_FILENO;
8492}
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
8796pub fn getStdIn() File {
8897 return File{
8998 .handle = getStdInHandle(),
90 .io_mode = .blocking,
91 .async_block_allowed = if (builtin.os.tag == .windows) File.async_block_allowed_yes else File.async_block_allowed_no,
99 .capable_io_mode = .blocking,
100 .intended_io_mode = default_mode,
92101 };
93102}
94103
lib/std/net.zig+2-5
......@@ -412,7 +412,7 @@ pub fn tcpConnectToAddress(address: Address) !fs.File {
412412 errdefer os.close(sockfd);
413413 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 };
416416}
417417
418418/// Call `AddressList.deinit` on the result.
......@@ -1381,10 +1381,7 @@ pub const StreamServer = struct {
13811381 var adr_len: os.socklen_t = @sizeOf(Address);
13821382 if (os.accept4(self.sockfd.?, &accepted_addr.any, &adr_len, accept_flags)) |fd| {
13831383 return Connection{
1384 .file = fs.File{
1385 .handle = fd,
1386 .io_mode = std.io.mode,
1387 },
1384 .file = fs.File{ .handle = fd },
13881385 .address = accepted_addr,
13891386 };
13901387 } else |err| switch (err) {
lib/std/os.zig+6-6
......@@ -173,8 +173,8 @@ fn getRandomBytesDevURandom(buf: []u8) !void {
173173
174174 const file = std.fs.File{
175175 .handle = fd,
176 .io_mode = .blocking,
177 .async_block_allowed = std.fs.File.async_block_allowed_yes,
176 .capable_io_mode = .blocking,
177 .intended_io_mode = .blocking,
178178 };
179179 const stream = file.inStream();
180180 stream.readNoEof(buf) catch return error.Unexpected;
......@@ -305,7 +305,7 @@ pub const ReadError = error{
305305/// For POSIX the limit is `math.maxInt(isize)`.
306306pub fn read(fd: fd_t, buf: []u8) ReadError!usize {
307307 if (builtin.os.tag == .windows) {
308 return windows.ReadFile(fd, buf, null, false);
308 return windows.ReadFile(fd, buf, null, std.io.default_mode);
309309 }
310310
311311 if (builtin.os.tag == .wasi and !builtin.link_libc) {
......@@ -408,7 +408,7 @@ pub const PReadError = ReadError || error{Unseekable};
408408/// used to perform the I/O. `error.WouldBlock` is not possible on Windows.
409409pub fn pread(fd: fd_t, buf: []u8, offset: u64) PReadError!usize {
410410 if (builtin.os.tag == .windows) {
411 return windows.ReadFile(fd, buf, offset, false);
411 return windows.ReadFile(fd, buf, offset, std.io.default_mode);
412412 }
413413
414414 while (true) {
......@@ -584,7 +584,7 @@ pub const WriteError = error{
584584/// The corresponding POSIX limit is `math.maxInt(isize)`.
585585pub fn write(fd: fd_t, bytes: []const u8) WriteError!usize {
586586 if (builtin.os.tag == .windows) {
587 return windows.WriteFile(fd, bytes, null, false);
587 return windows.WriteFile(fd, bytes, null, std.io.default_mode);
588588 }
589589
590590 if (builtin.os.tag == .wasi and !builtin.link_libc) {
......@@ -709,7 +709,7 @@ pub const PWriteError = WriteError || error{Unseekable};
709709/// The corresponding POSIX limit is `math.maxInt(isize)`.
710710pub fn pwrite(fd: fd_t, bytes: []const u8, offset: u64) PWriteError!usize {
711711 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);
713713 }
714714
715715 // Prevent EINVAL.
lib/std/os/windows.zig+25-10
......@@ -110,7 +110,7 @@ pub const OpenFileOptions = struct {
110110 share_access: ULONG = FILE_SHARE_WRITE | FILE_SHARE_READ | FILE_SHARE_DELETE,
111111 share_access_nonblocking: bool = false,
112112 creation: ULONG,
113 enable_async_io: bool = std.io.is_async,
113 io_mode: std.io.ModeOverride,
114114};
115115
116116/// 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
145145
146146 var delay: usize = 1;
147147 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;
149149 const rc = ntdll.NtCreateFile(
150150 &result,
151151 options.access_mask,
......@@ -451,11 +451,11 @@ pub const ReadFileError = error{
451451
452452/// If buffer's length exceeds what a Windows DWORD integer can hold, it will be broken into
453453/// multiple non-atomic reads.
454pub fn ReadFile(in_hFile: HANDLE, buffer: []u8, offset: ?u64, enable_async_io: bool) ReadFileError!usize {
455 if (std.event.Loop.instance != null and enable_async_io) {
454pub fn ReadFile(in_hFile: HANDLE, buffer: []u8, offset: ?u64, io_mode: std.io.ModeOverride) ReadFileError!usize {
455 if (io_mode != .blocking) {
456456 const loop = std.event.Loop.instance.?;
457 // TODO support async ReadFile with no offset
458 const off = if (offset == null) 0 else offset.?;
457 // TODO make getting the file position non-blocking
458 const off = if (offset) |o| o else try SetFilePointerEx_CURRENT_get(in_hFile);
459459 var resume_node = std.event.Loop.ResumeNode.Basic{
460460 .base = .{
461461 .id = .Basic,
......@@ -486,6 +486,11 @@ pub fn ReadFile(in_hFile: HANDLE, buffer: []u8, offset: ?u64, enable_async_io: b
486486 else => |err| return unexpectedError(err),
487487 }
488488 }
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 }
489494 return @as(usize, bytes_transferred);
490495 } else {
491496 var index: usize = 0;
......@@ -525,11 +530,16 @@ pub const WriteFileError = error{
525530 Unexpected,
526531};
527532
528pub fn WriteFile(handle: HANDLE, bytes: []const u8, offset: ?u64, enable_async_io: bool) WriteFileError!usize {
529 if (std.event.Loop.instance != null and enable_async_io) {
533pub fn WriteFile(
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) {
530540 const loop = std.event.Loop.instance.?;
531 // TODO support async WriteFile with no offset
532 const off = if (offset == null) 0 else offset.?;
541 // TODO make getting the file position non-blocking
542 const off = if (offset) |o| o else try SetFilePointerEx_CURRENT_get(handle);
533543 var resume_node = std.event.Loop.ResumeNode.Basic{
534544 .base = .{
535545 .id = .Basic,
......@@ -562,6 +572,11 @@ pub fn WriteFile(handle: HANDLE, bytes: []const u8, offset: ?u64, enable_async_i
562572 else => |err| return unexpectedError(err),
563573 }
564574 }
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 }
565580 return bytes_transferred;
566581 } else {
567582 var bytes_written: DWORD = undefined;
lib/std/pdb.zig+1-1
......@@ -470,7 +470,7 @@ pub const Pdb = struct {
470470 msf: Msf,
471471
472472 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 });
474474 self.allocator = coff_ptr.allocator;
475475 self.coff = coff_ptr;
476476