| author | |
| committer | |
| log | 03a7124543a52f7904090516e572b71b893c7ba2 |
| tree | f21587b5bfbb2ff0e209bbd3c4014d01bf49cc4b |
| parent | b7914d901c8c5761457a4774858f1004febc2d3a |
| parent | 7998e2b0f41bff86d8fbbb8112dd6c629d47e849 |
| signature | Signed by PGP key 4AEE18F83AFDEB23 |
fix behavior test with --test-evented-io on windows15 files changed, 646 insertions(+), 663 deletions(-)
lib/std/child_process.zig+10-30| ... | @@ -49,8 +49,6 @@ pub const ChildProcess = struct { | ... | @@ -49,8 +49,6 @@ pub const ChildProcess = struct { |
| 49 | /// Set to change the current working directory when spawning the child process. | 49 | /// Set to change the current working directory when spawning the child process. |
| 50 | /// This is not yet implemented for Windows. See https://github.com/ziglang/zig/issues/5190 | 50 | /// This is not yet implemented for Windows. See https://github.com/ziglang/zig/issues/5190 |
| 51 | /// Once that is done, `cwd` will be deprecated in favor of this field. | 51 | /// Once that is done, `cwd` will be deprecated in favor of this field. |
| 52 | /// The directory handle must be opened with the ability to be passed | ||
| 53 | /// to a child process (no `O_CLOEXEC` flag on POSIX). | ||
| 54 | cwd_dir: ?fs.Dir = null, | 52 | cwd_dir: ?fs.Dir = null, |
| 55 | 53 | ||
| 56 | err_pipe: if (builtin.os.tag == .windows) void else [2]os.fd_t, | 54 | err_pipe: if (builtin.os.tag == .windows) void else [2]os.fd_t, |
| ... | @@ -443,26 +441,17 @@ pub const ChildProcess = struct { | ... | @@ -443,26 +441,17 @@ pub const ChildProcess = struct { |
| 443 | // we are the parent | 441 | // we are the parent |
| 444 | const pid = @intCast(i32, pid_result); | 442 | const pid = @intCast(i32, pid_result); |
| 445 | if (self.stdin_behavior == StdIo.Pipe) { | 443 | if (self.stdin_behavior == StdIo.Pipe) { |
| 446 | self.stdin = File{ | 444 | self.stdin = File{ .handle = stdin_pipe[1] }; |
| 447 | .handle = stdin_pipe[1], | ||
| 448 | .io_mode = std.io.mode, | ||
| 449 | }; | ||
| 450 | } else { | 445 | } else { |
| 451 | self.stdin = null; | 446 | self.stdin = null; |
| 452 | } | 447 | } |
| 453 | if (self.stdout_behavior == StdIo.Pipe) { | 448 | if (self.stdout_behavior == StdIo.Pipe) { |
| 454 | self.stdout = File{ | 449 | self.stdout = File{ .handle = stdout_pipe[0] }; |
| 455 | .handle = stdout_pipe[0], | ||
| 456 | .io_mode = std.io.mode, | ||
| 457 | }; | ||
| 458 | } else { | 450 | } else { |
| 459 | self.stdout = null; | 451 | self.stdout = null; |
| 460 | } | 452 | } |
| 461 | if (self.stderr_behavior == StdIo.Pipe) { | 453 | if (self.stderr_behavior == StdIo.Pipe) { |
| 462 | self.stderr = File{ | 454 | self.stderr = File{ .handle = stderr_pipe[0] }; |
| 463 | .handle = stderr_pipe[0], | ||
| 464 | .io_mode = std.io.mode, | ||
| 465 | }; | ||
| 466 | } else { | 455 | } else { |
| 467 | self.stderr = null; | 456 | self.stderr = null; |
| 468 | } | 457 | } |
| ... | @@ -686,26 +675,17 @@ pub const ChildProcess = struct { | ... | @@ -686,26 +675,17 @@ pub const ChildProcess = struct { |
| 686 | }; | 675 | }; |
| 687 | 676 | ||
| 688 | if (g_hChildStd_IN_Wr) |h| { | 677 | if (g_hChildStd_IN_Wr) |h| { |
| 689 | self.stdin = File{ | 678 | self.stdin = File{ .handle = h }; |
| 690 | .handle = h, | ||
| 691 | .io_mode = io.mode, | ||
| 692 | }; | ||
| 693 | } else { | 679 | } else { |
| 694 | self.stdin = null; | 680 | self.stdin = null; |
| 695 | } | 681 | } |
| 696 | if (g_hChildStd_OUT_Rd) |h| { | 682 | if (g_hChildStd_OUT_Rd) |h| { |
| 697 | self.stdout = File{ | 683 | self.stdout = File{ .handle = h }; |
| 698 | .handle = h, | ||
| 699 | .io_mode = io.mode, | ||
| 700 | }; | ||
| 701 | } else { | 684 | } else { |
| 702 | self.stdout = null; | 685 | self.stdout = null; |
| 703 | } | 686 | } |
| 704 | if (g_hChildStd_ERR_Rd) |h| { | 687 | if (g_hChildStd_ERR_Rd) |h| { |
| 705 | self.stderr = File{ | 688 | self.stderr = File{ .handle = h }; |
| 706 | .handle = h, | ||
| 707 | .io_mode = io.mode, | ||
| 708 | }; | ||
| 709 | } else { | 689 | } else { |
| 710 | self.stderr = null; | 690 | self.stderr = null; |
| 711 | } | 691 | } |
| ... | @@ -845,8 +825,8 @@ const ErrInt = std.meta.Int(false, @sizeOf(anyerror) * 8); | ... | @@ -845,8 +825,8 @@ const ErrInt = std.meta.Int(false, @sizeOf(anyerror) * 8); |
| 845 | fn writeIntFd(fd: i32, value: ErrInt) !void { | 825 | fn writeIntFd(fd: i32, value: ErrInt) !void { |
| 846 | const file = File{ | 826 | const file = File{ |
| 847 | .handle = fd, | 827 | .handle = fd, |
| 848 | .io_mode = .blocking, | 828 | .capable_io_mode = .blocking, |
| 849 | .async_block_allowed = File.async_block_allowed_yes, | 829 | .intended_io_mode = .blocking, |
| 850 | }; | 830 | }; |
| 851 | file.outStream().writeIntNative(u64, @intCast(u64, value)) catch return error.SystemResources; | 831 | file.outStream().writeIntNative(u64, @intCast(u64, value)) catch return error.SystemResources; |
| 852 | } | 832 | } |
| ... | @@ -854,8 +834,8 @@ fn writeIntFd(fd: i32, value: ErrInt) !void { | ... | @@ -854,8 +834,8 @@ fn writeIntFd(fd: i32, value: ErrInt) !void { |
| 854 | fn readIntFd(fd: i32) !ErrInt { | 834 | fn readIntFd(fd: i32) !ErrInt { |
| 855 | const file = File{ | 835 | const file = File{ |
| 856 | .handle = fd, | 836 | .handle = fd, |
| 857 | .io_mode = .blocking, | 837 | .capable_io_mode = .blocking, |
| 858 | .async_block_allowed = File.async_block_allowed_yes, | 838 | .intended_io_mode = .blocking, |
| 859 | }; | 839 | }; |
| 860 | return @intCast(ErrInt, file.inStream().readIntNative(u64) catch return error.SystemResources); | 840 | return @intCast(ErrInt, file.inStream().readIntNative(u64) catch return error.SystemResources); |
| 861 | } | 841 | } |
lib/std/debug.zig+219-209| ... | @@ -112,39 +112,43 @@ pub fn detectTTYConfig() TTY.Config { | ... | @@ -112,39 +112,43 @@ pub fn detectTTYConfig() TTY.Config { |
| 112 | /// Tries to print the current stack trace to stderr, unbuffered, and ignores any error returned. | 112 | /// Tries to print the current stack trace to stderr, unbuffered, and ignores any error returned. |
| 113 | /// TODO multithreaded awareness | 113 | /// TODO multithreaded awareness |
| 114 | pub fn dumpCurrentStackTrace(start_addr: ?usize) void { | 114 | pub fn dumpCurrentStackTrace(start_addr: ?usize) void { |
| 115 | const stderr = getStderrStream(); | 115 | noasync { |
| 116 | if (builtin.strip_debug_info) { | 116 | const stderr = getStderrStream(); |
| 117 | noasync stderr.print("Unable to dump stack trace: debug info stripped\n", .{}) catch return; | 117 | if (builtin.strip_debug_info) { |
| 118 | return; | 118 | stderr.print("Unable to dump stack trace: debug info stripped\n", .{}) catch return; |
| 119 | return; | ||
| 120 | } | ||
| 121 | const debug_info = getSelfDebugInfo() catch |err| { | ||
| 122 | stderr.print("Unable to dump stack trace: Unable to open debug info: {}\n", .{@errorName(err)}) catch return; | ||
| 123 | return; | ||
| 124 | }; | ||
| 125 | writeCurrentStackTrace(stderr, debug_info, detectTTYConfig(), start_addr) catch |err| { | ||
| 126 | stderr.print("Unable to dump stack trace: {}\n", .{@errorName(err)}) catch return; | ||
| 127 | return; | ||
| 128 | }; | ||
| 119 | } | 129 | } |
| 120 | const debug_info = getSelfDebugInfo() catch |err| { | ||
| 121 | noasync stderr.print("Unable to dump stack trace: Unable to open debug info: {}\n", .{@errorName(err)}) catch return; | ||
| 122 | return; | ||
| 123 | }; | ||
| 124 | writeCurrentStackTrace(stderr, debug_info, detectTTYConfig(), start_addr) catch |err| { | ||
| 125 | noasync stderr.print("Unable to dump stack trace: {}\n", .{@errorName(err)}) catch return; | ||
| 126 | return; | ||
| 127 | }; | ||
| 128 | } | 130 | } |
| 129 | 131 | ||
| 130 | /// Tries to print the stack trace starting from the supplied base pointer to stderr, | 132 | /// Tries to print the stack trace starting from the supplied base pointer to stderr, |
| 131 | /// unbuffered, and ignores any error returned. | 133 | /// unbuffered, and ignores any error returned. |
| 132 | /// TODO multithreaded awareness | 134 | /// TODO multithreaded awareness |
| 133 | pub fn dumpStackTraceFromBase(bp: usize, ip: usize) void { | 135 | pub fn dumpStackTraceFromBase(bp: usize, ip: usize) void { |
| 134 | const stderr = getStderrStream(); | 136 | noasync { |
| 135 | if (builtin.strip_debug_info) { | 137 | const stderr = getStderrStream(); |
| 136 | noasync stderr.print("Unable to dump stack trace: debug info stripped\n", .{}) catch return; | 138 | if (builtin.strip_debug_info) { |
| 137 | return; | 139 | stderr.print("Unable to dump stack trace: debug info stripped\n", .{}) catch return; |
| 138 | } | 140 | return; |
| 139 | const debug_info = getSelfDebugInfo() catch |err| { | 141 | } |
| 140 | noasync stderr.print("Unable to dump stack trace: Unable to open debug info: {}\n", .{@errorName(err)}) catch return; | 142 | const debug_info = getSelfDebugInfo() catch |err| { |
| 141 | return; | 143 | stderr.print("Unable to dump stack trace: Unable to open debug info: {}\n", .{@errorName(err)}) catch return; |
| 142 | }; | 144 | return; |
| 143 | const tty_config = detectTTYConfig(); | 145 | }; |
| 144 | printSourceAtAddress(debug_info, stderr, ip, tty_config) catch return; | 146 | const tty_config = detectTTYConfig(); |
| 145 | var it = StackIterator.init(null, bp); | 147 | printSourceAtAddress(debug_info, stderr, ip, tty_config) catch return; |
| 146 | while (it.next()) |return_address| { | 148 | var it = StackIterator.init(null, bp); |
| 147 | printSourceAtAddress(debug_info, stderr, return_address - 1, tty_config) catch return; | 149 | while (it.next()) |return_address| { |
| 150 | printSourceAtAddress(debug_info, stderr, return_address - 1, tty_config) catch return; | ||
| 151 | } | ||
| 148 | } | 152 | } |
| 149 | } | 153 | } |
| 150 | 154 | ||
| ... | @@ -199,19 +203,21 @@ pub fn captureStackTrace(first_address: ?usize, stack_trace: *builtin.StackTrace | ... | @@ -199,19 +203,21 @@ pub fn captureStackTrace(first_address: ?usize, stack_trace: *builtin.StackTrace |
| 199 | /// Tries to print a stack trace to stderr, unbuffered, and ignores any error returned. | 203 | /// Tries to print a stack trace to stderr, unbuffered, and ignores any error returned. |
| 200 | /// TODO multithreaded awareness | 204 | /// TODO multithreaded awareness |
| 201 | pub fn dumpStackTrace(stack_trace: builtin.StackTrace) void { | 205 | pub fn dumpStackTrace(stack_trace: builtin.StackTrace) void { |
| 202 | const stderr = getStderrStream(); | 206 | noasync { |
| 203 | if (builtin.strip_debug_info) { | 207 | const stderr = getStderrStream(); |
| 204 | noasync stderr.print("Unable to dump stack trace: debug info stripped\n", .{}) catch return; | 208 | if (builtin.strip_debug_info) { |
| 205 | return; | 209 | stderr.print("Unable to dump stack trace: debug info stripped\n", .{}) catch return; |
| 210 | return; | ||
| 211 | } | ||
| 212 | const debug_info = getSelfDebugInfo() catch |err| { | ||
| 213 | stderr.print("Unable to dump stack trace: Unable to open debug info: {}\n", .{@errorName(err)}) catch return; | ||
| 214 | return; | ||
| 215 | }; | ||
| 216 | writeStackTrace(stack_trace, stderr, getDebugInfoAllocator(), debug_info, detectTTYConfig()) catch |err| { | ||
| 217 | stderr.print("Unable to dump stack trace: {}\n", .{@errorName(err)}) catch return; | ||
| 218 | return; | ||
| 219 | }; | ||
| 206 | } | 220 | } |
| 207 | const debug_info = getSelfDebugInfo() catch |err| { | ||
| 208 | noasync stderr.print("Unable to dump stack trace: Unable to open debug info: {}\n", .{@errorName(err)}) catch return; | ||
| 209 | return; | ||
| 210 | }; | ||
| 211 | writeStackTrace(stack_trace, stderr, getDebugInfoAllocator(), debug_info, detectTTYConfig()) catch |err| { | ||
| 212 | noasync stderr.print("Unable to dump stack trace: {}\n", .{@errorName(err)}) catch return; | ||
| 213 | return; | ||
| 214 | }; | ||
| 215 | } | 221 | } |
| 216 | 222 | ||
| 217 | /// This function invokes undefined behavior when `ok` is `false`. | 223 | /// This function invokes undefined behavior when `ok` is `false`. |
| ... | @@ -255,7 +261,7 @@ pub fn panicExtra(trace: ?*const builtin.StackTrace, first_trace_addr: ?usize, c | ... | @@ -255,7 +261,7 @@ pub fn panicExtra(trace: ?*const builtin.StackTrace, first_trace_addr: ?usize, c |
| 255 | resetSegfaultHandler(); | 261 | resetSegfaultHandler(); |
| 256 | } | 262 | } |
| 257 | 263 | ||
| 258 | switch (panic_stage) { | 264 | noasync switch (panic_stage) { |
| 259 | 0 => { | 265 | 0 => { |
| 260 | panic_stage = 1; | 266 | panic_stage = 1; |
| 261 | 267 | ||
| ... | @@ -267,7 +273,7 @@ pub fn panicExtra(trace: ?*const builtin.StackTrace, first_trace_addr: ?usize, c | ... | @@ -267,7 +273,7 @@ pub fn panicExtra(trace: ?*const builtin.StackTrace, first_trace_addr: ?usize, c |
| 267 | defer held.release(); | 273 | defer held.release(); |
| 268 | 274 | ||
| 269 | const stderr = getStderrStream(); | 275 | const stderr = getStderrStream(); |
| 270 | noasync stderr.print(format ++ "\n", args) catch os.abort(); | 276 | stderr.print(format ++ "\n", args) catch os.abort(); |
| 271 | if (trace) |t| { | 277 | if (trace) |t| { |
| 272 | dumpStackTrace(t.*); | 278 | dumpStackTrace(t.*); |
| 273 | } | 279 | } |
| ... | @@ -292,12 +298,12 @@ pub fn panicExtra(trace: ?*const builtin.StackTrace, first_trace_addr: ?usize, c | ... | @@ -292,12 +298,12 @@ pub fn panicExtra(trace: ?*const builtin.StackTrace, first_trace_addr: ?usize, c |
| 292 | // we're still holding the mutex but that's fine as we're going to | 298 | // we're still holding the mutex but that's fine as we're going to |
| 293 | // call abort() | 299 | // call abort() |
| 294 | const stderr = getStderrStream(); | 300 | const stderr = getStderrStream(); |
| 295 | noasync stderr.print("Panicked during a panic. Aborting.\n", .{}) catch os.abort(); | 301 | stderr.print("Panicked during a panic. Aborting.\n", .{}) catch os.abort(); |
| 296 | }, | 302 | }, |
| 297 | else => { | 303 | else => { |
| 298 | // Panicked while printing "Panicked during a panic." | 304 | // Panicked while printing "Panicked during a panic." |
| 299 | }, | 305 | }, |
| 300 | } | 306 | }; |
| 301 | 307 | ||
| 302 | os.abort(); | 308 | os.abort(); |
| 303 | } | 309 | } |
| ... | @@ -666,158 +672,160 @@ pub fn openSelfDebugInfo(allocator: *mem.Allocator) anyerror!DebugInfo { | ... | @@ -666,158 +672,160 @@ pub fn openSelfDebugInfo(allocator: *mem.Allocator) anyerror!DebugInfo { |
| 666 | 672 | ||
| 667 | /// TODO resources https://github.com/ziglang/zig/issues/4353 | 673 | /// TODO resources https://github.com/ziglang/zig/issues/4353 |
| 668 | fn openCoffDebugInfo(allocator: *mem.Allocator, coff_file_path: [:0]const u16) !ModuleDebugInfo { | 674 | fn openCoffDebugInfo(allocator: *mem.Allocator, coff_file_path: [:0]const u16) !ModuleDebugInfo { |
| 669 | const coff_file = try std.fs.openFileAbsoluteW(coff_file_path.ptr, .{}); | 675 | noasync { |
| 670 | errdefer coff_file.close(); | 676 | const coff_file = try std.fs.openFileAbsoluteW(coff_file_path, .{ .intended_io_mode = .blocking }); |
| 677 | errdefer coff_file.close(); | ||
| 671 | 678 | ||
| 672 | const coff_obj = try allocator.create(coff.Coff); | 679 | const coff_obj = try allocator.create(coff.Coff); |
| 673 | coff_obj.* = coff.Coff.init(allocator, coff_file); | 680 | coff_obj.* = coff.Coff.init(allocator, coff_file); |
| 674 | 681 | ||
| 675 | var di = ModuleDebugInfo{ | 682 | var di = ModuleDebugInfo{ |
| 676 | .base_address = undefined, | 683 | .base_address = undefined, |
| 677 | .coff = coff_obj, | 684 | .coff = coff_obj, |
| 678 | .pdb = undefined, | 685 | .pdb = undefined, |
| 679 | .sect_contribs = undefined, | 686 | .sect_contribs = undefined, |
| 680 | .modules = undefined, | 687 | .modules = undefined, |
| 681 | }; | 688 | }; |
| 682 | 689 | ||
| 683 | try di.coff.loadHeader(); | 690 | try di.coff.loadHeader(); |
| 684 | 691 | ||
| 685 | var path_buf: [windows.MAX_PATH]u8 = undefined; | 692 | var path_buf: [windows.MAX_PATH]u8 = undefined; |
| 686 | const len = try di.coff.getPdbPath(path_buf[0..]); | 693 | const len = try di.coff.getPdbPath(path_buf[0..]); |
| 687 | const raw_path = path_buf[0..len]; | 694 | const raw_path = path_buf[0..len]; |
| 688 | 695 | ||
| 689 | const path = try fs.path.resolve(allocator, &[_][]const u8{raw_path}); | 696 | const path = try fs.path.resolve(allocator, &[_][]const u8{raw_path}); |
| 690 | 697 | ||
| 691 | try di.pdb.openFile(di.coff, path); | 698 | try di.pdb.openFile(di.coff, path); |
| 692 | 699 | ||
| 693 | var pdb_stream = di.pdb.getStream(pdb.StreamType.Pdb) orelse return error.InvalidDebugInfo; | 700 | var pdb_stream = di.pdb.getStream(pdb.StreamType.Pdb) orelse return error.InvalidDebugInfo; |
| 694 | const version = try pdb_stream.inStream().readIntLittle(u32); | 701 | const version = try pdb_stream.inStream().readIntLittle(u32); |
| 695 | const signature = try pdb_stream.inStream().readIntLittle(u32); | 702 | const signature = try pdb_stream.inStream().readIntLittle(u32); |
| 696 | const age = try pdb_stream.inStream().readIntLittle(u32); | 703 | const age = try pdb_stream.inStream().readIntLittle(u32); |
| 697 | var guid: [16]u8 = undefined; | 704 | var guid: [16]u8 = undefined; |
| 698 | try pdb_stream.inStream().readNoEof(&guid); | 705 | try pdb_stream.inStream().readNoEof(&guid); |
| 699 | if (version != 20000404) // VC70, only value observed by LLVM team | 706 | if (version != 20000404) // VC70, only value observed by LLVM team |
| 700 | return error.UnknownPDBVersion; | 707 | return error.UnknownPDBVersion; |
| 701 | if (!mem.eql(u8, &di.coff.guid, &guid) or di.coff.age != age) | 708 | if (!mem.eql(u8, &di.coff.guid, &guid) or di.coff.age != age) |
| 702 | return error.PDBMismatch; | 709 | return error.PDBMismatch; |
| 703 | // We validated the executable and pdb match. | 710 | // We validated the executable and pdb match. |
| 704 | 711 | ||
| 705 | const string_table_index = str_tab_index: { | 712 | const string_table_index = str_tab_index: { |
| 706 | const name_bytes_len = try pdb_stream.inStream().readIntLittle(u32); | 713 | const name_bytes_len = try pdb_stream.inStream().readIntLittle(u32); |
| 707 | const name_bytes = try allocator.alloc(u8, name_bytes_len); | 714 | const name_bytes = try allocator.alloc(u8, name_bytes_len); |
| 708 | try pdb_stream.inStream().readNoEof(name_bytes); | 715 | try pdb_stream.inStream().readNoEof(name_bytes); |
| 709 | 716 | ||
| 710 | const HashTableHeader = packed struct { | 717 | const HashTableHeader = packed struct { |
| 711 | Size: u32, | 718 | Size: u32, |
| 712 | Capacity: u32, | 719 | Capacity: u32, |
| 713 | 720 | ||
| 714 | fn maxLoad(cap: u32) u32 { | 721 | fn maxLoad(cap: u32) u32 { |
| 715 | return cap * 2 / 3 + 1; | 722 | return cap * 2 / 3 + 1; |
| 716 | } | 723 | } |
| 717 | }; | 724 | }; |
| 718 | const hash_tbl_hdr = try pdb_stream.inStream().readStruct(HashTableHeader); | 725 | const hash_tbl_hdr = try pdb_stream.inStream().readStruct(HashTableHeader); |
| 719 | if (hash_tbl_hdr.Capacity == 0) | 726 | if (hash_tbl_hdr.Capacity == 0) |
| 720 | return error.InvalidDebugInfo; | 727 | return error.InvalidDebugInfo; |
| 721 | 728 | ||
| 722 | if (hash_tbl_hdr.Size > HashTableHeader.maxLoad(hash_tbl_hdr.Capacity)) | 729 | if (hash_tbl_hdr.Size > HashTableHeader.maxLoad(hash_tbl_hdr.Capacity)) |
| 723 | return error.InvalidDebugInfo; | 730 | return error.InvalidDebugInfo; |
| 724 | 731 | ||
| 725 | const present = try readSparseBitVector(&pdb_stream.inStream(), allocator); | 732 | const present = try readSparseBitVector(&pdb_stream.inStream(), allocator); |
| 726 | if (present.len != hash_tbl_hdr.Size) | 733 | if (present.len != hash_tbl_hdr.Size) |
| 727 | return error.InvalidDebugInfo; | 734 | return error.InvalidDebugInfo; |
| 728 | const deleted = try readSparseBitVector(&pdb_stream.inStream(), allocator); | 735 | const deleted = try readSparseBitVector(&pdb_stream.inStream(), allocator); |
| 729 | 736 | ||
| 730 | const Bucket = struct { | 737 | const Bucket = struct { |
| 731 | first: u32, | 738 | first: u32, |
| 732 | second: u32, | 739 | second: u32, |
| 733 | }; | 740 | }; |
| 734 | const bucket_list = try allocator.alloc(Bucket, present.len); | 741 | const bucket_list = try allocator.alloc(Bucket, present.len); |
| 735 | for (present) |_| { | 742 | for (present) |_| { |
| 736 | const name_offset = try pdb_stream.inStream().readIntLittle(u32); | 743 | const name_offset = try pdb_stream.inStream().readIntLittle(u32); |
| 737 | const name_index = try pdb_stream.inStream().readIntLittle(u32); | 744 | const name_index = try pdb_stream.inStream().readIntLittle(u32); |
| 738 | const name = mem.spanZ(@ptrCast([*:0]u8, name_bytes.ptr + name_offset)); | 745 | const name = mem.spanZ(@ptrCast([*:0]u8, name_bytes.ptr + name_offset)); |
| 739 | if (mem.eql(u8, name, "/names")) { | 746 | if (mem.eql(u8, name, "/names")) { |
| 740 | break :str_tab_index name_index; | 747 | break :str_tab_index name_index; |
| 748 | } | ||
| 741 | } | 749 | } |
| 742 | } | 750 | return error.MissingDebugInfo; |
| 743 | return error.MissingDebugInfo; | 751 | }; |
| 744 | }; | ||
| 745 | 752 | ||
| 746 | di.pdb.string_table = di.pdb.getStreamById(string_table_index) orelse return error.MissingDebugInfo; | 753 | di.pdb.string_table = di.pdb.getStreamById(string_table_index) orelse return error.MissingDebugInfo; |
| 747 | di.pdb.dbi = di.pdb.getStream(pdb.StreamType.Dbi) orelse return error.MissingDebugInfo; | 754 | di.pdb.dbi = di.pdb.getStream(pdb.StreamType.Dbi) orelse return error.MissingDebugInfo; |
| 748 | 755 | ||
| 749 | const dbi = di.pdb.dbi; | 756 | const dbi = di.pdb.dbi; |
| 750 | 757 | ||
| 751 | // Dbi Header | 758 | // Dbi Header |
| 752 | const dbi_stream_header = try dbi.inStream().readStruct(pdb.DbiStreamHeader); | 759 | const dbi_stream_header = try dbi.inStream().readStruct(pdb.DbiStreamHeader); |
| 753 | if (dbi_stream_header.VersionHeader != 19990903) // V70, only value observed by LLVM team | 760 | if (dbi_stream_header.VersionHeader != 19990903) // V70, only value observed by LLVM team |
| 754 | return error.UnknownPDBVersion; | 761 | return error.UnknownPDBVersion; |
| 755 | if (dbi_stream_header.Age != age) | 762 | if (dbi_stream_header.Age != age) |
| 756 | return error.UnmatchingPDB; | 763 | return error.UnmatchingPDB; |
| 757 | 764 | ||
| 758 | const mod_info_size = dbi_stream_header.ModInfoSize; | 765 | const mod_info_size = dbi_stream_header.ModInfoSize; |
| 759 | const section_contrib_size = dbi_stream_header.SectionContributionSize; | 766 | const section_contrib_size = dbi_stream_header.SectionContributionSize; |
| 760 | 767 | ||
| 761 | var modules = ArrayList(Module).init(allocator); | 768 | var modules = ArrayList(Module).init(allocator); |
| 762 | 769 | ||
| 763 | // Module Info Substream | 770 | // Module Info Substream |
| 764 | var mod_info_offset: usize = 0; | 771 | var mod_info_offset: usize = 0; |
| 765 | while (mod_info_offset != mod_info_size) { | 772 | while (mod_info_offset != mod_info_size) { |
| 766 | const mod_info = try dbi.inStream().readStruct(pdb.ModInfo); | 773 | const mod_info = try dbi.inStream().readStruct(pdb.ModInfo); |
| 767 | var this_record_len: usize = @sizeOf(pdb.ModInfo); | 774 | var this_record_len: usize = @sizeOf(pdb.ModInfo); |
| 768 | 775 | ||
| 769 | const module_name = try dbi.readNullTermString(allocator); | 776 | const module_name = try dbi.readNullTermString(allocator); |
| 770 | this_record_len += module_name.len + 1; | 777 | this_record_len += module_name.len + 1; |
| 771 | 778 | ||
| 772 | const obj_file_name = try dbi.readNullTermString(allocator); | 779 | const obj_file_name = try dbi.readNullTermString(allocator); |
| 773 | this_record_len += obj_file_name.len + 1; | 780 | this_record_len += obj_file_name.len + 1; |
| 774 | 781 | ||
| 775 | if (this_record_len % 4 != 0) { | 782 | if (this_record_len % 4 != 0) { |
| 776 | const round_to_next_4 = (this_record_len | 0x3) + 1; | 783 | const round_to_next_4 = (this_record_len | 0x3) + 1; |
| 777 | const march_forward_bytes = round_to_next_4 - this_record_len; | 784 | const march_forward_bytes = round_to_next_4 - this_record_len; |
| 778 | try dbi.seekBy(@intCast(isize, march_forward_bytes)); | 785 | try dbi.seekBy(@intCast(isize, march_forward_bytes)); |
| 779 | this_record_len += march_forward_bytes; | 786 | this_record_len += march_forward_bytes; |
| 780 | } | 787 | } |
| 781 | 788 | ||
| 782 | try modules.append(Module{ | 789 | try modules.append(Module{ |
| 783 | .mod_info = mod_info, | 790 | .mod_info = mod_info, |
| 784 | .module_name = module_name, | 791 | .module_name = module_name, |
| 785 | .obj_file_name = obj_file_name, | 792 | .obj_file_name = obj_file_name, |
| 786 | 793 | ||
| 787 | .populated = false, | 794 | .populated = false, |
| 788 | .symbols = undefined, | 795 | .symbols = undefined, |
| 789 | .subsect_info = undefined, | 796 | .subsect_info = undefined, |
| 790 | .checksum_offset = null, | 797 | .checksum_offset = null, |
| 791 | }); | 798 | }); |
| 792 | 799 | ||
| 793 | mod_info_offset += this_record_len; | 800 | mod_info_offset += this_record_len; |
| 794 | if (mod_info_offset > mod_info_size) | 801 | if (mod_info_offset > mod_info_size) |
| 795 | return error.InvalidDebugInfo; | 802 | return error.InvalidDebugInfo; |
| 796 | } | 803 | } |
| 797 | 804 | ||
| 798 | di.modules = modules.toOwnedSlice(); | 805 | di.modules = modules.toOwnedSlice(); |
| 799 | 806 | ||
| 800 | // Section Contribution Substream | 807 | // Section Contribution Substream |
| 801 | var sect_contribs = ArrayList(pdb.SectionContribEntry).init(allocator); | 808 | var sect_contribs = ArrayList(pdb.SectionContribEntry).init(allocator); |
| 802 | var sect_cont_offset: usize = 0; | 809 | var sect_cont_offset: usize = 0; |
| 803 | if (section_contrib_size != 0) { | 810 | if (section_contrib_size != 0) { |
| 804 | const ver = @intToEnum(pdb.SectionContrSubstreamVersion, try dbi.inStream().readIntLittle(u32)); | 811 | const ver = @intToEnum(pdb.SectionContrSubstreamVersion, try dbi.inStream().readIntLittle(u32)); |
| 805 | if (ver != pdb.SectionContrSubstreamVersion.Ver60) | 812 | if (ver != pdb.SectionContrSubstreamVersion.Ver60) |
| 806 | return error.InvalidDebugInfo; | 813 | return error.InvalidDebugInfo; |
| 807 | sect_cont_offset += @sizeOf(u32); | 814 | sect_cont_offset += @sizeOf(u32); |
| 808 | } | 815 | } |
| 809 | while (sect_cont_offset != section_contrib_size) { | 816 | while (sect_cont_offset != section_contrib_size) { |
| 810 | const entry = try sect_contribs.addOne(); | 817 | const entry = try sect_contribs.addOne(); |
| 811 | entry.* = try dbi.inStream().readStruct(pdb.SectionContribEntry); | 818 | entry.* = try dbi.inStream().readStruct(pdb.SectionContribEntry); |
| 812 | sect_cont_offset += @sizeOf(pdb.SectionContribEntry); | 819 | sect_cont_offset += @sizeOf(pdb.SectionContribEntry); |
| 813 | 820 | ||
| 814 | if (sect_cont_offset > section_contrib_size) | 821 | if (sect_cont_offset > section_contrib_size) |
| 815 | return error.InvalidDebugInfo; | 822 | return error.InvalidDebugInfo; |
| 816 | } | 823 | } |
| 817 | 824 | ||
| 818 | di.sect_contribs = sect_contribs.toOwnedSlice(); | 825 | di.sect_contribs = sect_contribs.toOwnedSlice(); |
| 819 | 826 | ||
| 820 | return di; | 827 | return di; |
| 828 | } | ||
| 821 | } | 829 | } |
| 822 | 830 | ||
| 823 | fn readSparseBitVector(stream: var, allocator: *mem.Allocator) ![]usize { | 831 | fn readSparseBitVector(stream: var, allocator: *mem.Allocator) ![]usize { |
| ... | @@ -1001,7 +1009,7 @@ fn openMachODebugInfo(allocator: *mem.Allocator, macho_file_path: []const u8) !M | ... | @@ -1001,7 +1009,7 @@ fn openMachODebugInfo(allocator: *mem.Allocator, macho_file_path: []const u8) !M |
| 1001 | fn printLineFromFileAnyOs(out_stream: var, line_info: LineInfo) !void { | 1009 | fn printLineFromFileAnyOs(out_stream: var, line_info: LineInfo) !void { |
| 1002 | // Need this to always block even in async I/O mode, because this could potentially | 1010 | // Need this to always block even in async I/O mode, because this could potentially |
| 1003 | // be called from e.g. the event loop code crashing. | 1011 | // be called from e.g. the event loop code crashing. |
| 1004 | var f = try fs.cwd().openFile(line_info.file_name, .{ .always_blocking = true }); | 1012 | var f = try fs.cwd().openFile(line_info.file_name, .{ .intended_io_mode = .blocking }); |
| 1005 | defer f.close(); | 1013 | defer f.close(); |
| 1006 | // TODO fstat and make sure that the file has the correct size | 1014 | // TODO fstat and make sure that the file has the correct size |
| 1007 | 1015 | ||
| ... | @@ -1049,7 +1057,7 @@ const MachoSymbol = struct { | ... | @@ -1049,7 +1057,7 @@ const MachoSymbol = struct { |
| 1049 | 1057 | ||
| 1050 | fn mapWholeFile(path: []const u8) ![]align(mem.page_size) const u8 { | 1058 | fn mapWholeFile(path: []const u8) ![]align(mem.page_size) const u8 { |
| 1051 | noasync { | 1059 | noasync { |
| 1052 | const file = try fs.cwd().openFile(path, .{ .always_blocking = true }); | 1060 | const file = try fs.cwd().openFile(path, .{ .intended_io_mode = .blocking }); |
| 1053 | defer file.close(); | 1061 | defer file.close(); |
| 1054 | 1062 | ||
| 1055 | const file_len = try math.cast(usize, try file.getEndPos()); | 1063 | const file_len = try math.cast(usize, try file.getEndPos()); |
| ... | @@ -1410,59 +1418,61 @@ pub const ModuleDebugInfo = switch (builtin.os.tag) { | ... | @@ -1410,59 +1418,61 @@ pub const ModuleDebugInfo = switch (builtin.os.tag) { |
| 1410 | } | 1418 | } |
| 1411 | 1419 | ||
| 1412 | fn getSymbolAtAddress(self: *@This(), address: usize) !SymbolInfo { | 1420 | fn getSymbolAtAddress(self: *@This(), address: usize) !SymbolInfo { |
| 1413 | // Translate the VA into an address into this object | 1421 | noasync { |
| 1414 | const relocated_address = address - self.base_address; | 1422 | // Translate the VA into an address into this object |
| 1415 | assert(relocated_address >= 0x100000000); | 1423 | const relocated_address = address - self.base_address; |
| 1416 | 1424 | assert(relocated_address >= 0x100000000); | |
| 1417 | // Find the .o file where this symbol is defined | ||
| 1418 | const symbol = machoSearchSymbols(self.symbols, relocated_address) orelse | ||
| 1419 | return SymbolInfo{}; | ||
| 1420 | |||
| 1421 | // Take the symbol name from the N_FUN STAB entry, we're going to | ||
| 1422 | // use it if we fail to find the DWARF infos | ||
| 1423 | const stab_symbol = mem.spanZ(self.strings[symbol.nlist.n_strx..]); | ||
| 1424 | 1425 | ||
| 1425 | if (symbol.ofile == null) | 1426 | // Find the .o file where this symbol is defined |
| 1426 | return SymbolInfo{ .symbol_name = stab_symbol }; | 1427 | const symbol = machoSearchSymbols(self.symbols, relocated_address) orelse |
| 1428 | return SymbolInfo{}; | ||
| 1427 | 1429 | ||
| 1428 | const o_file_path = mem.spanZ(self.strings[symbol.ofile.?.n_strx..]); | 1430 | // Take the symbol name from the N_FUN STAB entry, we're going to |
| 1431 | // use it if we fail to find the DWARF infos | ||
| 1432 | const stab_symbol = mem.spanZ(self.strings[symbol.nlist.n_strx..]); | ||
| 1429 | 1433 | ||
| 1430 | // Check if its debug infos are already in the cache | 1434 | if (symbol.ofile == null) |
| 1431 | var o_file_di = self.ofiles.getValue(o_file_path) orelse | ||
| 1432 | (self.loadOFile(o_file_path) catch |err| switch (err) { | ||
| 1433 | error.FileNotFound, | ||
| 1434 | error.MissingDebugInfo, | ||
| 1435 | error.InvalidDebugInfo, | ||
| 1436 | => { | ||
| 1437 | return SymbolInfo{ .symbol_name = stab_symbol }; | 1435 | return SymbolInfo{ .symbol_name = stab_symbol }; |
| 1438 | }, | ||
| 1439 | else => return err, | ||
| 1440 | }); | ||
| 1441 | 1436 | ||
| 1442 | // Translate again the address, this time into an address inside the | 1437 | const o_file_path = mem.spanZ(self.strings[symbol.ofile.?.n_strx..]); |
| 1443 | // .o file | ||
| 1444 | const relocated_address_o = relocated_address - symbol.reloc; | ||
| 1445 | 1438 | ||
| 1446 | if (o_file_di.findCompileUnit(relocated_address_o)) |compile_unit| { | 1439 | // Check if its debug infos are already in the cache |
| 1447 | return SymbolInfo{ | 1440 | var o_file_di = self.ofiles.getValue(o_file_path) orelse |
| 1448 | .symbol_name = o_file_di.getSymbolName(relocated_address_o) orelse "???", | 1441 | (self.loadOFile(o_file_path) catch |err| switch (err) { |
| 1449 | .compile_unit_name = compile_unit.die.getAttrString(&o_file_di, DW.AT_name) catch |err| switch (err) { | 1442 | error.FileNotFound, |
| 1450 | error.MissingDebugInfo, error.InvalidDebugInfo => "???", | 1443 | error.MissingDebugInfo, |
| 1451 | else => return err, | 1444 | error.InvalidDebugInfo, |
| 1445 | => { | ||
| 1446 | return SymbolInfo{ .symbol_name = stab_symbol }; | ||
| 1452 | }, | 1447 | }, |
| 1453 | .line_info = o_file_di.getLineNumberInfo(compile_unit.*, relocated_address_o) catch |err| switch (err) { | 1448 | else => return err, |
| 1454 | error.MissingDebugInfo, error.InvalidDebugInfo => null, | 1449 | }); |
| 1455 | else => return err, | 1450 | |
| 1451 | // Translate again the address, this time into an address inside the | ||
| 1452 | // .o file | ||
| 1453 | const relocated_address_o = relocated_address - symbol.reloc; | ||
| 1454 | |||
| 1455 | if (o_file_di.findCompileUnit(relocated_address_o)) |compile_unit| { | ||
| 1456 | return SymbolInfo{ | ||
| 1457 | .symbol_name = o_file_di.getSymbolName(relocated_address_o) orelse "???", | ||
| 1458 | .compile_unit_name = compile_unit.die.getAttrString(&o_file_di, DW.AT_name) catch |err| switch (err) { | ||
| 1459 | error.MissingDebugInfo, error.InvalidDebugInfo => "???", | ||
| 1460 | else => return err, | ||
| 1461 | }, | ||
| 1462 | .line_info = o_file_di.getLineNumberInfo(compile_unit.*, relocated_address_o) catch |err| switch (err) { | ||
| 1463 | error.MissingDebugInfo, error.InvalidDebugInfo => null, | ||
| 1464 | else => return err, | ||
| 1465 | }, | ||
| 1466 | }; | ||
| 1467 | } else |err| switch (err) { | ||
| 1468 | error.MissingDebugInfo, error.InvalidDebugInfo => { | ||
| 1469 | return SymbolInfo{ .symbol_name = stab_symbol }; | ||
| 1456 | }, | 1470 | }, |
| 1457 | }; | 1471 | else => return err, |
| 1458 | } else |err| switch (err) { | 1472 | } |
| 1459 | error.MissingDebugInfo, error.InvalidDebugInfo => { | ||
| 1460 | return SymbolInfo{ .symbol_name = stab_symbol }; | ||
| 1461 | }, | ||
| 1462 | else => return err, | ||
| 1463 | } | ||
| 1464 | 1473 | ||
| 1465 | unreachable; | 1474 | unreachable; |
| 1475 | } | ||
| 1466 | } | 1476 | } |
| 1467 | }, | 1477 | }, |
| 1468 | .uefi, .windows => struct { | 1478 | .uefi, .windows => struct { |
lib/std/dynamic_library.zig+2-2| ... | @@ -328,14 +328,14 @@ pub const WindowsDynLib = struct { | ... | @@ -328,14 +328,14 @@ pub const WindowsDynLib = struct { |
| 328 | 328 | ||
| 329 | pub fn open(path: []const u8) !WindowsDynLib { | 329 | pub fn open(path: []const u8) !WindowsDynLib { |
| 330 | const path_w = try windows.sliceToPrefixedFileW(path); | 330 | const path_w = try windows.sliceToPrefixedFileW(path); |
| 331 | return openW(&path_w); | 331 | return openW(path_w.span().ptr); |
| 332 | } | 332 | } |
| 333 | 333 | ||
| 334 | pub const openC = @compileError("deprecated: renamed to openZ"); | 334 | pub const openC = @compileError("deprecated: renamed to openZ"); |
| 335 | 335 | ||
| 336 | pub fn openZ(path_c: [*:0]const u8) !WindowsDynLib { | 336 | pub fn openZ(path_c: [*:0]const u8) !WindowsDynLib { |
| 337 | const path_w = try windows.cStrToPrefixedFileW(path_c); | 337 | const path_w = try windows.cStrToPrefixedFileW(path_c); |
| 338 | return openW(&path_w); | 338 | return openW(path_w.span().ptr); |
| 339 | } | 339 | } |
| 340 | 340 | ||
| 341 | pub fn openW(path_w: [*:0]const u16) !WindowsDynLib { | 341 | pub fn openW(path_w: [*:0]const u16) !WindowsDynLib { |
lib/std/event/loop.zig+69-144| ... | @@ -4,19 +4,27 @@ const root = @import("root"); | ... | @@ -4,19 +4,27 @@ const root = @import("root"); |
| 4 | const assert = std.debug.assert; | 4 | const assert = std.debug.assert; |
| 5 | const testing = std.testing; | 5 | const testing = std.testing; |
| 6 | const mem = std.mem; | 6 | const mem = std.mem; |
| 7 | const AtomicRmwOp = builtin.AtomicRmwOp; | ||
| 8 | const AtomicOrder = builtin.AtomicOrder; | ||
| 9 | const os = std.os; | 7 | const os = std.os; |
| 10 | const windows = os.windows; | 8 | const windows = os.windows; |
| 11 | const maxInt = std.math.maxInt; | 9 | const maxInt = std.math.maxInt; |
| 12 | const Thread = std.Thread; | 10 | const Thread = std.Thread; |
| 13 | 11 | ||
| 12 | const is_windows = std.Target.current.os.tag == .windows; | ||
| 13 | |||
| 14 | pub const Loop = struct { | 14 | pub const Loop = struct { |
| 15 | next_tick_queue: std.atomic.Queue(anyframe), | 15 | next_tick_queue: std.atomic.Queue(anyframe), |
| 16 | os_data: OsData, | 16 | os_data: OsData, |
| 17 | final_resume_node: ResumeNode, | 17 | final_resume_node: ResumeNode, |
| 18 | pending_event_count: usize, | 18 | pending_event_count: usize, |
| 19 | extra_threads: []*Thread, | 19 | extra_threads: []*Thread, |
| 20 | /// TODO change this to a pool of configurable number of threads | ||
| 21 | /// and rename it to be not file-system-specific. it will become | ||
| 22 | /// a thread pool for turning non-CPU-bound blocking things into | ||
| 23 | /// async things. A fallback for any missing OS-specific API. | ||
| 24 | fs_thread: *Thread, | ||
| 25 | fs_queue: std.atomic.Queue(Request), | ||
| 26 | fs_end_request: Request.Node, | ||
| 27 | fs_thread_wakeup: std.ResetEvent, | ||
| 20 | 28 | ||
| 21 | /// For resources that have the same lifetime as the `Loop`. | 29 | /// For resources that have the same lifetime as the `Loop`. |
| 22 | /// This is only used by `Loop` for the thread pool and associated resources. | 30 | /// This is only used by `Loop` for the thread pool and associated resources. |
| ... | @@ -143,7 +151,12 @@ pub const Loop = struct { | ... | @@ -143,7 +151,12 @@ pub const Loop = struct { |
| 143 | .handle = undefined, | 151 | .handle = undefined, |
| 144 | .overlapped = ResumeNode.overlapped_init, | 152 | .overlapped = ResumeNode.overlapped_init, |
| 145 | }, | 153 | }, |
| 154 | .fs_end_request = .{ .data = .{ .msg = .end, .finish = .NoAction } }, | ||
| 155 | .fs_queue = std.atomic.Queue(Request).init(), | ||
| 156 | .fs_thread = undefined, | ||
| 157 | .fs_thread_wakeup = std.ResetEvent.init(), | ||
| 146 | }; | 158 | }; |
| 159 | errdefer self.fs_thread_wakeup.deinit(); | ||
| 147 | errdefer self.arena.deinit(); | 160 | errdefer self.arena.deinit(); |
| 148 | 161 | ||
| 149 | // We need at least one of these in case the fs thread wants to use onNextTick | 162 | // We need at least one of these in case the fs thread wants to use onNextTick |
| ... | @@ -158,10 +171,19 @@ pub const Loop = struct { | ... | @@ -158,10 +171,19 @@ pub const Loop = struct { |
| 158 | 171 | ||
| 159 | try self.initOsData(extra_thread_count); | 172 | try self.initOsData(extra_thread_count); |
| 160 | errdefer self.deinitOsData(); | 173 | errdefer self.deinitOsData(); |
| 174 | |||
| 175 | if (!builtin.single_threaded) { | ||
| 176 | self.fs_thread = try Thread.spawn(self, posixFsRun); | ||
| 177 | } | ||
| 178 | errdefer if (!builtin.single_threaded) { | ||
| 179 | self.posixFsRequest(&self.fs_end_request); | ||
| 180 | self.fs_thread.wait(); | ||
| 181 | }; | ||
| 161 | } | 182 | } |
| 162 | 183 | ||
| 163 | pub fn deinit(self: *Loop) void { | 184 | pub fn deinit(self: *Loop) void { |
| 164 | self.deinitOsData(); | 185 | self.deinitOsData(); |
| 186 | self.fs_thread_wakeup.deinit(); | ||
| 165 | self.arena.deinit(); | 187 | self.arena.deinit(); |
| 166 | self.* = undefined; | 188 | self.* = undefined; |
| 167 | } | 189 | } |
| ... | @@ -173,21 +195,10 @@ pub const Loop = struct { | ... | @@ -173,21 +195,10 @@ pub const Loop = struct { |
| 173 | const wakeup_bytes = [_]u8{0x1} ** 8; | 195 | const wakeup_bytes = [_]u8{0x1} ** 8; |
| 174 | 196 | ||
| 175 | fn initOsData(self: *Loop, extra_thread_count: usize) InitOsDataError!void { | 197 | fn initOsData(self: *Loop, extra_thread_count: usize) InitOsDataError!void { |
| 176 | switch (builtin.os.tag) { | 198 | noasync switch (builtin.os.tag) { |
| 177 | .linux => { | 199 | .linux => { |
| 178 | self.os_data.fs_queue = std.atomic.Queue(Request).init(); | ||
| 179 | self.os_data.fs_queue_item = 0; | ||
| 180 | // we need another thread for the file system because Linux does not have an async | ||
| 181 | // file system I/O API. | ||
| 182 | self.os_data.fs_end_request = Request.Node{ | ||
| 183 | .data = Request{ | ||
| 184 | .msg = .end, | ||
| 185 | .finish = .NoAction, | ||
| 186 | }, | ||
| 187 | }; | ||
| 188 | |||
| 189 | errdefer { | 200 | errdefer { |
| 190 | while (self.available_eventfd_resume_nodes.pop()) |node| noasync os.close(node.data.eventfd); | 201 | while (self.available_eventfd_resume_nodes.pop()) |node| os.close(node.data.eventfd); |
| 191 | } | 202 | } |
| 192 | for (self.eventfd_resume_nodes) |*eventfd_node| { | 203 | for (self.eventfd_resume_nodes) |*eventfd_node| { |
| 193 | eventfd_node.* = std.atomic.Stack(ResumeNode.EventFd).Node{ | 204 | eventfd_node.* = std.atomic.Stack(ResumeNode.EventFd).Node{ |
| ... | @@ -206,10 +217,10 @@ pub const Loop = struct { | ... | @@ -206,10 +217,10 @@ pub const Loop = struct { |
| 206 | } | 217 | } |
| 207 | 218 | ||
| 208 | self.os_data.epollfd = try os.epoll_create1(os.EPOLL_CLOEXEC); | 219 | self.os_data.epollfd = try os.epoll_create1(os.EPOLL_CLOEXEC); |
| 209 | errdefer noasync os.close(self.os_data.epollfd); | 220 | errdefer os.close(self.os_data.epollfd); |
| 210 | 221 | ||
| 211 | self.os_data.final_eventfd = try os.eventfd(0, os.EFD_CLOEXEC | os.EFD_NONBLOCK); | 222 | self.os_data.final_eventfd = try os.eventfd(0, os.EFD_CLOEXEC | os.EFD_NONBLOCK); |
| 212 | errdefer noasync os.close(self.os_data.final_eventfd); | 223 | errdefer os.close(self.os_data.final_eventfd); |
| 213 | 224 | ||
| 214 | self.os_data.final_eventfd_event = os.epoll_event{ | 225 | self.os_data.final_eventfd_event = os.epoll_event{ |
| 215 | .events = os.EPOLLIN, | 226 | .events = os.EPOLLIN, |
| ... | @@ -222,12 +233,6 @@ pub const Loop = struct { | ... | @@ -222,12 +233,6 @@ pub const Loop = struct { |
| 222 | &self.os_data.final_eventfd_event, | 233 | &self.os_data.final_eventfd_event, |
| 223 | ); | 234 | ); |
| 224 | 235 | ||
| 225 | self.os_data.fs_thread = try Thread.spawn(self, posixFsRun); | ||
| 226 | errdefer { | ||
| 227 | self.posixFsRequest(&self.os_data.fs_end_request); | ||
| 228 | self.os_data.fs_thread.wait(); | ||
| 229 | } | ||
| 230 | |||
| 231 | if (builtin.single_threaded) { | 236 | if (builtin.single_threaded) { |
| 232 | assert(extra_thread_count == 0); | 237 | assert(extra_thread_count == 0); |
| 233 | return; | 238 | return; |
| ... | @@ -236,7 +241,7 @@ pub const Loop = struct { | ... | @@ -236,7 +241,7 @@ pub const Loop = struct { |
| 236 | var extra_thread_index: usize = 0; | 241 | var extra_thread_index: usize = 0; |
| 237 | errdefer { | 242 | errdefer { |
| 238 | // writing 8 bytes to an eventfd cannot fail | 243 | // writing 8 bytes to an eventfd cannot fail |
| 239 | const amt = noasync os.write(self.os_data.final_eventfd, &wakeup_bytes) catch unreachable; | 244 | const amt = os.write(self.os_data.final_eventfd, &wakeup_bytes) catch unreachable; |
| 240 | assert(amt == wakeup_bytes.len); | 245 | assert(amt == wakeup_bytes.len); |
| 241 | while (extra_thread_index != 0) { | 246 | while (extra_thread_index != 0) { |
| 242 | extra_thread_index -= 1; | 247 | extra_thread_index -= 1; |
| ... | @@ -249,22 +254,7 @@ pub const Loop = struct { | ... | @@ -249,22 +254,7 @@ pub const Loop = struct { |
| 249 | }, | 254 | }, |
| 250 | .macosx, .freebsd, .netbsd, .dragonfly => { | 255 | .macosx, .freebsd, .netbsd, .dragonfly => { |
| 251 | self.os_data.kqfd = try os.kqueue(); | 256 | self.os_data.kqfd = try os.kqueue(); |
| 252 | errdefer noasync os.close(self.os_data.kqfd); | 257 | errdefer os.close(self.os_data.kqfd); |
| 253 | |||
| 254 | self.os_data.fs_kqfd = try os.kqueue(); | ||
| 255 | errdefer noasync os.close(self.os_data.fs_kqfd); | ||
| 256 | |||
| 257 | self.os_data.fs_queue = std.atomic.Queue(Request).init(); | ||
| 258 | // we need another thread for the file system because Darwin does not have an async | ||
| 259 | // file system I/O API. | ||
| 260 | self.os_data.fs_end_request = Request.Node{ | ||
| 261 | .prev = undefined, | ||
| 262 | .next = undefined, | ||
| 263 | .data = Request{ | ||
| 264 | .msg = .end, | ||
| 265 | .finish = .NoAction, | ||
| 266 | }, | ||
| 267 | }; | ||
| 268 | 258 | ||
| 269 | const empty_kevs = &[0]os.Kevent{}; | 259 | const empty_kevs = &[0]os.Kevent{}; |
| 270 | 260 | ||
| ... | @@ -310,30 +300,6 @@ pub const Loop = struct { | ... | @@ -310,30 +300,6 @@ pub const Loop = struct { |
| 310 | self.os_data.final_kevent.flags = os.EV_ENABLE; | 300 | self.os_data.final_kevent.flags = os.EV_ENABLE; |
| 311 | self.os_data.final_kevent.fflags = os.NOTE_TRIGGER; | 301 | self.os_data.final_kevent.fflags = os.NOTE_TRIGGER; |
| 312 | 302 | ||
| 313 | self.os_data.fs_kevent_wake = os.Kevent{ | ||
| 314 | .ident = 0, | ||
| 315 | .filter = os.EVFILT_USER, | ||
| 316 | .flags = os.EV_ADD | os.EV_ENABLE, | ||
| 317 | .fflags = os.NOTE_TRIGGER, | ||
| 318 | .data = 0, | ||
| 319 | .udata = undefined, | ||
| 320 | }; | ||
| 321 | |||
| 322 | self.os_data.fs_kevent_wait = os.Kevent{ | ||
| 323 | .ident = 0, | ||
| 324 | .filter = os.EVFILT_USER, | ||
| 325 | .flags = os.EV_ADD | os.EV_CLEAR, | ||
| 326 | .fflags = 0, | ||
| 327 | .data = 0, | ||
| 328 | .udata = undefined, | ||
| 329 | }; | ||
| 330 | |||
| 331 | self.os_data.fs_thread = try Thread.spawn(self, posixFsRun); | ||
| 332 | errdefer { | ||
| 333 | self.posixFsRequest(&self.os_data.fs_end_request); | ||
| 334 | self.os_data.fs_thread.wait(); | ||
| 335 | } | ||
| 336 | |||
| 337 | if (builtin.single_threaded) { | 303 | if (builtin.single_threaded) { |
| 338 | assert(extra_thread_count == 0); | 304 | assert(extra_thread_count == 0); |
| 339 | return; | 305 | return; |
| ... | @@ -401,25 +367,24 @@ pub const Loop = struct { | ... | @@ -401,25 +367,24 @@ pub const Loop = struct { |
| 401 | } | 367 | } |
| 402 | }, | 368 | }, |
| 403 | else => {}, | 369 | else => {}, |
| 404 | } | 370 | }; |
| 405 | } | 371 | } |
| 406 | 372 | ||
| 407 | fn deinitOsData(self: *Loop) void { | 373 | fn deinitOsData(self: *Loop) void { |
| 408 | switch (builtin.os.tag) { | 374 | noasync switch (builtin.os.tag) { |
| 409 | .linux => { | 375 | .linux => { |
| 410 | noasync os.close(self.os_data.final_eventfd); | 376 | os.close(self.os_data.final_eventfd); |
| 411 | while (self.available_eventfd_resume_nodes.pop()) |node| noasync os.close(node.data.eventfd); | 377 | while (self.available_eventfd_resume_nodes.pop()) |node| os.close(node.data.eventfd); |
| 412 | noasync os.close(self.os_data.epollfd); | 378 | os.close(self.os_data.epollfd); |
| 413 | }, | 379 | }, |
| 414 | .macosx, .freebsd, .netbsd, .dragonfly => { | 380 | .macosx, .freebsd, .netbsd, .dragonfly => { |
| 415 | noasync os.close(self.os_data.kqfd); | 381 | os.close(self.os_data.kqfd); |
| 416 | noasync os.close(self.os_data.fs_kqfd); | ||
| 417 | }, | 382 | }, |
| 418 | .windows => { | 383 | .windows => { |
| 419 | windows.CloseHandle(self.os_data.io_port); | 384 | windows.CloseHandle(self.os_data.io_port); |
| 420 | }, | 385 | }, |
| 421 | else => {}, | 386 | else => {}, |
| 422 | } | 387 | }; |
| 423 | } | 388 | } |
| 424 | 389 | ||
| 425 | /// resume_node must live longer than the anyframe that it holds a reference to. | 390 | /// resume_node must live longer than the anyframe that it holds a reference to. |
| ... | @@ -657,7 +622,7 @@ pub const Loop = struct { | ... | @@ -657,7 +622,7 @@ pub const Loop = struct { |
| 657 | .freebsd, | 622 | .freebsd, |
| 658 | .netbsd, | 623 | .netbsd, |
| 659 | .dragonfly, | 624 | .dragonfly, |
| 660 | => self.os_data.fs_thread.wait(), | 625 | => self.fs_thread.wait(), |
| 661 | else => {}, | 626 | else => {}, |
| 662 | } | 627 | } |
| 663 | 628 | ||
| ... | @@ -694,23 +659,25 @@ pub const Loop = struct { | ... | @@ -694,23 +659,25 @@ pub const Loop = struct { |
| 694 | 659 | ||
| 695 | /// call finishOneEvent when done | 660 | /// call finishOneEvent when done |
| 696 | pub fn beginOneEvent(self: *Loop) void { | 661 | pub fn beginOneEvent(self: *Loop) void { |
| 697 | _ = @atomicRmw(usize, &self.pending_event_count, AtomicRmwOp.Add, 1, AtomicOrder.SeqCst); | 662 | _ = @atomicRmw(usize, &self.pending_event_count, .Add, 1, .SeqCst); |
| 698 | } | 663 | } |
| 699 | 664 | ||
| 700 | pub fn finishOneEvent(self: *Loop) void { | 665 | pub fn finishOneEvent(self: *Loop) void { |
| 701 | const prev = @atomicRmw(usize, &self.pending_event_count, AtomicRmwOp.Sub, 1, AtomicOrder.SeqCst); | 666 | noasync { |
| 702 | if (prev == 1) { | 667 | const prev = @atomicRmw(usize, &self.pending_event_count, .Sub, 1, .SeqCst); |
| 668 | if (prev != 1) return; | ||
| 669 | |||
| 703 | // cause all the threads to stop | 670 | // cause all the threads to stop |
| 671 | self.posixFsRequest(&self.fs_end_request); | ||
| 672 | |||
| 704 | switch (builtin.os.tag) { | 673 | switch (builtin.os.tag) { |
| 705 | .linux => { | 674 | .linux => { |
| 706 | self.posixFsRequest(&self.os_data.fs_end_request); | ||
| 707 | // writing 8 bytes to an eventfd cannot fail | 675 | // writing 8 bytes to an eventfd cannot fail |
| 708 | const amt = noasync os.write(self.os_data.final_eventfd, &wakeup_bytes) catch unreachable; | 676 | const amt = os.write(self.os_data.final_eventfd, &wakeup_bytes) catch unreachable; |
| 709 | assert(amt == wakeup_bytes.len); | 677 | assert(amt == wakeup_bytes.len); |
| 710 | return; | 678 | return; |
| 711 | }, | 679 | }, |
| 712 | .macosx, .freebsd, .netbsd, .dragonfly => { | 680 | .macosx, .freebsd, .netbsd, .dragonfly => { |
| 713 | self.posixFsRequest(&self.os_data.fs_end_request); | ||
| 714 | const final_kevent = @as(*const [1]os.Kevent, &self.os_data.final_kevent); | 681 | const final_kevent = @as(*const [1]os.Kevent, &self.os_data.final_kevent); |
| 715 | const empty_kevs = &[0]os.Kevent{}; | 682 | const empty_kevs = &[0]os.Kevent{}; |
| 716 | // cannot fail because we already added it and this just enables it | 683 | // cannot fail because we already added it and this just enables it |
| ... | @@ -1063,73 +1030,55 @@ pub const Loop = struct { | ... | @@ -1063,73 +1030,55 @@ pub const Loop = struct { |
| 1063 | 1030 | ||
| 1064 | fn posixFsRequest(self: *Loop, request_node: *Request.Node) void { | 1031 | fn posixFsRequest(self: *Loop, request_node: *Request.Node) void { |
| 1065 | self.beginOneEvent(); // finished in posixFsRun after processing the msg | 1032 | self.beginOneEvent(); // finished in posixFsRun after processing the msg |
| 1066 | self.os_data.fs_queue.put(request_node); | 1033 | self.fs_queue.put(request_node); |
| 1067 | switch (builtin.os.tag) { | 1034 | self.fs_thread_wakeup.set(); |
| 1068 | .macosx, .freebsd, .netbsd, .dragonfly => { | ||
| 1069 | const fs_kevs = @as(*const [1]os.Kevent, &self.os_data.fs_kevent_wake); | ||
| 1070 | const empty_kevs = &[0]os.Kevent{}; | ||
| 1071 | _ = os.kevent(self.os_data.fs_kqfd, fs_kevs, empty_kevs, null) catch unreachable; | ||
| 1072 | }, | ||
| 1073 | .linux => { | ||
| 1074 | @atomicStore(i32, &self.os_data.fs_queue_item, 1, AtomicOrder.SeqCst); | ||
| 1075 | const rc = os.linux.futex_wake(&self.os_data.fs_queue_item, os.linux.FUTEX_WAKE, 1); | ||
| 1076 | switch (os.linux.getErrno(rc)) { | ||
| 1077 | 0 => {}, | ||
| 1078 | os.EINVAL => unreachable, | ||
| 1079 | else => unreachable, | ||
| 1080 | } | ||
| 1081 | }, | ||
| 1082 | else => @compileError("Unsupported OS"), | ||
| 1083 | } | ||
| 1084 | } | 1035 | } |
| 1085 | 1036 | ||
| 1086 | fn posixFsCancel(self: *Loop, request_node: *Request.Node) void { | 1037 | fn posixFsCancel(self: *Loop, request_node: *Request.Node) void { |
| 1087 | if (self.os_data.fs_queue.remove(request_node)) { | 1038 | if (self.fs_queue.remove(request_node)) { |
| 1088 | self.finishOneEvent(); | 1039 | self.finishOneEvent(); |
| 1089 | } | 1040 | } |
| 1090 | } | 1041 | } |
| 1091 | 1042 | ||
| 1092 | // TODO make this whole function noasync | ||
| 1093 | // https://github.com/ziglang/zig/issues/3157 | ||
| 1094 | fn posixFsRun(self: *Loop) void { | 1043 | fn posixFsRun(self: *Loop) void { |
| 1095 | while (true) { | 1044 | noasync while (true) { |
| 1096 | if (builtin.os.tag == .linux) { | 1045 | self.fs_thread_wakeup.reset(); |
| 1097 | @atomicStore(i32, &self.os_data.fs_queue_item, 0, .SeqCst); | 1046 | while (self.fs_queue.get()) |node| { |
| 1098 | } | ||
| 1099 | while (self.os_data.fs_queue.get()) |node| { | ||
| 1100 | switch (node.data.msg) { | 1047 | switch (node.data.msg) { |
| 1101 | .end => return, | 1048 | .end => return, |
| 1102 | .read => |*msg| { | 1049 | .read => |*msg| { |
| 1103 | msg.result = noasync os.read(msg.fd, msg.buf); | 1050 | msg.result = os.read(msg.fd, msg.buf); |
| 1104 | }, | 1051 | }, |
| 1105 | .readv => |*msg| { | 1052 | .readv => |*msg| { |
| 1106 | msg.result = noasync os.readv(msg.fd, msg.iov); | 1053 | msg.result = os.readv(msg.fd, msg.iov); |
| 1107 | }, | 1054 | }, |
| 1108 | .write => |*msg| { | 1055 | .write => |*msg| { |
| 1109 | msg.result = noasync os.write(msg.fd, msg.bytes); | 1056 | msg.result = os.write(msg.fd, msg.bytes); |
| 1110 | }, | 1057 | }, |
| 1111 | .writev => |*msg| { | 1058 | .writev => |*msg| { |
| 1112 | msg.result = noasync os.writev(msg.fd, msg.iov); | 1059 | msg.result = os.writev(msg.fd, msg.iov); |
| 1113 | }, | 1060 | }, |
| 1114 | .pwritev => |*msg| { | 1061 | .pwritev => |*msg| { |
| 1115 | msg.result = noasync os.pwritev(msg.fd, msg.iov, msg.offset); | 1062 | msg.result = os.pwritev(msg.fd, msg.iov, msg.offset); |
| 1116 | }, | 1063 | }, |
| 1117 | .pread => |*msg| { | 1064 | .pread => |*msg| { |
| 1118 | msg.result = noasync os.pread(msg.fd, msg.buf, msg.offset); | 1065 | msg.result = os.pread(msg.fd, msg.buf, msg.offset); |
| 1119 | }, | 1066 | }, |
| 1120 | .preadv => |*msg| { | 1067 | .preadv => |*msg| { |
| 1121 | msg.result = noasync os.preadv(msg.fd, msg.iov, msg.offset); | 1068 | msg.result = os.preadv(msg.fd, msg.iov, msg.offset); |
| 1122 | }, | 1069 | }, |
| 1123 | .open => |*msg| { | 1070 | .open => |*msg| { |
| 1124 | msg.result = noasync os.openZ(msg.path, msg.flags, msg.mode); | 1071 | if (is_windows) unreachable; // TODO |
| 1072 | msg.result = os.openZ(msg.path, msg.flags, msg.mode); | ||
| 1125 | }, | 1073 | }, |
| 1126 | .openat => |*msg| { | 1074 | .openat => |*msg| { |
| 1127 | msg.result = noasync os.openatZ(msg.fd, msg.path, msg.flags, msg.mode); | 1075 | if (is_windows) unreachable; // TODO |
| 1076 | msg.result = os.openatZ(msg.fd, msg.path, msg.flags, msg.mode); | ||
| 1128 | }, | 1077 | }, |
| 1129 | .faccessat => |*msg| { | 1078 | .faccessat => |*msg| { |
| 1130 | msg.result = noasync os.faccessatZ(msg.dirfd, msg.path, msg.mode, msg.flags); | 1079 | msg.result = os.faccessatZ(msg.dirfd, msg.path, msg.mode, msg.flags); |
| 1131 | }, | 1080 | }, |
| 1132 | .close => |*msg| noasync os.close(msg.fd), | 1081 | .close => |*msg| os.close(msg.fd), |
| 1133 | } | 1082 | } |
| 1134 | switch (node.data.finish) { | 1083 | switch (node.data.finish) { |
| 1135 | .TickNode => |*tick_node| self.onNextTick(tick_node), | 1084 | .TickNode => |*tick_node| self.onNextTick(tick_node), |
| ... | @@ -1137,22 +1086,8 @@ pub const Loop = struct { | ... | @@ -1137,22 +1086,8 @@ pub const Loop = struct { |
| 1137 | } | 1086 | } |
| 1138 | self.finishOneEvent(); | 1087 | self.finishOneEvent(); |
| 1139 | } | 1088 | } |
| 1140 | switch (builtin.os.tag) { | 1089 | self.fs_thread_wakeup.wait(); |
| 1141 | .linux => { | 1090 | }; |
| 1142 | const rc = os.linux.futex_wait(&self.os_data.fs_queue_item, os.linux.FUTEX_WAIT, 0, null); | ||
| 1143 | switch (os.linux.getErrno(rc)) { | ||
| 1144 | 0, os.EINTR, os.EAGAIN => continue, | ||
| 1145 | else => unreachable, | ||
| 1146 | } | ||
| 1147 | }, | ||
| 1148 | .macosx, .freebsd, .netbsd, .dragonfly => { | ||
| 1149 | const fs_kevs = @as(*const [1]os.Kevent, &self.os_data.fs_kevent_wait); | ||
| 1150 | var out_kevs: [1]os.Kevent = undefined; | ||
| 1151 | _ = os.kevent(self.os_data.fs_kqfd, fs_kevs, out_kevs[0..], null) catch unreachable; | ||
| 1152 | }, | ||
| 1153 | else => @compileError("Unsupported OS"), | ||
| 1154 | } | ||
| 1155 | } | ||
| 1156 | } | 1091 | } |
| 1157 | 1092 | ||
| 1158 | const OsData = switch (builtin.os.tag) { | 1093 | const OsData = switch (builtin.os.tag) { |
| ... | @@ -1168,22 +1103,12 @@ pub const Loop = struct { | ... | @@ -1168,22 +1103,12 @@ pub const Loop = struct { |
| 1168 | const KEventData = struct { | 1103 | const KEventData = struct { |
| 1169 | kqfd: i32, | 1104 | kqfd: i32, |
| 1170 | final_kevent: os.Kevent, | 1105 | final_kevent: os.Kevent, |
| 1171 | fs_kevent_wake: os.Kevent, | ||
| 1172 | fs_kevent_wait: os.Kevent, | ||
| 1173 | fs_thread: *Thread, | ||
| 1174 | fs_kqfd: i32, | ||
| 1175 | fs_queue: std.atomic.Queue(Request), | ||
| 1176 | fs_end_request: Request.Node, | ||
| 1177 | }; | 1106 | }; |
| 1178 | 1107 | ||
| 1179 | const LinuxOsData = struct { | 1108 | const LinuxOsData = struct { |
| 1180 | epollfd: i32, | 1109 | epollfd: i32, |
| 1181 | final_eventfd: i32, | 1110 | final_eventfd: i32, |
| 1182 | final_eventfd_event: os.linux.epoll_event, | 1111 | final_eventfd_event: os.linux.epoll_event, |
| 1183 | fs_thread: *Thread, | ||
| 1184 | fs_queue_item: i32, | ||
| 1185 | fs_queue: std.atomic.Queue(Request), | ||
| 1186 | fs_end_request: Request.Node, | ||
| 1187 | }; | 1112 | }; |
| 1188 | 1113 | ||
| 1189 | pub const Request = struct { | 1114 | pub const Request = struct { |
| ... | @@ -1324,11 +1249,11 @@ test "std.event.Loop - basic" { | ... | @@ -1324,11 +1249,11 @@ test "std.event.Loop - basic" { |
| 1324 | loop.run(); | 1249 | loop.run(); |
| 1325 | } | 1250 | } |
| 1326 | 1251 | ||
| 1327 | async fn testEventLoop() i32 { | 1252 | fn testEventLoop() i32 { |
| 1328 | return 1234; | 1253 | return 1234; |
| 1329 | } | 1254 | } |
| 1330 | 1255 | ||
| 1331 | async fn testEventLoop2(h: anyframe->i32, did_it: *bool) void { | 1256 | fn testEventLoop2(h: anyframe->i32, did_it: *bool) void { |
| 1332 | const value = await h; | 1257 | const value = await h; |
| 1333 | testing.expect(value == 1234); | 1258 | testing.expect(value == 1234); |
| 1334 | did_it.* = true; | 1259 | did_it.* = true; |
lib/std/fs.zig+78-71| ... | @@ -8,6 +8,8 @@ const Allocator = std.mem.Allocator; | ... | @@ -8,6 +8,8 @@ const Allocator = std.mem.Allocator; |
| 8 | const assert = std.debug.assert; | 8 | const assert = std.debug.assert; |
| 9 | const math = std.math; | 9 | const math = std.math; |
| 10 | 10 | ||
| 11 | const is_darwin = std.Target.current.os.tag.isDarwin(); | ||
| 12 | |||
| 11 | pub const path = @import("fs/path.zig"); | 13 | pub const path = @import("fs/path.zig"); |
| 12 | pub const File = @import("fs/file.zig").File; | 14 | pub const File = @import("fs/file.zig").File; |
| 13 | 15 | ||
| ... | @@ -197,7 +199,7 @@ pub const AtomicFile = struct { | ... | @@ -197,7 +199,7 @@ pub const AtomicFile = struct { |
| 197 | if (std.Target.current.os.tag == .windows) { | 199 | if (std.Target.current.os.tag == .windows) { |
| 198 | const dest_path_w = try os.windows.sliceToPrefixedFileW(self.dest_basename); | 200 | const dest_path_w = try os.windows.sliceToPrefixedFileW(self.dest_basename); |
| 199 | const tmp_path_w = try os.windows.cStrToPrefixedFileW(&self.tmp_path_buf); | 201 | const tmp_path_w = try os.windows.cStrToPrefixedFileW(&self.tmp_path_buf); |
| 200 | try os.renameatW(self.dir.fd, &tmp_path_w, self.dir.fd, &dest_path_w, os.windows.TRUE); | 202 | try os.renameatW(self.dir.fd, tmp_path_w.span(), self.dir.fd, dest_path_w.span(), os.windows.TRUE); |
| 201 | self.file_exists = false; | 203 | self.file_exists = false; |
| 202 | } else { | 204 | } else { |
| 203 | const dest_path_c = try os.toPosixPath(self.dest_basename); | 205 | const dest_path_c = try os.toPosixPath(self.dest_basename); |
| ... | @@ -580,7 +582,7 @@ pub const Dir = struct { | ... | @@ -580,7 +582,7 @@ pub const Dir = struct { |
| 580 | pub fn openFile(self: Dir, sub_path: []const u8, flags: File.OpenFlags) File.OpenError!File { | 582 | pub fn openFile(self: Dir, sub_path: []const u8, flags: File.OpenFlags) File.OpenError!File { |
| 581 | if (builtin.os.tag == .windows) { | 583 | if (builtin.os.tag == .windows) { |
| 582 | const path_w = try os.windows.sliceToPrefixedFileW(sub_path); | 584 | const path_w = try os.windows.sliceToPrefixedFileW(sub_path); |
| 583 | return self.openFileW(&path_w, flags); | 585 | return self.openFileW(path_w.span(), flags); |
| 584 | } | 586 | } |
| 585 | const path_c = try os.toPosixPath(sub_path); | 587 | const path_c = try os.toPosixPath(sub_path); |
| 586 | return self.openFileZ(&path_c, flags); | 588 | return self.openFileZ(&path_c, flags); |
| ... | @@ -592,13 +594,16 @@ pub const Dir = struct { | ... | @@ -592,13 +594,16 @@ pub const Dir = struct { |
| 592 | pub fn openFileZ(self: Dir, sub_path: [*:0]const u8, flags: File.OpenFlags) File.OpenError!File { | 594 | pub fn openFileZ(self: Dir, sub_path: [*:0]const u8, flags: File.OpenFlags) File.OpenError!File { |
| 593 | if (builtin.os.tag == .windows) { | 595 | if (builtin.os.tag == .windows) { |
| 594 | const path_w = try os.windows.cStrToPrefixedFileW(sub_path); | 596 | const path_w = try os.windows.cStrToPrefixedFileW(sub_path); |
| 595 | return self.openFileW(&path_w, flags); | 597 | return self.openFileW(path_w.span(), flags); |
| 596 | } | 598 | } |
| 597 | 599 | ||
| 598 | // Use the O_ locking flags if the os supports them | 600 | // 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, |
| ... | @@ -606,14 +611,13 @@ pub const Dir = struct { | ... | @@ -606,14 +611,13 @@ pub const Dir = struct { |
| 606 | } else 0; | 611 | } else 0; |
| 607 | 612 | ||
| 608 | const O_LARGEFILE = if (@hasDecl(os, "O_LARGEFILE")) os.O_LARGEFILE else 0; | 613 | const O_LARGEFILE = if (@hasDecl(os, "O_LARGEFILE")) os.O_LARGEFILE else 0; |
| 609 | const O_CLOEXEC: u32 = if (flags.share_with_child_process) 0 else os.O_CLOEXEC; | 614 | const os_flags = lock_flag | O_LARGEFILE | os.O_CLOEXEC | if (flags.write and flags.read) |
| 610 | const os_flags = lock_flag | O_LARGEFILE | O_CLOEXEC | if (flags.write and flags.read) | ||
| 611 | @as(u32, os.O_RDWR) | 615 | @as(u32, os.O_RDWR) |
| 612 | else if (flags.write) | 616 | else if (flags.write) |
| 613 | @as(u32, os.O_WRONLY) | 617 | @as(u32, os.O_WRONLY) |
| 614 | else | 618 | else |
| 615 | @as(u32, os.O_RDONLY); | 619 | @as(u32, os.O_RDONLY); |
| 616 | const fd = if (need_async_thread and !flags.always_blocking) | 620 | const fd = if (flags.intended_io_mode != .blocking) |
| 617 | 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) |
| 618 | else | 622 | else |
| 619 | try os.openatZ(self.fd, sub_path, os_flags, 0); | 623 | try os.openatZ(self.fd, sub_path, os_flags, 0); |
| ... | @@ -630,31 +634,32 @@ pub const Dir = struct { | ... | @@ -630,31 +634,32 @@ pub const Dir = struct { |
| 630 | 634 | ||
| 631 | return File{ | 635 | return File{ |
| 632 | .handle = fd, | 636 | .handle = fd, |
| 633 | .io_mode = .blocking, | 637 | .capable_io_mode = .blocking, |
| 634 | .async_block_allowed = if (flags.always_blocking) | 638 | .intended_io_mode = flags.intended_io_mode, |
| 635 | File.async_block_allowed_yes | ||
| 636 | else | ||
| 637 | File.async_block_allowed_no, | ||
| 638 | }; | 639 | }; |
| 639 | } | 640 | } |
| 640 | 641 | ||
| 641 | /// Same as `openFile` but Windows-only and the path parameter is | 642 | /// Same as `openFile` but Windows-only and the path parameter is |
| 642 | /// [WTF-16](https://simonsapin.github.io/wtf-8/#potentially-ill-formed-utf-16) encoded. | 643 | /// [WTF-16](https://simonsapin.github.io/wtf-8/#potentially-ill-formed-utf-16) encoded. |
| 643 | pub fn openFileW(self: Dir, sub_path_w: [*:0]const u16, flags: File.OpenFlags) File.OpenError!File { | 644 | pub fn openFileW(self: Dir, sub_path_w: []const u16, flags: File.OpenFlags) File.OpenError!File { |
| 644 | const w = os.windows; | 645 | const w = os.windows; |
| 645 | const access_mask = w.SYNCHRONIZE | | ||
| 646 | (if (flags.read) @as(u32, w.GENERIC_READ) else 0) | | ||
| 647 | (if (flags.write) @as(u32, w.GENERIC_WRITE) else 0); | ||
| 648 | |||
| 649 | const share_access = switch (flags.lock) { | ||
| 650 | .None => @as(?w.ULONG, null), | ||
| 651 | .Shared => w.FILE_SHARE_READ | w.FILE_SHARE_DELETE, | ||
| 652 | .Exclusive => w.FILE_SHARE_DELETE, | ||
| 653 | }; | ||
| 654 | |||
| 655 | return @as(File, .{ | 646 | return @as(File, .{ |
| 656 | .handle = try os.windows.OpenFileW(self.fd, sub_path_w, null, access_mask, share_access, flags.lock_nonblocking, w.FILE_OPEN), | 647 | .handle = try os.windows.OpenFile(sub_path_w, .{ |
| 657 | .io_mode = .blocking, | 648 | .dir = self.fd, |
| 649 | .access_mask = w.SYNCHRONIZE | | ||
| 650 | (if (flags.read) @as(u32, w.GENERIC_READ) else 0) | | ||
| 651 | (if (flags.write) @as(u32, w.GENERIC_WRITE) else 0), | ||
| 652 | .share_access = switch (flags.lock) { | ||
| 653 | .None => w.FILE_SHARE_WRITE | w.FILE_SHARE_READ | w.FILE_SHARE_DELETE, | ||
| 654 | .Shared => w.FILE_SHARE_READ | w.FILE_SHARE_DELETE, | ||
| 655 | .Exclusive => w.FILE_SHARE_DELETE, | ||
| 656 | }, | ||
| 657 | .share_access_nonblocking = flags.lock_nonblocking, | ||
| 658 | .creation = w.FILE_OPEN, | ||
| 659 | .io_mode = flags.intended_io_mode, | ||
| 660 | }), | ||
| 661 | .capable_io_mode = std.io.default_mode, | ||
| 662 | .intended_io_mode = flags.intended_io_mode, | ||
| 658 | }); | 663 | }); |
| 659 | } | 664 | } |
| 660 | 665 | ||
| ... | @@ -664,7 +669,7 @@ pub const Dir = struct { | ... | @@ -664,7 +669,7 @@ pub const Dir = struct { |
| 664 | pub fn createFile(self: Dir, sub_path: []const u8, flags: File.CreateFlags) File.OpenError!File { | 669 | pub fn createFile(self: Dir, sub_path: []const u8, flags: File.CreateFlags) File.OpenError!File { |
| 665 | if (builtin.os.tag == .windows) { | 670 | if (builtin.os.tag == .windows) { |
| 666 | const path_w = try os.windows.sliceToPrefixedFileW(sub_path); | 671 | const path_w = try os.windows.sliceToPrefixedFileW(sub_path); |
| 667 | return self.createFileW(&path_w, flags); | 672 | return self.createFileW(path_w.span(), flags); |
| 668 | } | 673 | } |
| 669 | const path_c = try os.toPosixPath(sub_path); | 674 | const path_c = try os.toPosixPath(sub_path); |
| 670 | return self.createFileZ(&path_c, flags); | 675 | return self.createFileZ(&path_c, flags); |
| ... | @@ -676,13 +681,16 @@ pub const Dir = struct { | ... | @@ -676,13 +681,16 @@ pub const Dir = struct { |
| 676 | pub fn createFileZ(self: Dir, sub_path_c: [*:0]const u8, flags: File.CreateFlags) File.OpenError!File { | 681 | pub fn createFileZ(self: Dir, sub_path_c: [*:0]const u8, flags: File.CreateFlags) File.OpenError!File { |
| 677 | if (builtin.os.tag == .windows) { | 682 | if (builtin.os.tag == .windows) { |
| 678 | const path_w = try os.windows.cStrToPrefixedFileW(sub_path_c); | 683 | const path_w = try os.windows.cStrToPrefixedFileW(sub_path_c); |
| 679 | return self.createFileW(&path_w, flags); | 684 | return self.createFileW(path_w.span(), flags); |
| 680 | } | 685 | } |
| 681 | 686 | ||
| 682 | // Use the O_ locking flags if the os supports them | 687 | // Use the O_ locking flags if the os supports them |
| 683 | // (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) |
| 684 | 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; |
| 685 | 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; | ||
| 686 | 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) { |
| 687 | .None => @as(u32, 0), | 695 | .None => @as(u32, 0), |
| 688 | .Shared => os.O_SHLOCK, | 696 | .Shared => os.O_SHLOCK, |
| ... | @@ -690,12 +698,11 @@ pub const Dir = struct { | ... | @@ -690,12 +698,11 @@ pub const Dir = struct { |
| 690 | } else 0; | 698 | } else 0; |
| 691 | 699 | ||
| 692 | const O_LARGEFILE = if (@hasDecl(os, "O_LARGEFILE")) os.O_LARGEFILE else 0; | 700 | const O_LARGEFILE = if (@hasDecl(os, "O_LARGEFILE")) os.O_LARGEFILE else 0; |
| 693 | const O_CLOEXEC: u32 = if (flags.share_with_child_process) 0 else os.O_CLOEXEC; | 701 | const os_flags = lock_flag | O_LARGEFILE | os.O_CREAT | os.O_CLOEXEC | |
| 694 | const os_flags = lock_flag | O_LARGEFILE | os.O_CREAT | O_CLOEXEC | | ||
| 695 | (if (flags.truncate) @as(u32, os.O_TRUNC) else 0) | | 702 | (if (flags.truncate) @as(u32, os.O_TRUNC) else 0) | |
| 696 | (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) | |
| 697 | (if (flags.exclusive) @as(u32, os.O_EXCL) else 0); | 704 | (if (flags.exclusive) @as(u32, os.O_EXCL) else 0); |
| 698 | const fd = if (need_async_thread) | 705 | const fd = if (flags.intended_io_mode != .blocking) |
| 699 | 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) |
| 700 | else | 707 | else |
| 701 | 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); |
| ... | @@ -710,31 +717,38 @@ pub const Dir = struct { | ... | @@ -710,31 +717,38 @@ pub const Dir = struct { |
| 710 | }); | 717 | }); |
| 711 | } | 718 | } |
| 712 | 719 | ||
| 713 | 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 | }; | ||
| 714 | } | 725 | } |
| 715 | 726 | ||
| 716 | /// Same as `createFile` but Windows-only and the path parameter is | 727 | /// Same as `createFile` but Windows-only and the path parameter is |
| 717 | /// [WTF-16](https://simonsapin.github.io/wtf-8/#potentially-ill-formed-utf-16) encoded. | 728 | /// [WTF-16](https://simonsapin.github.io/wtf-8/#potentially-ill-formed-utf-16) encoded. |
| 718 | pub fn createFileW(self: Dir, sub_path_w: [*:0]const u16, flags: File.CreateFlags) File.OpenError!File { | 729 | pub fn createFileW(self: Dir, sub_path_w: []const u16, flags: File.CreateFlags) File.OpenError!File { |
| 719 | const w = os.windows; | 730 | const w = os.windows; |
| 720 | const access_mask = w.SYNCHRONIZE | w.GENERIC_WRITE | | 731 | const read_flag = if (flags.read) @as(u32, w.GENERIC_READ) else 0; |
| 721 | (if (flags.read) @as(u32, w.GENERIC_READ) else 0); | ||
| 722 | const creation = if (flags.exclusive) | ||
| 723 | @as(u32, w.FILE_CREATE) | ||
| 724 | else if (flags.truncate) | ||
| 725 | @as(u32, w.FILE_OVERWRITE_IF) | ||
| 726 | else | ||
| 727 | @as(u32, w.FILE_OPEN_IF); | ||
| 728 | |||
| 729 | const share_access = switch (flags.lock) { | ||
| 730 | .None => @as(?w.ULONG, null), | ||
| 731 | .Shared => w.FILE_SHARE_READ | w.FILE_SHARE_DELETE, | ||
| 732 | .Exclusive => w.FILE_SHARE_DELETE, | ||
| 733 | }; | ||
| 734 | |||
| 735 | return @as(File, .{ | 732 | return @as(File, .{ |
| 736 | .handle = try os.windows.OpenFileW(self.fd, sub_path_w, null, access_mask, share_access, flags.lock_nonblocking, creation), | 733 | .handle = try os.windows.OpenFile(sub_path_w, .{ |
| 737 | .io_mode = .blocking, | 734 | .dir = self.fd, |
| 735 | .access_mask = w.SYNCHRONIZE | w.GENERIC_WRITE | read_flag, | ||
| 736 | .share_access = switch (flags.lock) { | ||
| 737 | .None => w.FILE_SHARE_WRITE | w.FILE_SHARE_READ | w.FILE_SHARE_DELETE, | ||
| 738 | .Shared => w.FILE_SHARE_READ | w.FILE_SHARE_DELETE, | ||
| 739 | .Exclusive => w.FILE_SHARE_DELETE, | ||
| 740 | }, | ||
| 741 | .share_access_nonblocking = flags.lock_nonblocking, | ||
| 742 | .creation = if (flags.exclusive) | ||
| 743 | @as(u32, w.FILE_CREATE) | ||
| 744 | else if (flags.truncate) | ||
| 745 | @as(u32, w.FILE_OVERWRITE_IF) | ||
| 746 | else | ||
| 747 | @as(u32, w.FILE_OPEN_IF), | ||
| 748 | .io_mode = flags.intended_io_mode, | ||
| 749 | }), | ||
| 750 | .capable_io_mode = std.io.default_mode, | ||
| 751 | .intended_io_mode = flags.intended_io_mode, | ||
| 738 | }); | 752 | }); |
| 739 | } | 753 | } |
| 740 | 754 | ||
| ... | @@ -818,11 +832,6 @@ pub const Dir = struct { | ... | @@ -818,11 +832,6 @@ pub const Dir = struct { |
| 818 | /// `true` means the opened directory can be scanned for the files and sub-directories | 832 | /// `true` means the opened directory can be scanned for the files and sub-directories |
| 819 | /// of the result. It means the `iterate` function can be called. | 833 | /// of the result. It means the `iterate` function can be called. |
| 820 | iterate: bool = false, | 834 | iterate: bool = false, |
| 821 | |||
| 822 | /// `true` means the opened directory can be passed to a child process. | ||
| 823 | /// `false` means the directory handle is considered to be closed when a child | ||
| 824 | /// process is spawned. This corresponds to the inverse of `O_CLOEXEC` on POSIX. | ||
| 825 | share_with_child_process: bool = false, | ||
| 826 | }; | 835 | }; |
| 827 | 836 | ||
| 828 | /// Opens a directory at the given path. The directory is a system resource that remains | 837 | /// Opens a directory at the given path. The directory is a system resource that remains |
| ... | @@ -832,7 +841,7 @@ pub const Dir = struct { | ... | @@ -832,7 +841,7 @@ pub const Dir = struct { |
| 832 | pub fn openDir(self: Dir, sub_path: []const u8, args: OpenDirOptions) OpenError!Dir { | 841 | pub fn openDir(self: Dir, sub_path: []const u8, args: OpenDirOptions) OpenError!Dir { |
| 833 | if (builtin.os.tag == .windows) { | 842 | if (builtin.os.tag == .windows) { |
| 834 | const sub_path_w = try os.windows.sliceToPrefixedFileW(sub_path); | 843 | const sub_path_w = try os.windows.sliceToPrefixedFileW(sub_path); |
| 835 | return self.openDirW(&sub_path_w, args); | 844 | return self.openDirW(sub_path_w.span().ptr, args); |
| 836 | } else { | 845 | } else { |
| 837 | const sub_path_c = try os.toPosixPath(sub_path); | 846 | const sub_path_c = try os.toPosixPath(sub_path); |
| 838 | return self.openDirZ(&sub_path_c, args); | 847 | return self.openDirZ(&sub_path_c, args); |
| ... | @@ -845,14 +854,12 @@ pub const Dir = struct { | ... | @@ -845,14 +854,12 @@ pub const Dir = struct { |
| 845 | pub fn openDirZ(self: Dir, sub_path_c: [*:0]const u8, args: OpenDirOptions) OpenError!Dir { | 854 | pub fn openDirZ(self: Dir, sub_path_c: [*:0]const u8, args: OpenDirOptions) OpenError!Dir { |
| 846 | if (builtin.os.tag == .windows) { | 855 | if (builtin.os.tag == .windows) { |
| 847 | const sub_path_w = try os.windows.cStrToPrefixedFileW(sub_path_c); | 856 | const sub_path_w = try os.windows.cStrToPrefixedFileW(sub_path_c); |
| 848 | return self.openDirW(&sub_path_w, args); | 857 | return self.openDirW(sub_path_w.span().ptr, args); |
| 849 | } else if (!args.iterate) { | 858 | } else if (!args.iterate) { |
| 850 | const O_PATH = if (@hasDecl(os, "O_PATH")) os.O_PATH else 0; | 859 | const O_PATH = if (@hasDecl(os, "O_PATH")) os.O_PATH else 0; |
| 851 | const O_CLOEXEC: u32 = if (args.share_with_child_process) 0 else os.O_CLOEXEC; | 860 | return self.openDirFlagsZ(sub_path_c, os.O_DIRECTORY | os.O_RDONLY | os.O_CLOEXEC | O_PATH); |
| 852 | return self.openDirFlagsZ(sub_path_c, os.O_DIRECTORY | os.O_RDONLY | O_CLOEXEC | O_PATH); | ||
| 853 | } else { | 861 | } else { |
| 854 | const O_CLOEXEC: u32 = if (args.share_with_child_process) 0 else os.O_CLOEXEC; | 862 | return self.openDirFlagsZ(sub_path_c, os.O_DIRECTORY | os.O_RDONLY | os.O_CLOEXEC); |
| 855 | return self.openDirFlagsZ(sub_path_c, os.O_DIRECTORY | os.O_RDONLY | O_CLOEXEC); | ||
| 856 | } | 863 | } |
| 857 | } | 864 | } |
| 858 | 865 | ||
| ... | @@ -989,7 +996,7 @@ pub const Dir = struct { | ... | @@ -989,7 +996,7 @@ pub const Dir = struct { |
| 989 | pub fn deleteDir(self: Dir, sub_path: []const u8) DeleteDirError!void { | 996 | pub fn deleteDir(self: Dir, sub_path: []const u8) DeleteDirError!void { |
| 990 | if (builtin.os.tag == .windows) { | 997 | if (builtin.os.tag == .windows) { |
| 991 | const sub_path_w = try os.windows.sliceToPrefixedFileW(sub_path); | 998 | const sub_path_w = try os.windows.sliceToPrefixedFileW(sub_path); |
| 992 | return self.deleteDirW(&sub_path_w); | 999 | return self.deleteDirW(sub_path_w.span().ptr); |
| 993 | } | 1000 | } |
| 994 | const sub_path_c = try os.toPosixPath(sub_path); | 1001 | const sub_path_c = try os.toPosixPath(sub_path); |
| 995 | return self.deleteDirZ(&sub_path_c); | 1002 | return self.deleteDirZ(&sub_path_c); |
| ... | @@ -1248,7 +1255,7 @@ pub const Dir = struct { | ... | @@ -1248,7 +1255,7 @@ pub const Dir = struct { |
| 1248 | pub fn access(self: Dir, sub_path: []const u8, flags: File.OpenFlags) AccessError!void { | 1255 | pub fn access(self: Dir, sub_path: []const u8, flags: File.OpenFlags) AccessError!void { |
| 1249 | if (builtin.os.tag == .windows) { | 1256 | if (builtin.os.tag == .windows) { |
| 1250 | const sub_path_w = try os.windows.sliceToPrefixedFileW(sub_path); | 1257 | const sub_path_w = try os.windows.sliceToPrefixedFileW(sub_path); |
| 1251 | return self.accessW(&sub_path_w, flags); | 1258 | return self.accessW(sub_path_w.span().ptr, flags); |
| 1252 | } | 1259 | } |
| 1253 | const path_c = try os.toPosixPath(sub_path); | 1260 | const path_c = try os.toPosixPath(sub_path); |
| 1254 | return self.accessZ(&path_c, flags); | 1261 | return self.accessZ(&path_c, flags); |
| ... | @@ -1258,7 +1265,7 @@ pub const Dir = struct { | ... | @@ -1258,7 +1265,7 @@ pub const Dir = struct { |
| 1258 | pub fn accessZ(self: Dir, sub_path: [*:0]const u8, flags: File.OpenFlags) AccessError!void { | 1265 | pub fn accessZ(self: Dir, sub_path: [*:0]const u8, flags: File.OpenFlags) AccessError!void { |
| 1259 | if (builtin.os.tag == .windows) { | 1266 | if (builtin.os.tag == .windows) { |
| 1260 | const sub_path_w = try os.windows.cStrToPrefixedFileW(sub_path); | 1267 | const sub_path_w = try os.windows.cStrToPrefixedFileW(sub_path); |
| 1261 | return self.accessW(&sub_path_w, flags); | 1268 | return self.accessW(sub_path_w.span().ptr, flags); |
| 1262 | } | 1269 | } |
| 1263 | const os_mode = if (flags.write and flags.read) | 1270 | const os_mode = if (flags.write and flags.read) |
| 1264 | @as(u32, os.R_OK | os.W_OK) | 1271 | @as(u32, os.R_OK | os.W_OK) |
| ... | @@ -1266,7 +1273,7 @@ pub const Dir = struct { | ... | @@ -1266,7 +1273,7 @@ pub const Dir = struct { |
| 1266 | @as(u32, os.W_OK) | 1273 | @as(u32, os.W_OK) |
| 1267 | else | 1274 | else |
| 1268 | @as(u32, os.F_OK); | 1275 | @as(u32, os.F_OK); |
| 1269 | const result = if (need_async_thread) | 1276 | const result = if (need_async_thread and flags.intended_io_mode != .blocking) |
| 1270 | std.event.Loop.instance.?.faccessatZ(self.fd, sub_path, os_mode, 0) | 1277 | std.event.Loop.instance.?.faccessatZ(self.fd, sub_path, os_mode, 0) |
| 1271 | else | 1278 | else |
| 1272 | os.faccessatZ(self.fd, sub_path, os_mode, 0); | 1279 | os.faccessatZ(self.fd, sub_path, os_mode, 0); |
| ... | @@ -1408,8 +1415,8 @@ pub fn openFileAbsoluteZ(absolute_path_c: [*:0]const u8, flags: File.OpenFlags) | ... | @@ -1408,8 +1415,8 @@ pub fn openFileAbsoluteZ(absolute_path_c: [*:0]const u8, flags: File.OpenFlags) |
| 1408 | } | 1415 | } |
| 1409 | 1416 | ||
| 1410 | /// Same as `openFileAbsolute` but the path parameter is WTF-16 encoded. | 1417 | /// Same as `openFileAbsolute` but the path parameter is WTF-16 encoded. |
| 1411 | pub fn openFileAbsoluteW(absolute_path_w: [*:0]const u16, flags: File.OpenFlags) File.OpenError!File { | 1418 | pub fn openFileAbsoluteW(absolute_path_w: []const u16, flags: File.OpenFlags) File.OpenError!File { |
| 1412 | assert(path.isAbsoluteWindowsW(absolute_path_w)); | 1419 | assert(path.isAbsoluteWindowsWTF16(absolute_path_w)); |
| 1413 | return cwd().openFileW(absolute_path_w, flags); | 1420 | return cwd().openFileW(absolute_path_w, flags); |
| 1414 | } | 1421 | } |
| 1415 | 1422 | ||
| ... | @@ -1598,7 +1605,7 @@ pub fn openSelfExe() OpenSelfExeError!File { | ... | @@ -1598,7 +1605,7 @@ pub fn openSelfExe() OpenSelfExeError!File { |
| 1598 | if (builtin.os.tag == .windows) { | 1605 | if (builtin.os.tag == .windows) { |
| 1599 | const wide_slice = selfExePathW(); | 1606 | const wide_slice = selfExePathW(); |
| 1600 | const prefixed_path_w = try os.windows.wToPrefixedFileW(wide_slice); | 1607 | const prefixed_path_w = try os.windows.wToPrefixedFileW(wide_slice); |
| 1601 | return cwd().openFileW(&prefixed_path_w, .{}); | 1608 | return cwd().openFileW(prefixed_path_w.span(), .{}); |
| 1602 | } | 1609 | } |
| 1603 | var buf: [MAX_PATH_BYTES]u8 = undefined; | 1610 | var buf: [MAX_PATH_BYTES]u8 = undefined; |
| 1604 | const self_exe_path = try selfExePath(&buf); | 1611 | const self_exe_path = try selfExePath(&buf); |
| ... | @@ -1626,7 +1633,7 @@ pub fn selfExePathAlloc(allocator: *Allocator) ![]u8 { | ... | @@ -1626,7 +1633,7 @@ pub fn selfExePathAlloc(allocator: *Allocator) ![]u8 { |
| 1626 | /// been deleted, the file path looks something like `/a/b/c/exe (deleted)`. | 1633 | /// been deleted, the file path looks something like `/a/b/c/exe (deleted)`. |
| 1627 | /// TODO make the return type of this a null terminated pointer | 1634 | /// TODO make the return type of this a null terminated pointer |
| 1628 | pub fn selfExePath(out_buffer: *[MAX_PATH_BYTES]u8) SelfExePathError![]u8 { | 1635 | pub fn selfExePath(out_buffer: *[MAX_PATH_BYTES]u8) SelfExePathError![]u8 { |
| 1629 | if (comptime std.Target.current.isDarwin()) { | 1636 | if (is_darwin) { |
| 1630 | var u32_len: u32 = out_buffer.len; | 1637 | var u32_len: u32 = out_buffer.len; |
| 1631 | const rc = std.c._NSGetExecutablePath(out_buffer, &u32_len); | 1638 | const rc = std.c._NSGetExecutablePath(out_buffer, &u32_len); |
| 1632 | if (rc != 0) return error.NameTooLong; | 1639 | if (rc != 0) return error.NameTooLong; |
lib/std/fs/file.zig+62-34| ... | @@ -8,7 +8,7 @@ const assert = std.debug.assert; | ... | @@ -8,7 +8,7 @@ const assert = std.debug.assert; |
| 8 | const windows = os.windows; | 8 | const windows = os.windows; |
| 9 | const Os = builtin.Os; | 9 | const Os = builtin.Os; |
| 10 | const maxInt = std.math.maxInt; | 10 | const maxInt = std.math.maxInt; |
| 11 | const need_async_thread = std.fs.need_async_thread; | 11 | const is_windows = std.Target.current.os.tag == .windows; |
| 12 | 12 | ||
| 13 | pub const File = struct { | 13 | pub const File = struct { |
| 14 | /// The OS-specific file descriptor or file handle. | 14 | /// The OS-specific file descriptor or file handle. |
| ... | @@ -17,15 +17,14 @@ pub const File = struct { | ... | @@ -17,15 +17,14 @@ pub const File = struct { |
| 17 | /// On some systems, such as Linux, file system file descriptors are incapable of non-blocking I/O. | 17 | /// 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-blocking | 18 | /// 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, | 19 | /// 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. | 20 | /// or, more specifically, whether the I/O is always blocking. |
| 21 | io_mode: io.Mode, | 21 | capable_io_mode: io.ModeOverride = io.default_mode, |
| 22 | 22 | ||
| 23 | /// Even when 'std.io.mode' is async, it is still sometimes desirable to perform blocking I/O, although | 23 | /// 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. | 24 | /// 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, | 25 | /// This field tracks both by acting as an overriding I/O mode. When not building in async I/O mode, |
| 26 | 26 | /// 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 {}; | 27 | intended_io_mode: io.ModeOverride = io.default_mode, |
| 28 | pub const async_block_allowed_no = if (io.is_async) false else {}; | ||
| 29 | 28 | ||
| 30 | pub const Mode = os.mode_t; | 29 | pub const Mode = os.mode_t; |
| 31 | 30 | ||
| ... | @@ -36,9 +35,7 @@ pub const File = struct { | ... | @@ -36,9 +35,7 @@ pub const File = struct { |
| 36 | 35 | ||
| 37 | pub const OpenError = windows.CreateFileError || os.OpenError || os.FlockError; | 36 | pub const OpenError = windows.CreateFileError || os.OpenError || os.FlockError; |
| 38 | 37 | ||
| 39 | pub const Lock = enum { | 38 | pub const Lock = enum { None, Shared, Exclusive }; |
| 40 | None, Shared, Exclusive | ||
| 41 | }; | ||
| 42 | 39 | ||
| 43 | /// TODO https://github.com/ziglang/zig/issues/3802 | 40 | /// TODO https://github.com/ziglang/zig/issues/3802 |
| 44 | pub const OpenFlags = struct { | 41 | pub const OpenFlags = struct { |
| ... | @@ -63,17 +60,15 @@ pub const File = struct { | ... | @@ -63,17 +60,15 @@ pub const File = struct { |
| 63 | /// Sets whether or not to wait until the file is locked to return. If set to true, | 60 | /// 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 file | 61 | /// `error.WouldBlock` will be returned. Otherwise, the file will wait until the file |
| 65 | /// is available to proceed. | 62 | /// is available to proceed. |
| 63 | /// In async I/O mode, non-blocking at the OS level is | ||
| 64 | /// determined by `intended_io_mode`, and `true` means `error.WouldBlock` is returned, | ||
| 65 | /// and `false` means `error.WouldBlock` is handled by the event loop. | ||
| 66 | lock_nonblocking: bool = false, | 66 | lock_nonblocking: bool = false, |
| 67 | 67 | ||
| 68 | /// This prevents `O_NONBLOCK` from being passed even if `std.io.is_async`. | 68 | /// Setting this to `.blocking` prevents `O_NONBLOCK` from being passed even |
| 69 | /// It allows the use of `noasync` when calling functions related to opening | 69 | /// if `std.io.is_async`. It allows the use of `noasync` when calling functions |
| 70 | /// the file, reading, and writing. | 70 | /// related to opening the file, reading, writing, and locking. |
| 71 | always_blocking: bool = false, | 71 | intended_io_mode: io.ModeOverride = io.default_mode, |
| 72 | |||
| 73 | /// `true` means the opened directory can be passed to a child process. | ||
| 74 | /// `false` means the directory handle is considered to be closed when a child | ||
| 75 | /// process is spawned. This corresponds to the inverse of `O_CLOEXEC` on POSIX. | ||
| 76 | share_with_child_process: bool = false, | ||
| 77 | }; | 72 | }; |
| 78 | 73 | ||
| 79 | /// TODO https://github.com/ziglang/zig/issues/3802 | 74 | /// TODO https://github.com/ziglang/zig/issues/3802 |
| ... | @@ -107,22 +102,27 @@ pub const File = struct { | ... | @@ -107,22 +102,27 @@ pub const File = struct { |
| 107 | /// Sets whether or not to wait until the file is locked to return. If set to true, | 102 | /// Sets whether or not to wait until the file is locked to return. If set to true, |
| 108 | /// `error.WouldBlock` will be returned. Otherwise, the file will wait until the file | 103 | /// `error.WouldBlock` will be returned. Otherwise, the file will wait until the file |
| 109 | /// is available to proceed. | 104 | /// is available to proceed. |
| 105 | /// In async I/O mode, non-blocking at the OS level is | ||
| 106 | /// determined by `intended_io_mode`, and `true` means `error.WouldBlock` is returned, | ||
| 107 | /// and `false` means `error.WouldBlock` is handled by the event loop. | ||
| 110 | lock_nonblocking: bool = false, | 108 | lock_nonblocking: bool = false, |
| 111 | 109 | ||
| 112 | /// For POSIX systems this is the file system mode the file will | 110 | /// For POSIX systems this is the file system mode the file will |
| 113 | /// be created with. | 111 | /// be created with. |
| 114 | mode: Mode = default_mode, | 112 | mode: Mode = default_mode, |
| 115 | 113 | ||
| 116 | /// `true` means the opened directory can be passed to a child process. | 114 | /// Setting this to `.blocking` prevents `O_NONBLOCK` from being passed even |
| 117 | /// `false` means the directory handle is considered to be closed when a child | 115 | /// if `std.io.is_async`. It allows the use of `noasync` when calling functions |
| 118 | /// process is spawned. This corresponds to the inverse of `O_CLOEXEC` on POSIX. | 116 | /// related to opening the file, reading, writing, and locking. |
| 119 | share_with_child_process: bool = false, | 117 | intended_io_mode: io.ModeOverride = io.default_mode, |
| 120 | }; | 118 | }; |
| 121 | 119 | ||
| 122 | /// Upon success, the stream is in an uninitialized state. To continue using it, | 120 | /// Upon success, the stream is in an uninitialized state. To continue using it, |
| 123 | /// you must use the open() function. | 121 | /// you must use the open() function. |
| 124 | pub fn close(self: File) void { | 122 | pub fn close(self: File) void { |
| 125 | if (need_async_thread and self.io_mode == .blocking and !self.async_block_allowed) { | 123 | if (is_windows) { |
| 124 | windows.CloseHandle(self.handle); | ||
| 125 | } else if (self.capable_io_mode != self.intended_io_mode) { | ||
| 126 | std.event.Loop.instance.?.close(self.handle); | 126 | std.event.Loop.instance.?.close(self.handle); |
| 127 | } else { | 127 | } else { |
| 128 | os.close(self.handle); | 128 | os.close(self.handle); |
| ... | @@ -305,7 +305,9 @@ pub const File = struct { | ... | @@ -305,7 +305,9 @@ pub const File = struct { |
| 305 | pub const PReadError = os.PReadError; | 305 | pub const PReadError = os.PReadError; |
| 306 | 306 | ||
| 307 | pub fn read(self: File, buffer: []u8) ReadError!usize { | 307 | pub fn read(self: File, buffer: []u8) ReadError!usize { |
| 308 | if (need_async_thread and self.io_mode == .blocking and !self.async_block_allowed) { | 308 | if (is_windows) { |
| 309 | return windows.ReadFile(self.handle, buffer, null, self.intended_io_mode); | ||
| 310 | } else if (self.capable_io_mode != self.intended_io_mode) { | ||
| 309 | return std.event.Loop.instance.?.read(self.handle, buffer); | 311 | return std.event.Loop.instance.?.read(self.handle, buffer); |
| 310 | } else { | 312 | } else { |
| 311 | return os.read(self.handle, buffer); | 313 | return os.read(self.handle, buffer); |
| ... | @@ -325,7 +327,9 @@ pub const File = struct { | ... | @@ -325,7 +327,9 @@ pub const File = struct { |
| 325 | } | 327 | } |
| 326 | 328 | ||
| 327 | pub fn pread(self: File, buffer: []u8, offset: u64) PReadError!usize { | 329 | pub fn pread(self: File, buffer: []u8, offset: u64) PReadError!usize { |
| 328 | if (need_async_thread and self.io_mode == .blocking and !self.async_block_allowed) { | 330 | if (is_windows) { |
| 331 | return windows.ReadFile(self.handle, buffer, offset, self.intended_io_mode); | ||
| 332 | } else if (self.capable_io_mode != self.intended_io_mode) { | ||
| 329 | return std.event.Loop.instance.?.pread(self.handle, buffer, offset); | 333 | return std.event.Loop.instance.?.pread(self.handle, buffer, offset); |
| 330 | } else { | 334 | } else { |
| 331 | return os.pread(self.handle, buffer, offset); | 335 | return os.pread(self.handle, buffer, offset); |
| ... | @@ -345,7 +349,12 @@ pub const File = struct { | ... | @@ -345,7 +349,12 @@ pub const File = struct { |
| 345 | } | 349 | } |
| 346 | 350 | ||
| 347 | pub fn readv(self: File, iovecs: []const os.iovec) ReadError!usize { | 351 | 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) { | 352 | if (is_windows) { |
| 353 | // TODO improve this to use ReadFileScatter | ||
| 354 | if (iovecs.len == 0) return @as(usize, 0); | ||
| 355 | const first = iovecs[0]; | ||
| 356 | return windows.ReadFile(self.handle, first.iov_base[0..first.iov_len], null, self.intended_io_mode); | ||
| 357 | } else if (self.capable_io_mode != self.intended_io_mode) { | ||
| 349 | return std.event.Loop.instance.?.readv(self.handle, iovecs); | 358 | return std.event.Loop.instance.?.readv(self.handle, iovecs); |
| 350 | } else { | 359 | } else { |
| 351 | return os.readv(self.handle, iovecs); | 360 | return os.readv(self.handle, iovecs); |
| ... | @@ -379,7 +388,12 @@ pub const File = struct { | ... | @@ -379,7 +388,12 @@ pub const File = struct { |
| 379 | } | 388 | } |
| 380 | 389 | ||
| 381 | pub fn preadv(self: File, iovecs: []const os.iovec, offset: u64) PReadError!usize { | 390 | 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) { | 391 | if (is_windows) { |
| 392 | // TODO improve this to use ReadFileScatter | ||
| 393 | if (iovecs.len == 0) return @as(usize, 0); | ||
| 394 | const first = iovecs[0]; | ||
| 395 | return windows.ReadFile(self.handle, first.iov_base[0..first.iov_len], offset, self.intended_io_mode); | ||
| 396 | } else if (self.capable_io_mode != self.intended_io_mode) { | ||
| 383 | return std.event.Loop.instance.?.preadv(self.handle, iovecs, offset); | 397 | return std.event.Loop.instance.?.preadv(self.handle, iovecs, offset); |
| 384 | } else { | 398 | } else { |
| 385 | return os.preadv(self.handle, iovecs, offset); | 399 | return os.preadv(self.handle, iovecs, offset); |
| ... | @@ -416,7 +430,9 @@ pub const File = struct { | ... | @@ -416,7 +430,9 @@ pub const File = struct { |
| 416 | pub const PWriteError = os.PWriteError; | 430 | pub const PWriteError = os.PWriteError; |
| 417 | 431 | ||
| 418 | pub fn write(self: File, bytes: []const u8) WriteError!usize { | 432 | pub fn write(self: File, bytes: []const u8) WriteError!usize { |
| 419 | if (need_async_thread and self.io_mode == .blocking and !self.async_block_allowed) { | 433 | if (is_windows) { |
| 434 | return windows.WriteFile(self.handle, bytes, null, self.intended_io_mode); | ||
| 435 | } else if (self.capable_io_mode != self.intended_io_mode) { | ||
| 420 | return std.event.Loop.instance.?.write(self.handle, bytes); | 436 | return std.event.Loop.instance.?.write(self.handle, bytes); |
| 421 | } else { | 437 | } else { |
| 422 | return os.write(self.handle, bytes); | 438 | return os.write(self.handle, bytes); |
| ... | @@ -431,7 +447,9 @@ pub const File = struct { | ... | @@ -431,7 +447,9 @@ pub const File = struct { |
| 431 | } | 447 | } |
| 432 | 448 | ||
| 433 | pub fn pwrite(self: File, bytes: []const u8, offset: u64) PWriteError!usize { | 449 | pub fn pwrite(self: File, bytes: []const u8, offset: u64) PWriteError!usize { |
| 434 | if (need_async_thread and self.io_mode == .blocking and !self.async_block_allowed) { | 450 | if (is_windows) { |
| 451 | return windows.WriteFile(self.handle, bytes, offset, self.intended_io_mode); | ||
| 452 | } else if (self.capable_io_mode != self.intended_io_mode) { | ||
| 435 | return std.event.Loop.instance.?.pwrite(self.handle, bytes, offset); | 453 | return std.event.Loop.instance.?.pwrite(self.handle, bytes, offset); |
| 436 | } else { | 454 | } else { |
| 437 | return os.pwrite(self.handle, bytes, offset); | 455 | return os.pwrite(self.handle, bytes, offset); |
| ... | @@ -446,7 +464,12 @@ pub const File = struct { | ... | @@ -446,7 +464,12 @@ pub const File = struct { |
| 446 | } | 464 | } |
| 447 | 465 | ||
| 448 | pub fn writev(self: File, iovecs: []const os.iovec_const) WriteError!usize { | 466 | pub fn writev(self: File, iovecs: []const os.iovec_const) WriteError!usize { |
| 449 | if (need_async_thread and self.io_mode == .blocking and !self.async_block_allowed) { | 467 | if (is_windows) { |
| 468 | // TODO improve this to use WriteFileScatter | ||
| 469 | if (iovecs.len == 0) return @as(usize, 0); | ||
| 470 | const first = iovecs[0]; | ||
| 471 | return windows.WriteFile(self.handle, first.iov_base[0..first.iov_len], null, self.intended_io_mode); | ||
| 472 | } else if (self.capable_io_mode != self.intended_io_mode) { | ||
| 450 | return std.event.Loop.instance.?.writev(self.handle, iovecs); | 473 | return std.event.Loop.instance.?.writev(self.handle, iovecs); |
| 451 | } else { | 474 | } else { |
| 452 | return os.writev(self.handle, iovecs); | 475 | return os.writev(self.handle, iovecs); |
| ... | @@ -472,7 +495,12 @@ pub const File = struct { | ... | @@ -472,7 +495,12 @@ pub const File = struct { |
| 472 | } | 495 | } |
| 473 | 496 | ||
| 474 | pub fn pwritev(self: File, iovecs: []os.iovec_const, offset: usize) PWriteError!usize { | 497 | pub fn pwritev(self: File, iovecs: []os.iovec_const, offset: usize) PWriteError!usize { |
| 475 | if (need_async_thread and self.io_mode == .blocking and !self.async_block_allowed) { | 498 | if (is_windows) { |
| 499 | // TODO improve this to use WriteFileScatter | ||
| 500 | if (iovecs.len == 0) return @as(usize, 0); | ||
| 501 | const first = iovecs[0]; | ||
| 502 | return windows.WriteFile(self.handle, first.iov_base[0..first.iov_len], offset, self.intended_io_mode); | ||
| 503 | } else if (self.capable_io_mode != self.intended_io_mode) { | ||
| 476 | return std.event.Loop.instance.?.pwritev(self.handle, iovecs, offset); | 504 | return std.event.Loop.instance.?.pwritev(self.handle, iovecs, offset); |
| 477 | } else { | 505 | } else { |
| 478 | return os.pwritev(self.handle, iovecs, offset); | 506 | return os.pwritev(self.handle, iovecs, offset); |
lib/std/fs/path.zig+4| ... | @@ -177,6 +177,10 @@ pub fn isAbsoluteWindowsW(path_w: [*:0]const u16) bool { | ... | @@ -177,6 +177,10 @@ pub fn isAbsoluteWindowsW(path_w: [*:0]const u16) bool { |
| 177 | return isAbsoluteWindowsImpl(u16, mem.spanZ(path_w)); | 177 | return isAbsoluteWindowsImpl(u16, mem.spanZ(path_w)); |
| 178 | } | 178 | } |
| 179 | 179 | ||
| 180 | pub fn isAbsoluteWindowsWTF16(path: []const u16) bool { | ||
| 181 | return isAbsoluteWindowsImpl(u16, path); | ||
| 182 | } | ||
| 183 | |||
| 180 | pub const isAbsoluteWindowsC = @compileError("deprecated: renamed to isAbsoluteWindowsZ"); | 184 | pub const isAbsoluteWindowsC = @compileError("deprecated: renamed to isAbsoluteWindowsZ"); |
| 181 | 185 | ||
| 182 | pub fn isAbsoluteWindowsZ(path_c: [*:0]const u8) bool { | 186 | pub fn isAbsoluteWindowsZ(path_c: [*:0]const u8) bool { |
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 | } |
| 28 | 28 | ||
| 29 | test "open file with lock twice, make sure it wasn't open at the same time" { | 29 | test "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 | } | ||
| 31 | 36 | ||
| 32 | const filename = "file_lock_test.txt"; | 37 | const filename = "file_lock_test.txt"; |
| 33 | 38 | ||
| ... | @@ -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" { |
| 58 | test "create file, lock and read from multiple process at once" { | 63 | test "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; |
| 60 | 65 | ||
| 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/5006 | 72 | // https://github.com/ziglang/zig/issues/5006 |
| 63 | return error.SkipZigTest; | 73 | return error.SkipZigTest; |
lib/std/io.zig+17-4| ... | @@ -30,6 +30,11 @@ else | ... | @@ -30,6 +30,11 @@ else |
| 30 | Mode.blocking; | 30 | Mode.blocking; |
| 31 | pub const is_async = mode != .blocking; | 31 | pub const is_async = mode != .blocking; |
| 32 | 32 | ||
| 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`. | ||
| 35 | pub const ModeOverride = if (is_async) Mode else enum { blocking }; | ||
| 36 | pub const default_mode: ModeOverride = if (is_async) Mode.evented else .blocking; | ||
| 37 | |||
| 33 | fn getStdOutHandle() os.fd_t { | 38 | fn 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,10 +47,13 @@ fn getStdOutHandle() os.fd_t { | ... | @@ -42,10 +47,13 @@ fn getStdOutHandle() os.fd_t { |
| 42 | return os.STDOUT_FILENO; | 47 | return os.STDOUT_FILENO; |
| 43 | } | 48 | } |
| 44 | 49 | ||
| 50 | /// TODO: async stdout on windows without a dedicated thread. | ||
| 51 | /// https://github.com/ziglang/zig/pull/4816#issuecomment-604521023 | ||
| 45 | pub fn getStdOut() File { | 52 | pub fn getStdOut() File { |
| 46 | return File{ | 53 | return File{ |
| 47 | .handle = getStdOutHandle(), | 54 | .handle = getStdOutHandle(), |
| 48 | .io_mode = .blocking, | 55 | .capable_io_mode = .blocking, |
| 56 | .intended_io_mode = default_mode, | ||
| 49 | }; | 57 | }; |
| 50 | } | 58 | } |
| 51 | 59 | ||
| ... | @@ -61,11 +69,13 @@ fn getStdErrHandle() os.fd_t { | ... | @@ -61,11 +69,13 @@ fn getStdErrHandle() os.fd_t { |
| 61 | return os.STDERR_FILENO; | 69 | return os.STDERR_FILENO; |
| 62 | } | 70 | } |
| 63 | 71 | ||
| 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. | ||
| 64 | pub fn getStdErr() File { | 74 | pub fn getStdErr() File { |
| 65 | return File{ | 75 | return File{ |
| 66 | .handle = getStdErrHandle(), | 76 | .handle = getStdErrHandle(), |
| 67 | .io_mode = .blocking, | 77 | .capable_io_mode = .blocking, |
| 68 | .async_block_allowed = File.async_block_allowed_yes, | 78 | .intended_io_mode = .blocking, |
| 69 | }; | 79 | }; |
| 70 | } | 80 | } |
| 71 | 81 | ||
| ... | @@ -81,10 +91,13 @@ fn getStdInHandle() os.fd_t { | ... | @@ -81,10 +91,13 @@ fn getStdInHandle() os.fd_t { |
| 81 | return os.STDIN_FILENO; | 91 | return os.STDIN_FILENO; |
| 82 | } | 92 | } |
| 83 | 93 | ||
| 94 | /// TODO: async stdin on windows without a dedicated thread. | ||
| 95 | /// https://github.com/ziglang/zig/pull/4816#issuecomment-604521023 | ||
| 84 | pub fn getStdIn() File { | 96 | pub fn getStdIn() File { |
| 85 | return File{ | 97 | return File{ |
| 86 | .handle = getStdInHandle(), | 98 | .handle = getStdInHandle(), |
| 87 | .io_mode = .blocking, | 99 | .capable_io_mode = .blocking, |
| 100 | .intended_io_mode = default_mode, | ||
| 88 | }; | 101 | }; |
| 89 | } | 102 | } |
| 90 | 103 |
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()); |
| 414 | 414 | ||
| 415 | return fs.File{ .handle = sockfd, .io_mode = std.io.mode }; | 415 | return fs.File{ .handle = sockfd }; |
| 416 | } | 416 | } |
| 417 | 417 | ||
| 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+59-44| ... | @@ -177,8 +177,8 @@ fn getRandomBytesDevURandom(buf: []u8) !void { | ... | @@ -177,8 +177,8 @@ fn getRandomBytesDevURandom(buf: []u8) !void { |
| 177 | 177 | ||
| 178 | const file = std.fs.File{ | 178 | const file = std.fs.File{ |
| 179 | .handle = fd, | 179 | .handle = fd, |
| 180 | .io_mode = .blocking, | 180 | .capable_io_mode = .blocking, |
| 181 | .async_block_allowed = std.fs.File.async_block_allowed_yes, | 181 | .intended_io_mode = .blocking, |
| 182 | }; | 182 | }; |
| 183 | const stream = file.inStream(); | 183 | const stream = file.inStream(); |
| 184 | stream.readNoEof(buf) catch return error.Unexpected; | 184 | stream.readNoEof(buf) catch return error.Unexpected; |
| ... | @@ -309,7 +309,7 @@ pub const ReadError = error{ | ... | @@ -309,7 +309,7 @@ pub const ReadError = error{ |
| 309 | /// For POSIX the limit is `math.maxInt(isize)`. | 309 | /// For POSIX the limit is `math.maxInt(isize)`. |
| 310 | pub fn read(fd: fd_t, buf: []u8) ReadError!usize { | 310 | pub fn read(fd: fd_t, buf: []u8) ReadError!usize { |
| 311 | if (builtin.os.tag == .windows) { | 311 | if (builtin.os.tag == .windows) { |
| 312 | return windows.ReadFile(fd, buf, null); | 312 | return windows.ReadFile(fd, buf, null, std.io.default_mode); |
| 313 | } | 313 | } |
| 314 | 314 | ||
| 315 | if (builtin.os.tag == .wasi and !builtin.link_libc) { | 315 | if (builtin.os.tag == .wasi and !builtin.link_libc) { |
| ... | @@ -369,7 +369,7 @@ pub fn read(fd: fd_t, buf: []u8) ReadError!usize { | ... | @@ -369,7 +369,7 @@ pub fn read(fd: fd_t, buf: []u8) ReadError!usize { |
| 369 | /// On these systems, the read races with concurrent writes to the same file descriptor. | 369 | /// On these systems, the read races with concurrent writes to the same file descriptor. |
| 370 | pub fn readv(fd: fd_t, iov: []const iovec) ReadError!usize { | 370 | pub fn readv(fd: fd_t, iov: []const iovec) ReadError!usize { |
| 371 | if (std.Target.current.os.tag == .windows) { | 371 | if (std.Target.current.os.tag == .windows) { |
| 372 | // TODO does Windows have a way to read an io vector? | 372 | // TODO improve this to use ReadFileScatter |
| 373 | if (iov.len == 0) return @as(usize, 0); | 373 | if (iov.len == 0) return @as(usize, 0); |
| 374 | const first = iov[0]; | 374 | const first = iov[0]; |
| 375 | return read(fd, first.iov_base[0..first.iov_len]); | 375 | return read(fd, first.iov_base[0..first.iov_len]); |
| ... | @@ -412,7 +412,7 @@ pub const PReadError = ReadError || error{Unseekable}; | ... | @@ -412,7 +412,7 @@ pub const PReadError = ReadError || error{Unseekable}; |
| 412 | /// used to perform the I/O. `error.WouldBlock` is not possible on Windows. | 412 | /// used to perform the I/O. `error.WouldBlock` is not possible on Windows. |
| 413 | pub fn pread(fd: fd_t, buf: []u8, offset: u64) PReadError!usize { | 413 | pub fn pread(fd: fd_t, buf: []u8, offset: u64) PReadError!usize { |
| 414 | if (builtin.os.tag == .windows) { | 414 | if (builtin.os.tag == .windows) { |
| 415 | return windows.ReadFile(fd, buf, offset); | 415 | return windows.ReadFile(fd, buf, offset, std.io.default_mode); |
| 416 | } | 416 | } |
| 417 | 417 | ||
| 418 | while (true) { | 418 | while (true) { |
| ... | @@ -588,7 +588,7 @@ pub const WriteError = error{ | ... | @@ -588,7 +588,7 @@ pub const WriteError = error{ |
| 588 | /// The corresponding POSIX limit is `math.maxInt(isize)`. | 588 | /// The corresponding POSIX limit is `math.maxInt(isize)`. |
| 589 | pub fn write(fd: fd_t, bytes: []const u8) WriteError!usize { | 589 | pub fn write(fd: fd_t, bytes: []const u8) WriteError!usize { |
| 590 | if (builtin.os.tag == .windows) { | 590 | if (builtin.os.tag == .windows) { |
| 591 | return windows.WriteFile(fd, bytes, null); | 591 | return windows.WriteFile(fd, bytes, null, std.io.default_mode); |
| 592 | } | 592 | } |
| 593 | 593 | ||
| 594 | if (builtin.os.tag == .wasi and !builtin.link_libc) { | 594 | if (builtin.os.tag == .wasi and !builtin.link_libc) { |
| ... | @@ -655,7 +655,7 @@ pub fn write(fd: fd_t, bytes: []const u8) WriteError!usize { | ... | @@ -655,7 +655,7 @@ pub fn write(fd: fd_t, bytes: []const u8) WriteError!usize { |
| 655 | /// If `iov.len` is larger than will fit in a `u31`, a partial write will occur. | 655 | /// If `iov.len` is larger than will fit in a `u31`, a partial write will occur. |
| 656 | pub fn writev(fd: fd_t, iov: []const iovec_const) WriteError!usize { | 656 | pub fn writev(fd: fd_t, iov: []const iovec_const) WriteError!usize { |
| 657 | if (std.Target.current.os.tag == .windows) { | 657 | if (std.Target.current.os.tag == .windows) { |
| 658 | // TODO does Windows have a way to write an io vector? | 658 | // TODO improve this to use WriteFileScatter |
| 659 | if (iov.len == 0) return @as(usize, 0); | 659 | if (iov.len == 0) return @as(usize, 0); |
| 660 | const first = iov[0]; | 660 | const first = iov[0]; |
| 661 | return write(fd, first.iov_base[0..first.iov_len]); | 661 | return write(fd, first.iov_base[0..first.iov_len]); |
| ... | @@ -713,7 +713,7 @@ pub const PWriteError = WriteError || error{Unseekable}; | ... | @@ -713,7 +713,7 @@ pub const PWriteError = WriteError || error{Unseekable}; |
| 713 | /// The corresponding POSIX limit is `math.maxInt(isize)`. | 713 | /// The corresponding POSIX limit is `math.maxInt(isize)`. |
| 714 | pub fn pwrite(fd: fd_t, bytes: []const u8, offset: u64) PWriteError!usize { | 714 | pub fn pwrite(fd: fd_t, bytes: []const u8, offset: u64) PWriteError!usize { |
| 715 | if (std.Target.current.os.tag == .windows) { | 715 | if (std.Target.current.os.tag == .windows) { |
| 716 | return windows.WriteFile(fd, bytes, offset); | 716 | return windows.WriteFile(fd, bytes, offset, std.io.default_mode); |
| 717 | } | 717 | } |
| 718 | 718 | ||
| 719 | // Prevent EINVAL. | 719 | // Prevent EINVAL. |
| ... | @@ -858,8 +858,11 @@ pub const OpenError = error{ | ... | @@ -858,8 +858,11 @@ pub const OpenError = error{ |
| 858 | 858 | ||
| 859 | /// Open and possibly create a file. Keeps trying if it gets interrupted. | 859 | /// Open and possibly create a file. Keeps trying if it gets interrupted. |
| 860 | /// See also `openC`. | 860 | /// See also `openC`. |
| 861 | /// TODO support windows | ||
| 862 | pub fn open(file_path: []const u8, flags: u32, perm: mode_t) OpenError!fd_t { | 861 | pub fn open(file_path: []const u8, flags: u32, perm: mode_t) OpenError!fd_t { |
| 862 | if (std.Target.current.os.tag == .windows) { | ||
| 863 | const file_path_w = try windows.sliceToPrefixedFileW(file_path); | ||
| 864 | return openW(file_path_w.span(), flags, perm); | ||
| 865 | } | ||
| 863 | const file_path_c = try toPosixPath(file_path); | 866 | const file_path_c = try toPosixPath(file_path); |
| 864 | return openZ(&file_path_c, flags, perm); | 867 | return openZ(&file_path_c, flags, perm); |
| 865 | } | 868 | } |
| ... | @@ -868,8 +871,11 @@ pub const openC = @compileError("deprecated: renamed to openZ"); | ... | @@ -868,8 +871,11 @@ pub const openC = @compileError("deprecated: renamed to openZ"); |
| 868 | 871 | ||
| 869 | /// Open and possibly create a file. Keeps trying if it gets interrupted. | 872 | /// Open and possibly create a file. Keeps trying if it gets interrupted. |
| 870 | /// See also `open`. | 873 | /// See also `open`. |
| 871 | /// TODO support windows | ||
| 872 | pub fn openZ(file_path: [*:0]const u8, flags: u32, perm: mode_t) OpenError!fd_t { | 874 | pub fn openZ(file_path: [*:0]const u8, flags: u32, perm: mode_t) OpenError!fd_t { |
| 875 | if (std.Target.current.os.tag == .windows) { | ||
| 876 | const file_path_w = try windows.cStrToPrefixedFileW(file_path); | ||
| 877 | return openW(file_path_w.span(), flags, perm); | ||
| 878 | } | ||
| 873 | while (true) { | 879 | while (true) { |
| 874 | const rc = system.open(file_path, flags, perm); | 880 | const rc = system.open(file_path, flags, perm); |
| 875 | switch (errno(rc)) { | 881 | switch (errno(rc)) { |
| ... | @@ -899,6 +905,13 @@ pub fn openZ(file_path: [*:0]const u8, flags: u32, perm: mode_t) OpenError!fd_t | ... | @@ -899,6 +905,13 @@ pub fn openZ(file_path: [*:0]const u8, flags: u32, perm: mode_t) OpenError!fd_t |
| 899 | } | 905 | } |
| 900 | } | 906 | } |
| 901 | 907 | ||
| 908 | /// Windows-only. The path parameter is | ||
| 909 | /// [WTF-16](https://simonsapin.github.io/wtf-8/#potentially-ill-formed-utf-16) encoded. | ||
| 910 | /// Translates the POSIX open API call to a Windows API call. | ||
| 911 | pub fn openW(file_path_w: []const u16, flags: u32, perm: usize) OpenError!fd_t { | ||
| 912 | @compileError("TODO implement openW for windows"); | ||
| 913 | } | ||
| 914 | |||
| 902 | /// Open and possibly create a file. Keeps trying if it gets interrupted. | 915 | /// Open and possibly create a file. Keeps trying if it gets interrupted. |
| 903 | /// `file_path` is relative to the open directory handle `dir_fd`. | 916 | /// `file_path` is relative to the open directory handle `dir_fd`. |
| 904 | /// See also `openatC`. | 917 | /// See also `openatC`. |
| ... | @@ -1308,7 +1321,7 @@ pub fn symlink(target_path: []const u8, sym_link_path: []const u8) SymLinkError! | ... | @@ -1308,7 +1321,7 @@ pub fn symlink(target_path: []const u8, sym_link_path: []const u8) SymLinkError! |
| 1308 | if (builtin.os.tag == .windows) { | 1321 | if (builtin.os.tag == .windows) { |
| 1309 | const target_path_w = try windows.sliceToPrefixedFileW(target_path); | 1322 | const target_path_w = try windows.sliceToPrefixedFileW(target_path); |
| 1310 | const sym_link_path_w = try windows.sliceToPrefixedFileW(sym_link_path); | 1323 | const sym_link_path_w = try windows.sliceToPrefixedFileW(sym_link_path); |
| 1311 | return windows.CreateSymbolicLinkW(&sym_link_path_w, &target_path_w, 0); | 1324 | return windows.CreateSymbolicLinkW(sym_link_path_w.span().ptr, target_path_w.span().ptr, 0); |
| 1312 | } else { | 1325 | } else { |
| 1313 | const target_path_c = try toPosixPath(target_path); | 1326 | const target_path_c = try toPosixPath(target_path); |
| 1314 | const sym_link_path_c = try toPosixPath(sym_link_path); | 1327 | const sym_link_path_c = try toPosixPath(sym_link_path); |
| ... | @@ -1324,7 +1337,7 @@ pub fn symlinkZ(target_path: [*:0]const u8, sym_link_path: [*:0]const u8) SymLin | ... | @@ -1324,7 +1337,7 @@ pub fn symlinkZ(target_path: [*:0]const u8, sym_link_path: [*:0]const u8) SymLin |
| 1324 | if (builtin.os.tag == .windows) { | 1337 | if (builtin.os.tag == .windows) { |
| 1325 | const target_path_w = try windows.cStrToPrefixedFileW(target_path); | 1338 | const target_path_w = try windows.cStrToPrefixedFileW(target_path); |
| 1326 | const sym_link_path_w = try windows.cStrToPrefixedFileW(sym_link_path); | 1339 | const sym_link_path_w = try windows.cStrToPrefixedFileW(sym_link_path); |
| 1327 | return windows.CreateSymbolicLinkW(&sym_link_path_w, &target_path_w, 0); | 1340 | return windows.CreateSymbolicLinkW(sym_link_path_w.span().ptr, target_path_w.span().ptr, 0); |
| 1328 | } | 1341 | } |
| 1329 | switch (errno(system.symlink(target_path, sym_link_path))) { | 1342 | switch (errno(system.symlink(target_path, sym_link_path))) { |
| 1330 | 0 => return, | 1343 | 0 => return, |
| ... | @@ -1400,7 +1413,7 @@ pub const UnlinkError = error{ | ... | @@ -1400,7 +1413,7 @@ pub const UnlinkError = error{ |
| 1400 | pub fn unlink(file_path: []const u8) UnlinkError!void { | 1413 | pub fn unlink(file_path: []const u8) UnlinkError!void { |
| 1401 | if (builtin.os.tag == .windows) { | 1414 | if (builtin.os.tag == .windows) { |
| 1402 | const file_path_w = try windows.sliceToPrefixedFileW(file_path); | 1415 | const file_path_w = try windows.sliceToPrefixedFileW(file_path); |
| 1403 | return windows.DeleteFileW(&file_path_w); | 1416 | return windows.DeleteFileW(file_path_w.span().ptr); |
| 1404 | } else { | 1417 | } else { |
| 1405 | const file_path_c = try toPosixPath(file_path); | 1418 | const file_path_c = try toPosixPath(file_path); |
| 1406 | return unlinkZ(&file_path_c); | 1419 | return unlinkZ(&file_path_c); |
| ... | @@ -1413,7 +1426,7 @@ pub const unlinkC = @compileError("deprecated: renamed to unlinkZ"); | ... | @@ -1413,7 +1426,7 @@ pub const unlinkC = @compileError("deprecated: renamed to unlinkZ"); |
| 1413 | pub fn unlinkZ(file_path: [*:0]const u8) UnlinkError!void { | 1426 | pub fn unlinkZ(file_path: [*:0]const u8) UnlinkError!void { |
| 1414 | if (builtin.os.tag == .windows) { | 1427 | if (builtin.os.tag == .windows) { |
| 1415 | const file_path_w = try windows.cStrToPrefixedFileW(file_path); | 1428 | const file_path_w = try windows.cStrToPrefixedFileW(file_path); |
| 1416 | return windows.DeleteFileW(&file_path_w); | 1429 | return windows.DeleteFileW(file_path_w.span().ptr); |
| 1417 | } | 1430 | } |
| 1418 | switch (errno(system.unlink(file_path))) { | 1431 | switch (errno(system.unlink(file_path))) { |
| 1419 | 0 => return, | 1432 | 0 => return, |
| ... | @@ -1444,7 +1457,7 @@ pub const UnlinkatError = UnlinkError || error{ | ... | @@ -1444,7 +1457,7 @@ pub const UnlinkatError = UnlinkError || error{ |
| 1444 | pub fn unlinkat(dirfd: fd_t, file_path: []const u8, flags: u32) UnlinkatError!void { | 1457 | pub fn unlinkat(dirfd: fd_t, file_path: []const u8, flags: u32) UnlinkatError!void { |
| 1445 | if (builtin.os.tag == .windows) { | 1458 | if (builtin.os.tag == .windows) { |
| 1446 | const file_path_w = try windows.sliceToPrefixedFileW(file_path); | 1459 | const file_path_w = try windows.sliceToPrefixedFileW(file_path); |
| 1447 | return unlinkatW(dirfd, &file_path_w, flags); | 1460 | return unlinkatW(dirfd, file_path_w.span().ptr, flags); |
| 1448 | } | 1461 | } |
| 1449 | const file_path_c = try toPosixPath(file_path); | 1462 | const file_path_c = try toPosixPath(file_path); |
| 1450 | return unlinkatZ(dirfd, &file_path_c, flags); | 1463 | return unlinkatZ(dirfd, &file_path_c, flags); |
| ... | @@ -1456,7 +1469,7 @@ pub const unlinkatC = @compileError("deprecated: renamed to unlinkatZ"); | ... | @@ -1456,7 +1469,7 @@ pub const unlinkatC = @compileError("deprecated: renamed to unlinkatZ"); |
| 1456 | pub fn unlinkatZ(dirfd: fd_t, file_path_c: [*:0]const u8, flags: u32) UnlinkatError!void { | 1469 | pub fn unlinkatZ(dirfd: fd_t, file_path_c: [*:0]const u8, flags: u32) UnlinkatError!void { |
| 1457 | if (builtin.os.tag == .windows) { | 1470 | if (builtin.os.tag == .windows) { |
| 1458 | const file_path_w = try windows.cStrToPrefixedFileW(file_path_c); | 1471 | const file_path_w = try windows.cStrToPrefixedFileW(file_path_c); |
| 1459 | return unlinkatW(dirfd, &file_path_w, flags); | 1472 | return unlinkatW(dirfd, file_path_w.span().ptr, flags); |
| 1460 | } | 1473 | } |
| 1461 | switch (errno(system.unlinkat(dirfd, file_path_c, flags))) { | 1474 | switch (errno(system.unlinkat(dirfd, file_path_c, flags))) { |
| 1462 | 0 => return, | 1475 | 0 => return, |
| ... | @@ -1571,7 +1584,7 @@ pub fn rename(old_path: []const u8, new_path: []const u8) RenameError!void { | ... | @@ -1571,7 +1584,7 @@ pub fn rename(old_path: []const u8, new_path: []const u8) RenameError!void { |
| 1571 | if (builtin.os.tag == .windows) { | 1584 | if (builtin.os.tag == .windows) { |
| 1572 | const old_path_w = try windows.sliceToPrefixedFileW(old_path); | 1585 | const old_path_w = try windows.sliceToPrefixedFileW(old_path); |
| 1573 | const new_path_w = try windows.sliceToPrefixedFileW(new_path); | 1586 | const new_path_w = try windows.sliceToPrefixedFileW(new_path); |
| 1574 | return renameW(&old_path_w, &new_path_w); | 1587 | return renameW(old_path_w.span().ptr, new_path_w.span().ptr); |
| 1575 | } else { | 1588 | } else { |
| 1576 | const old_path_c = try toPosixPath(old_path); | 1589 | const old_path_c = try toPosixPath(old_path); |
| 1577 | const new_path_c = try toPosixPath(new_path); | 1590 | const new_path_c = try toPosixPath(new_path); |
| ... | @@ -1586,7 +1599,7 @@ pub fn renameZ(old_path: [*:0]const u8, new_path: [*:0]const u8) RenameError!voi | ... | @@ -1586,7 +1599,7 @@ pub fn renameZ(old_path: [*:0]const u8, new_path: [*:0]const u8) RenameError!voi |
| 1586 | if (builtin.os.tag == .windows) { | 1599 | if (builtin.os.tag == .windows) { |
| 1587 | const old_path_w = try windows.cStrToPrefixedFileW(old_path); | 1600 | const old_path_w = try windows.cStrToPrefixedFileW(old_path); |
| 1588 | const new_path_w = try windows.cStrToPrefixedFileW(new_path); | 1601 | const new_path_w = try windows.cStrToPrefixedFileW(new_path); |
| 1589 | return renameW(&old_path_w, &new_path_w); | 1602 | return renameW(old_path_w.span().ptr, new_path_w.span().ptr); |
| 1590 | } | 1603 | } |
| 1591 | switch (errno(system.rename(old_path, new_path))) { | 1604 | switch (errno(system.rename(old_path, new_path))) { |
| 1592 | 0 => return, | 1605 | 0 => return, |
| ... | @@ -1629,7 +1642,7 @@ pub fn renameat( | ... | @@ -1629,7 +1642,7 @@ pub fn renameat( |
| 1629 | if (builtin.os.tag == .windows) { | 1642 | if (builtin.os.tag == .windows) { |
| 1630 | const old_path_w = try windows.sliceToPrefixedFileW(old_path); | 1643 | const old_path_w = try windows.sliceToPrefixedFileW(old_path); |
| 1631 | const new_path_w = try windows.sliceToPrefixedFileW(new_path); | 1644 | const new_path_w = try windows.sliceToPrefixedFileW(new_path); |
| 1632 | return renameatW(old_dir_fd, &old_path_w, new_dir_fd, &new_path_w, windows.TRUE); | 1645 | return renameatW(old_dir_fd, old_path_w.span(), new_dir_fd, new_path_w.span(), windows.TRUE); |
| 1633 | } else { | 1646 | } else { |
| 1634 | const old_path_c = try toPosixPath(old_path); | 1647 | const old_path_c = try toPosixPath(old_path); |
| 1635 | const new_path_c = try toPosixPath(new_path); | 1648 | const new_path_c = try toPosixPath(new_path); |
| ... | @@ -1647,7 +1660,7 @@ pub fn renameatZ( | ... | @@ -1647,7 +1660,7 @@ pub fn renameatZ( |
| 1647 | if (builtin.os.tag == .windows) { | 1660 | if (builtin.os.tag == .windows) { |
| 1648 | const old_path_w = try windows.cStrToPrefixedFileW(old_path); | 1661 | const old_path_w = try windows.cStrToPrefixedFileW(old_path); |
| 1649 | const new_path_w = try windows.cStrToPrefixedFileW(new_path); | 1662 | const new_path_w = try windows.cStrToPrefixedFileW(new_path); |
| 1650 | return renameatW(old_dir_fd, &old_path_w, new_dir_fd, &new_path_w, windows.TRUE); | 1663 | return renameatW(old_dir_fd, old_path_w.span(), new_dir_fd, new_path_w.span(), windows.TRUE); |
| 1651 | } | 1664 | } |
| 1652 | 1665 | ||
| 1653 | switch (errno(system.renameat(old_dir_fd, old_path, new_dir_fd, new_path))) { | 1666 | switch (errno(system.renameat(old_dir_fd, old_path, new_dir_fd, new_path))) { |
| ... | @@ -1674,38 +1687,40 @@ pub fn renameatZ( | ... | @@ -1674,38 +1687,40 @@ pub fn renameatZ( |
| 1674 | } | 1687 | } |
| 1675 | } | 1688 | } |
| 1676 | 1689 | ||
| 1677 | /// Same as `renameat` except the parameters are null-terminated UTF16LE encoded byte arrays. | 1690 | /// Same as `renameat` but Windows-only and the path parameters are |
| 1678 | /// Assumes target is Windows. | 1691 | /// [WTF-16](https://simonsapin.github.io/wtf-8/#potentially-ill-formed-utf-16) encoded. |
| 1679 | /// TODO these args can actually be slices when using ntdll. audit the rest of the W functions too. | ||
| 1680 | pub fn renameatW( | 1692 | pub fn renameatW( |
| 1681 | old_dir_fd: fd_t, | 1693 | old_dir_fd: fd_t, |
| 1682 | old_path: [*:0]const u16, | 1694 | old_path_w: []const u16, |
| 1683 | new_dir_fd: fd_t, | 1695 | new_dir_fd: fd_t, |
| 1684 | new_path_w: [*:0]const u16, | 1696 | new_path_w: []const u16, |
| 1685 | ReplaceIfExists: windows.BOOLEAN, | 1697 | ReplaceIfExists: windows.BOOLEAN, |
| 1686 | ) RenameError!void { | 1698 | ) RenameError!void { |
| 1687 | const access_mask = windows.SYNCHRONIZE | windows.GENERIC_WRITE | windows.DELETE; | 1699 | const src_fd = windows.OpenFile(old_path_w, .{ |
| 1688 | const src_fd = windows.OpenFileW(old_dir_fd, old_path, null, access_mask, null, false, windows.FILE_OPEN) catch |err| switch (err) { | 1700 | .dir = old_dir_fd, |
| 1689 | error.WouldBlock => unreachable, | 1701 | .access_mask = windows.SYNCHRONIZE | windows.GENERIC_WRITE | windows.DELETE, |
| 1702 | .creation = windows.FILE_OPEN, | ||
| 1703 | .io_mode = .blocking, | ||
| 1704 | }) catch |err| switch (err) { | ||
| 1705 | error.WouldBlock => unreachable, // Not possible without `.share_access_nonblocking = true`. | ||
| 1690 | else => |e| return e, | 1706 | else => |e| return e, |
| 1691 | }; | 1707 | }; |
| 1692 | defer windows.CloseHandle(src_fd); | 1708 | defer windows.CloseHandle(src_fd); |
| 1693 | 1709 | ||
| 1694 | const struct_buf_len = @sizeOf(windows.FILE_RENAME_INFORMATION) + (MAX_PATH_BYTES - 1); | 1710 | const struct_buf_len = @sizeOf(windows.FILE_RENAME_INFORMATION) + (MAX_PATH_BYTES - 1); |
| 1695 | var rename_info_buf: [struct_buf_len]u8 align(@alignOf(windows.FILE_RENAME_INFORMATION)) = undefined; | 1711 | var rename_info_buf: [struct_buf_len]u8 align(@alignOf(windows.FILE_RENAME_INFORMATION)) = undefined; |
| 1696 | const new_path = mem.span(new_path_w); | 1712 | const struct_len = @sizeOf(windows.FILE_RENAME_INFORMATION) - 1 + new_path_w.len * 2; |
| 1697 | const struct_len = @sizeOf(windows.FILE_RENAME_INFORMATION) - 1 + new_path.len * 2; | ||
| 1698 | if (struct_len > struct_buf_len) return error.NameTooLong; | 1713 | if (struct_len > struct_buf_len) return error.NameTooLong; |
| 1699 | 1714 | ||
| 1700 | const rename_info = @ptrCast(*windows.FILE_RENAME_INFORMATION, &rename_info_buf); | 1715 | const rename_info = @ptrCast(*windows.FILE_RENAME_INFORMATION, &rename_info_buf); |
| 1701 | 1716 | ||
| 1702 | rename_info.* = .{ | 1717 | rename_info.* = .{ |
| 1703 | .ReplaceIfExists = ReplaceIfExists, | 1718 | .ReplaceIfExists = ReplaceIfExists, |
| 1704 | .RootDirectory = if (std.fs.path.isAbsoluteWindowsW(new_path_w)) null else new_dir_fd, | 1719 | .RootDirectory = if (std.fs.path.isAbsoluteWindowsWTF16(new_path_w)) null else new_dir_fd, |
| 1705 | .FileNameLength = @intCast(u32, new_path.len * 2), // already checked error.NameTooLong | 1720 | .FileNameLength = @intCast(u32, new_path_w.len * 2), // already checked error.NameTooLong |
| 1706 | .FileName = undefined, | 1721 | .FileName = undefined, |
| 1707 | }; | 1722 | }; |
| 1708 | std.mem.copy(u16, @as([*]u16, &rename_info.FileName)[0..new_path.len], new_path); | 1723 | std.mem.copy(u16, @as([*]u16, &rename_info.FileName)[0..new_path_w.len], new_path_w); |
| 1709 | 1724 | ||
| 1710 | var io_status_block: windows.IO_STATUS_BLOCK = undefined; | 1725 | var io_status_block: windows.IO_STATUS_BLOCK = undefined; |
| 1711 | 1726 | ||
| ... | @@ -1749,7 +1764,7 @@ pub const MakeDirError = error{ | ... | @@ -1749,7 +1764,7 @@ pub const MakeDirError = error{ |
| 1749 | pub fn mkdirat(dir_fd: fd_t, sub_dir_path: []const u8, mode: u32) MakeDirError!void { | 1764 | pub fn mkdirat(dir_fd: fd_t, sub_dir_path: []const u8, mode: u32) MakeDirError!void { |
| 1750 | if (builtin.os.tag == .windows) { | 1765 | if (builtin.os.tag == .windows) { |
| 1751 | const sub_dir_path_w = try windows.sliceToPrefixedFileW(sub_dir_path); | 1766 | const sub_dir_path_w = try windows.sliceToPrefixedFileW(sub_dir_path); |
| 1752 | return mkdiratW(dir_fd, &sub_dir_path_w, mode); | 1767 | return mkdiratW(dir_fd, sub_dir_path_w.span().ptr, mode); |
| 1753 | } else { | 1768 | } else { |
| 1754 | const sub_dir_path_c = try toPosixPath(sub_dir_path); | 1769 | const sub_dir_path_c = try toPosixPath(sub_dir_path); |
| 1755 | return mkdiratZ(dir_fd, &sub_dir_path_c, mode); | 1770 | return mkdiratZ(dir_fd, &sub_dir_path_c, mode); |
| ... | @@ -1761,7 +1776,7 @@ pub const mkdiratC = @compileError("deprecated: renamed to mkdiratZ"); | ... | @@ -1761,7 +1776,7 @@ pub const mkdiratC = @compileError("deprecated: renamed to mkdiratZ"); |
| 1761 | pub fn mkdiratZ(dir_fd: fd_t, sub_dir_path: [*:0]const u8, mode: u32) MakeDirError!void { | 1776 | pub fn mkdiratZ(dir_fd: fd_t, sub_dir_path: [*:0]const u8, mode: u32) MakeDirError!void { |
| 1762 | if (builtin.os.tag == .windows) { | 1777 | if (builtin.os.tag == .windows) { |
| 1763 | const sub_dir_path_w = try windows.cStrToPrefixedFileW(sub_dir_path); | 1778 | const sub_dir_path_w = try windows.cStrToPrefixedFileW(sub_dir_path); |
| 1764 | return mkdiratW(dir_fd, &sub_dir_path_w, mode); | 1779 | return mkdiratW(dir_fd, sub_dir_path_w.span().ptr, mode); |
| 1765 | } | 1780 | } |
| 1766 | switch (errno(system.mkdirat(dir_fd, sub_dir_path, mode))) { | 1781 | switch (errno(system.mkdirat(dir_fd, sub_dir_path, mode))) { |
| 1767 | 0 => return, | 1782 | 0 => return, |
| ... | @@ -1805,7 +1820,7 @@ pub fn mkdir(dir_path: []const u8, mode: u32) MakeDirError!void { | ... | @@ -1805,7 +1820,7 @@ pub fn mkdir(dir_path: []const u8, mode: u32) MakeDirError!void { |
| 1805 | pub fn mkdirZ(dir_path: [*:0]const u8, mode: u32) MakeDirError!void { | 1820 | pub fn mkdirZ(dir_path: [*:0]const u8, mode: u32) MakeDirError!void { |
| 1806 | if (builtin.os.tag == .windows) { | 1821 | if (builtin.os.tag == .windows) { |
| 1807 | const dir_path_w = try windows.cStrToPrefixedFileW(dir_path); | 1822 | const dir_path_w = try windows.cStrToPrefixedFileW(dir_path); |
| 1808 | const sub_dir_handle = try windows.CreateDirectoryW(null, &dir_path_w, null); | 1823 | const sub_dir_handle = try windows.CreateDirectoryW(null, dir_path_w.span().ptr, null); |
| 1809 | windows.CloseHandle(sub_dir_handle); | 1824 | windows.CloseHandle(sub_dir_handle); |
| 1810 | return; | 1825 | return; |
| 1811 | } | 1826 | } |
| ... | @@ -1846,7 +1861,7 @@ pub const DeleteDirError = error{ | ... | @@ -1846,7 +1861,7 @@ pub const DeleteDirError = error{ |
| 1846 | pub fn rmdir(dir_path: []const u8) DeleteDirError!void { | 1861 | pub fn rmdir(dir_path: []const u8) DeleteDirError!void { |
| 1847 | if (builtin.os.tag == .windows) { | 1862 | if (builtin.os.tag == .windows) { |
| 1848 | const dir_path_w = try windows.sliceToPrefixedFileW(dir_path); | 1863 | const dir_path_w = try windows.sliceToPrefixedFileW(dir_path); |
| 1849 | return windows.RemoveDirectoryW(&dir_path_w); | 1864 | return windows.RemoveDirectoryW(dir_path_w.span().ptr); |
| 1850 | } else { | 1865 | } else { |
| 1851 | const dir_path_c = try toPosixPath(dir_path); | 1866 | const dir_path_c = try toPosixPath(dir_path); |
| 1852 | return rmdirZ(&dir_path_c); | 1867 | return rmdirZ(&dir_path_c); |
| ... | @@ -1859,7 +1874,7 @@ pub const rmdirC = @compileError("deprecated: renamed to rmdirZ"); | ... | @@ -1859,7 +1874,7 @@ pub const rmdirC = @compileError("deprecated: renamed to rmdirZ"); |
| 1859 | pub fn rmdirZ(dir_path: [*:0]const u8) DeleteDirError!void { | 1874 | pub fn rmdirZ(dir_path: [*:0]const u8) DeleteDirError!void { |
| 1860 | if (builtin.os.tag == .windows) { | 1875 | if (builtin.os.tag == .windows) { |
| 1861 | const dir_path_w = try windows.cStrToPrefixedFileW(dir_path); | 1876 | const dir_path_w = try windows.cStrToPrefixedFileW(dir_path); |
| 1862 | return windows.RemoveDirectoryW(&dir_path_w); | 1877 | return windows.RemoveDirectoryW(dir_path_w.span().ptr); |
| 1863 | } | 1878 | } |
| 1864 | switch (errno(system.rmdir(dir_path))) { | 1879 | switch (errno(system.rmdir(dir_path))) { |
| 1865 | 0 => return, | 1880 | 0 => return, |
| ... | @@ -2869,7 +2884,7 @@ pub const AccessError = error{ | ... | @@ -2869,7 +2884,7 @@ pub const AccessError = error{ |
| 2869 | pub fn access(path: []const u8, mode: u32) AccessError!void { | 2884 | pub fn access(path: []const u8, mode: u32) AccessError!void { |
| 2870 | if (builtin.os.tag == .windows) { | 2885 | if (builtin.os.tag == .windows) { |
| 2871 | const path_w = try windows.sliceToPrefixedFileW(path); | 2886 | const path_w = try windows.sliceToPrefixedFileW(path); |
| 2872 | _ = try windows.GetFileAttributesW(&path_w); | 2887 | _ = try windows.GetFileAttributesW(path_w.span().ptr); |
| 2873 | return; | 2888 | return; |
| 2874 | } | 2889 | } |
| 2875 | const path_c = try toPosixPath(path); | 2890 | const path_c = try toPosixPath(path); |
| ... | @@ -2882,7 +2897,7 @@ pub const accessC = @compileError("Deprecated in favor of `accessZ`"); | ... | @@ -2882,7 +2897,7 @@ pub const accessC = @compileError("Deprecated in favor of `accessZ`"); |
| 2882 | pub fn accessZ(path: [*:0]const u8, mode: u32) AccessError!void { | 2897 | pub fn accessZ(path: [*:0]const u8, mode: u32) AccessError!void { |
| 2883 | if (builtin.os.tag == .windows) { | 2898 | if (builtin.os.tag == .windows) { |
| 2884 | const path_w = try windows.cStrToPrefixedFileW(path); | 2899 | const path_w = try windows.cStrToPrefixedFileW(path); |
| 2885 | _ = try windows.GetFileAttributesW(&path_w); | 2900 | _ = try windows.GetFileAttributesW(path_w.span().ptr); |
| 2886 | return; | 2901 | return; |
| 2887 | } | 2902 | } |
| 2888 | switch (errno(system.access(path, mode))) { | 2903 | switch (errno(system.access(path, mode))) { |
| ... | @@ -2923,7 +2938,7 @@ pub fn accessW(path: [*:0]const u16, mode: u32) windows.GetFileAttributesError!v | ... | @@ -2923,7 +2938,7 @@ pub fn accessW(path: [*:0]const u16, mode: u32) windows.GetFileAttributesError!v |
| 2923 | pub fn faccessat(dirfd: fd_t, path: []const u8, mode: u32, flags: u32) AccessError!void { | 2938 | pub fn faccessat(dirfd: fd_t, path: []const u8, mode: u32, flags: u32) AccessError!void { |
| 2924 | if (builtin.os.tag == .windows) { | 2939 | if (builtin.os.tag == .windows) { |
| 2925 | const path_w = try windows.sliceToPrefixedFileW(path); | 2940 | const path_w = try windows.sliceToPrefixedFileW(path); |
| 2926 | return faccessatW(dirfd, &path_w, mode, flags); | 2941 | return faccessatW(dirfd, path_w.span().ptr, mode, flags); |
| 2927 | } | 2942 | } |
| 2928 | const path_c = try toPosixPath(path); | 2943 | const path_c = try toPosixPath(path); |
| 2929 | return faccessatZ(dirfd, &path_c, mode, flags); | 2944 | return faccessatZ(dirfd, &path_c, mode, flags); |
| ... | @@ -2933,7 +2948,7 @@ pub fn faccessat(dirfd: fd_t, path: []const u8, mode: u32, flags: u32) AccessErr | ... | @@ -2933,7 +2948,7 @@ pub fn faccessat(dirfd: fd_t, path: []const u8, mode: u32, flags: u32) AccessErr |
| 2933 | pub fn faccessatZ(dirfd: fd_t, path: [*:0]const u8, mode: u32, flags: u32) AccessError!void { | 2948 | pub fn faccessatZ(dirfd: fd_t, path: [*:0]const u8, mode: u32, flags: u32) AccessError!void { |
| 2934 | if (builtin.os.tag == .windows) { | 2949 | if (builtin.os.tag == .windows) { |
| 2935 | const path_w = try windows.cStrToPrefixedFileW(path); | 2950 | const path_w = try windows.cStrToPrefixedFileW(path); |
| 2936 | return faccessatW(dirfd, &path_w, mode, flags); | 2951 | return faccessatW(dirfd, path_w.span().ptr, mode, flags); |
| 2937 | } | 2952 | } |
| 2938 | switch (errno(system.faccessat(dirfd, path, mode, flags))) { | 2953 | switch (errno(system.faccessat(dirfd, path, mode, flags))) { |
| 2939 | 0 => return, | 2954 | 0 => return, |
| ... | @@ -3288,7 +3303,7 @@ pub const RealPathError = error{ | ... | @@ -3288,7 +3303,7 @@ pub const RealPathError = error{ |
| 3288 | pub fn realpath(pathname: []const u8, out_buffer: *[MAX_PATH_BYTES]u8) RealPathError![]u8 { | 3303 | pub fn realpath(pathname: []const u8, out_buffer: *[MAX_PATH_BYTES]u8) RealPathError![]u8 { |
| 3289 | if (builtin.os.tag == .windows) { | 3304 | if (builtin.os.tag == .windows) { |
| 3290 | const pathname_w = try windows.sliceToPrefixedFileW(pathname); | 3305 | const pathname_w = try windows.sliceToPrefixedFileW(pathname); |
| 3291 | return realpathW(&pathname_w, out_buffer); | 3306 | return realpathW(pathname_w.span().ptr, out_buffer); |
| 3292 | } | 3307 | } |
| 3293 | const pathname_c = try toPosixPath(pathname); | 3308 | const pathname_c = try toPosixPath(pathname); |
| 3294 | return realpathZ(&pathname_c, out_buffer); | 3309 | return realpathZ(&pathname_c, out_buffer); |
| ... | @@ -3300,7 +3315,7 @@ pub const realpathC = @compileError("deprecated: renamed realpathZ"); | ... | @@ -3300,7 +3315,7 @@ pub const realpathC = @compileError("deprecated: renamed realpathZ"); |
| 3300 | pub fn realpathZ(pathname: [*:0]const u8, out_buffer: *[MAX_PATH_BYTES]u8) RealPathError![]u8 { | 3315 | pub fn realpathZ(pathname: [*:0]const u8, out_buffer: *[MAX_PATH_BYTES]u8) RealPathError![]u8 { |
| 3301 | if (builtin.os.tag == .windows) { | 3316 | if (builtin.os.tag == .windows) { |
| 3302 | const pathname_w = try windows.cStrToPrefixedFileW(pathname); | 3317 | const pathname_w = try windows.cStrToPrefixedFileW(pathname); |
| 3303 | return realpathW(&pathname_w, out_buffer); | 3318 | return realpathW(pathname_w.span().ptr, out_buffer); |
| 3304 | } | 3319 | } |
| 3305 | if (builtin.os.tag == .linux and !builtin.link_libc) { | 3320 | if (builtin.os.tag == .linux and !builtin.link_libc) { |
| 3306 | const fd = openZ(pathname, linux.O_PATH | linux.O_NONBLOCK | linux.O_CLOEXEC, 0) catch |err| switch (err) { | 3321 | const fd = openZ(pathname, linux.O_PATH | linux.O_NONBLOCK | linux.O_CLOEXEC, 0) catch |err| switch (err) { |
lib/std/os/windows.zig+110-116| ... | @@ -59,7 +59,7 @@ pub fn CreateFile( | ... | @@ -59,7 +59,7 @@ pub fn CreateFile( |
| 59 | hTemplateFile: ?HANDLE, | 59 | hTemplateFile: ?HANDLE, |
| 60 | ) CreateFileError!HANDLE { | 60 | ) CreateFileError!HANDLE { |
| 61 | const file_path_w = try sliceToPrefixedFileW(file_path); | 61 | const file_path_w = try sliceToPrefixedFileW(file_path); |
| 62 | return CreateFileW(&file_path_w, desired_access, share_mode, lpSecurityAttributes, creation_disposition, flags_and_attrs, hTemplateFile); | 62 | return CreateFileW(file_path_w.span().ptr, desired_access, share_mode, lpSecurityAttributes, creation_disposition, flags_and_attrs, hTemplateFile); |
| 63 | } | 63 | } |
| 64 | 64 | ||
| 65 | pub fn CreateFileW( | 65 | pub fn CreateFileW( |
| ... | @@ -103,57 +103,59 @@ pub const OpenError = error{ | ... | @@ -103,57 +103,59 @@ pub const OpenError = error{ |
| 103 | WouldBlock, | 103 | WouldBlock, |
| 104 | }; | 104 | }; |
| 105 | 105 | ||
| 106 | /// TODO rename to CreateFileW | 106 | pub const OpenFileOptions = struct { |
| 107 | /// TODO actually we don't need the path parameter to be null terminated | ||
| 108 | pub fn OpenFileW( | ||
| 109 | dir: ?HANDLE, | ||
| 110 | sub_path_w: [*:0]const u16, | ||
| 111 | sa: ?*SECURITY_ATTRIBUTES, | ||
| 112 | access_mask: ACCESS_MASK, | 107 | access_mask: ACCESS_MASK, |
| 113 | share_access_opt: ?ULONG, | 108 | dir: ?HANDLE = null, |
| 114 | share_access_nonblocking: bool, | 109 | sa: ?*SECURITY_ATTRIBUTES = null, |
| 110 | share_access: ULONG = FILE_SHARE_WRITE | FILE_SHARE_READ | FILE_SHARE_DELETE, | ||
| 111 | share_access_nonblocking: bool = false, | ||
| 115 | creation: ULONG, | 112 | creation: ULONG, |
| 116 | ) OpenError!HANDLE { | 113 | io_mode: std.io.ModeOverride, |
| 117 | if (sub_path_w[0] == '.' and sub_path_w[1] == 0) { | 114 | }; |
| 115 | |||
| 116 | /// TODO when share_access_nonblocking is false, this implementation uses | ||
| 117 | /// untinterruptible sleep() to block. This is not the final iteration of the API. | ||
| 118 | pub fn OpenFile(sub_path_w: []const u16, options: OpenFileOptions) OpenError!HANDLE { | ||
| 119 | if (mem.eql(u16, sub_path_w, &[_]u16{'.'})) { | ||
| 118 | return error.IsDir; | 120 | return error.IsDir; |
| 119 | } | 121 | } |
| 120 | if (sub_path_w[0] == '.' and sub_path_w[1] == '.' and sub_path_w[2] == 0) { | 122 | if (mem.eql(u16, sub_path_w, &[_]u16{ '.', '.' })) { |
| 121 | return error.IsDir; | 123 | return error.IsDir; |
| 122 | } | 124 | } |
| 123 | 125 | ||
| 124 | var result: HANDLE = undefined; | 126 | var result: HANDLE = undefined; |
| 125 | 127 | ||
| 126 | const path_len_bytes = math.cast(u16, mem.lenZ(sub_path_w) * 2) catch |err| switch (err) { | 128 | const path_len_bytes = math.cast(u16, sub_path_w.len * 2) catch |err| switch (err) { |
| 127 | error.Overflow => return error.NameTooLong, | 129 | error.Overflow => return error.NameTooLong, |
| 128 | }; | 130 | }; |
| 129 | var nt_name = UNICODE_STRING{ | 131 | var nt_name = UNICODE_STRING{ |
| 130 | .Length = path_len_bytes, | 132 | .Length = path_len_bytes, |
| 131 | .MaximumLength = path_len_bytes, | 133 | .MaximumLength = path_len_bytes, |
| 132 | .Buffer = @intToPtr([*]u16, @ptrToInt(sub_path_w)), | 134 | .Buffer = @intToPtr([*]u16, @ptrToInt(sub_path_w.ptr)), |
| 133 | }; | 135 | }; |
| 134 | var attr = OBJECT_ATTRIBUTES{ | 136 | var attr = OBJECT_ATTRIBUTES{ |
| 135 | .Length = @sizeOf(OBJECT_ATTRIBUTES), | 137 | .Length = @sizeOf(OBJECT_ATTRIBUTES), |
| 136 | .RootDirectory = if (std.fs.path.isAbsoluteWindowsW(sub_path_w)) null else dir, | 138 | .RootDirectory = if (std.fs.path.isAbsoluteWindowsWTF16(sub_path_w)) null else options.dir, |
| 137 | .Attributes = 0, // Note we do not use OBJ_CASE_INSENSITIVE here. | 139 | .Attributes = 0, // Note we do not use OBJ_CASE_INSENSITIVE here. |
| 138 | .ObjectName = &nt_name, | 140 | .ObjectName = &nt_name, |
| 139 | .SecurityDescriptor = if (sa) |ptr| ptr.lpSecurityDescriptor else null, | 141 | .SecurityDescriptor = if (options.sa) |ptr| ptr.lpSecurityDescriptor else null, |
| 140 | .SecurityQualityOfService = null, | 142 | .SecurityQualityOfService = null, |
| 141 | }; | 143 | }; |
| 142 | var io: IO_STATUS_BLOCK = undefined; | 144 | var io: IO_STATUS_BLOCK = undefined; |
| 143 | const share_access = share_access_opt orelse (FILE_SHARE_WRITE | FILE_SHARE_READ | FILE_SHARE_DELETE); | ||
| 144 | 145 | ||
| 145 | var delay: usize = 1; | 146 | var delay: usize = 1; |
| 146 | while (true) { | 147 | while (true) { |
| 148 | const blocking_flag: ULONG = if (options.io_mode == .blocking) FILE_SYNCHRONOUS_IO_NONALERT else 0; | ||
| 147 | const rc = ntdll.NtCreateFile( | 149 | const rc = ntdll.NtCreateFile( |
| 148 | &result, | 150 | &result, |
| 149 | access_mask, | 151 | options.access_mask, |
| 150 | &attr, | 152 | &attr, |
| 151 | &io, | 153 | &io, |
| 152 | null, | 154 | null, |
| 153 | FILE_ATTRIBUTE_NORMAL, | 155 | FILE_ATTRIBUTE_NORMAL, |
| 154 | share_access, | 156 | options.share_access, |
| 155 | creation, | 157 | options.creation, |
| 156 | FILE_NON_DIRECTORY_FILE | FILE_SYNCHRONOUS_IO_NONALERT, | 158 | FILE_NON_DIRECTORY_FILE | blocking_flag, |
| 157 | null, | 159 | null, |
| 158 | 0, | 160 | 0, |
| 159 | ); | 161 | ); |
| ... | @@ -165,14 +167,16 @@ pub fn OpenFileW( | ... | @@ -165,14 +167,16 @@ pub fn OpenFileW( |
| 165 | .NO_MEDIA_IN_DEVICE => return error.NoDevice, | 167 | .NO_MEDIA_IN_DEVICE => return error.NoDevice, |
| 166 | .INVALID_PARAMETER => unreachable, | 168 | .INVALID_PARAMETER => unreachable, |
| 167 | .SHARING_VIOLATION => { | 169 | .SHARING_VIOLATION => { |
| 168 | if (share_access_nonblocking) { | 170 | if (options.share_access_nonblocking) { |
| 169 | return error.WouldBlock; | 171 | return error.WouldBlock; |
| 170 | } | 172 | } |
| 173 | // TODO sleep in a way that is interruptable | ||
| 174 | // TODO integrate with async I/O | ||
| 171 | std.time.sleep(delay); | 175 | std.time.sleep(delay); |
| 172 | if (delay < 1 * std.time.ns_per_s) { | 176 | if (delay < 1 * std.time.ns_per_s) { |
| 173 | delay *= 2; | 177 | delay *= 2; |
| 174 | } | 178 | } |
| 175 | continue; // TODO: don't loop for async | 179 | continue; |
| 176 | }, | 180 | }, |
| 177 | .ACCESS_DENIED => return error.AccessDenied, | 181 | .ACCESS_DENIED => return error.AccessDenied, |
| 178 | .PIPE_BUSY => return error.PipeBusy, | 182 | .PIPE_BUSY => return error.PipeBusy, |
| ... | @@ -195,7 +199,7 @@ pub fn CreatePipe(rd: *HANDLE, wr: *HANDLE, sattr: *const SECURITY_ATTRIBUTES) C | ... | @@ -195,7 +199,7 @@ pub fn CreatePipe(rd: *HANDLE, wr: *HANDLE, sattr: *const SECURITY_ATTRIBUTES) C |
| 195 | 199 | ||
| 196 | pub fn CreateEventEx(attributes: ?*SECURITY_ATTRIBUTES, name: []const u8, flags: DWORD, desired_access: DWORD) !HANDLE { | 200 | pub fn CreateEventEx(attributes: ?*SECURITY_ATTRIBUTES, name: []const u8, flags: DWORD, desired_access: DWORD) !HANDLE { |
| 197 | const nameW = try sliceToPrefixedFileW(name); | 201 | const nameW = try sliceToPrefixedFileW(name); |
| 198 | return CreateEventExW(attributes, &nameW, flags, desired_access); | 202 | return CreateEventExW(attributes, nameW.span().ptr, flags, desired_access); |
| 199 | } | 203 | } |
| 200 | 204 | ||
| 201 | pub fn CreateEventExW(attributes: ?*SECURITY_ATTRIBUTES, nameW: [*:0]const u16, flags: DWORD, desired_access: DWORD) !HANDLE { | 205 | pub fn CreateEventExW(attributes: ?*SECURITY_ATTRIBUTES, nameW: [*:0]const u16, flags: DWORD, desired_access: DWORD) !HANDLE { |
| ... | @@ -328,42 +332,6 @@ pub fn WaitForMultipleObjectsEx(handles: []const HANDLE, waitAll: bool, millisec | ... | @@ -328,42 +332,6 @@ pub fn WaitForMultipleObjectsEx(handles: []const HANDLE, waitAll: bool, millisec |
| 328 | } | 332 | } |
| 329 | } | 333 | } |
| 330 | 334 | ||
| 331 | pub const FindFirstFileError = error{ | ||
| 332 | FileNotFound, | ||
| 333 | InvalidUtf8, | ||
| 334 | BadPathName, | ||
| 335 | NameTooLong, | ||
| 336 | Unexpected, | ||
| 337 | }; | ||
| 338 | |||
| 339 | pub fn FindFirstFile(dir_path: []const u8, find_file_data: *WIN32_FIND_DATAW) FindFirstFileError!HANDLE { | ||
| 340 | const dir_path_w = try sliceToPrefixedSuffixedFileW(dir_path, [_]u16{ '\\', '*' }); | ||
| 341 | const handle = kernel32.FindFirstFileW(&dir_path_w, find_file_data); | ||
| 342 | |||
| 343 | if (handle == INVALID_HANDLE_VALUE) { | ||
| 344 | switch (kernel32.GetLastError()) { | ||
| 345 | .FILE_NOT_FOUND => return error.FileNotFound, | ||
| 346 | .PATH_NOT_FOUND => return error.FileNotFound, | ||
| 347 | else => |err| return unexpectedError(err), | ||
| 348 | } | ||
| 349 | } | ||
| 350 | |||
| 351 | return handle; | ||
| 352 | } | ||
| 353 | |||
| 354 | pub const FindNextFileError = error{Unexpected}; | ||
| 355 | |||
| 356 | /// Returns `true` if there was another file, `false` otherwise. | ||
| 357 | pub fn FindNextFile(handle: HANDLE, find_file_data: *WIN32_FIND_DATAW) FindNextFileError!bool { | ||
| 358 | if (kernel32.FindNextFileW(handle, find_file_data) == 0) { | ||
| 359 | switch (kernel32.GetLastError()) { | ||
| 360 | .NO_MORE_FILES => return false, | ||
| 361 | else => |err| return unexpectedError(err), | ||
| 362 | } | ||
| 363 | } | ||
| 364 | return true; | ||
| 365 | } | ||
| 366 | |||
| 367 | pub const CreateIoCompletionPortError = error{Unexpected}; | 335 | pub const CreateIoCompletionPortError = error{Unexpected}; |
| 368 | 336 | ||
| 369 | pub fn CreateIoCompletionPort( | 337 | pub fn CreateIoCompletionPort( |
| ... | @@ -447,10 +415,11 @@ pub const ReadFileError = error{ | ... | @@ -447,10 +415,11 @@ pub const ReadFileError = error{ |
| 447 | 415 | ||
| 448 | /// If buffer's length exceeds what a Windows DWORD integer can hold, it will be broken into | 416 | /// If buffer's length exceeds what a Windows DWORD integer can hold, it will be broken into |
| 449 | /// multiple non-atomic reads. | 417 | /// multiple non-atomic reads. |
| 450 | pub fn ReadFile(in_hFile: HANDLE, buffer: []u8, offset: ?u64) ReadFileError!usize { | 418 | pub fn ReadFile(in_hFile: HANDLE, buffer: []u8, offset: ?u64, io_mode: std.io.ModeOverride) ReadFileError!usize { |
| 451 | if (std.event.Loop.instance) |loop| { | 419 | if (io_mode != .blocking) { |
| 452 | // TODO support async ReadFile with no offset | 420 | const loop = std.event.Loop.instance.?; |
| 453 | const off = offset.?; | 421 | // TODO make getting the file position non-blocking |
| 422 | const off = if (offset) |o| o else try SetFilePointerEx_CURRENT_get(in_hFile); | ||
| 454 | var resume_node = std.event.Loop.ResumeNode.Basic{ | 423 | var resume_node = std.event.Loop.ResumeNode.Basic{ |
| 455 | .base = .{ | 424 | .base = .{ |
| 456 | .id = .Basic, | 425 | .id = .Basic, |
| ... | @@ -465,22 +434,27 @@ pub fn ReadFile(in_hFile: HANDLE, buffer: []u8, offset: ?u64) ReadFileError!usiz | ... | @@ -465,22 +434,27 @@ pub fn ReadFile(in_hFile: HANDLE, buffer: []u8, offset: ?u64) ReadFileError!usiz |
| 465 | }, | 434 | }, |
| 466 | }; | 435 | }; |
| 467 | // TODO only call create io completion port once per fd | 436 | // TODO only call create io completion port once per fd |
| 468 | _ = windows.CreateIoCompletionPort(fd, loop.os_data.io_port, undefined, undefined) catch undefined; | 437 | _ = CreateIoCompletionPort(in_hFile, loop.os_data.io_port, undefined, undefined) catch undefined; |
| 469 | loop.beginOneEvent(); | 438 | loop.beginOneEvent(); |
| 470 | suspend { | 439 | suspend { |
| 471 | // TODO handle buffer bigger than DWORD can hold | 440 | // TODO handle buffer bigger than DWORD can hold |
| 472 | _ = windows.kernel32.ReadFile(fd, buffer.ptr, @intCast(windows.DWORD, buffer.len), null, &resume_node.base.overlapped); | 441 | _ = kernel32.ReadFile(in_hFile, buffer.ptr, @intCast(DWORD, buffer.len), null, &resume_node.base.overlapped); |
| 473 | } | 442 | } |
| 474 | var bytes_transferred: windows.DWORD = undefined; | 443 | var bytes_transferred: DWORD = undefined; |
| 475 | if (windows.kernel32.GetOverlappedResult(fd, &resume_node.base.overlapped, &bytes_transferred, windows.FALSE) == 0) { | 444 | if (kernel32.GetOverlappedResult(in_hFile, &resume_node.base.overlapped, &bytes_transferred, FALSE) == 0) { |
| 476 | switch (windows.kernel32.GetLastError()) { | 445 | switch (kernel32.GetLastError()) { |
| 477 | .IO_PENDING => unreachable, | 446 | .IO_PENDING => unreachable, |
| 478 | .OPERATION_ABORTED => return error.OperationAborted, | 447 | .OPERATION_ABORTED => return error.OperationAborted, |
| 479 | .BROKEN_PIPE => return error.BrokenPipe, | 448 | .BROKEN_PIPE => return error.BrokenPipe, |
| 480 | .HANDLE_EOF => return @as(usize, bytes_transferred), | 449 | .HANDLE_EOF => return @as(usize, bytes_transferred), |
| 481 | else => |err| return windows.unexpectedError(err), | 450 | else => |err| return unexpectedError(err), |
| 482 | } | 451 | } |
| 483 | } | 452 | } |
| 453 | if (offset == null) { | ||
| 454 | // TODO make setting the file position non-blocking | ||
| 455 | const new_off = off + bytes_transferred; | ||
| 456 | try SetFilePointerEx_CURRENT(in_hFile, @bitCast(i64, new_off)); | ||
| 457 | } | ||
| 484 | return @as(usize, bytes_transferred); | 458 | return @as(usize, bytes_transferred); |
| 485 | } else { | 459 | } else { |
| 486 | var index: usize = 0; | 460 | var index: usize = 0; |
| ... | @@ -520,10 +494,16 @@ pub const WriteFileError = error{ | ... | @@ -520,10 +494,16 @@ pub const WriteFileError = error{ |
| 520 | Unexpected, | 494 | Unexpected, |
| 521 | }; | 495 | }; |
| 522 | 496 | ||
| 523 | pub fn WriteFile(handle: HANDLE, bytes: []const u8, offset: ?u64) WriteFileError!usize { | 497 | pub fn WriteFile( |
| 524 | if (std.event.Loop.instance) |loop| { | 498 | handle: HANDLE, |
| 525 | // TODO support async WriteFile with no offset | 499 | bytes: []const u8, |
| 526 | const off = offset.?; | 500 | offset: ?u64, |
| 501 | io_mode: std.io.ModeOverride, | ||
| 502 | ) WriteFileError!usize { | ||
| 503 | if (std.event.Loop.instance != null and io_mode != .blocking) { | ||
| 504 | const loop = std.event.Loop.instance.?; | ||
| 505 | // TODO make getting the file position non-blocking | ||
| 506 | const off = if (offset) |o| o else try SetFilePointerEx_CURRENT_get(handle); | ||
| 527 | var resume_node = std.event.Loop.ResumeNode.Basic{ | 507 | var resume_node = std.event.Loop.ResumeNode.Basic{ |
| 528 | .base = .{ | 508 | .base = .{ |
| 529 | .id = .Basic, | 509 | .id = .Basic, |
| ... | @@ -538,14 +518,14 @@ pub fn WriteFile(handle: HANDLE, bytes: []const u8, offset: ?u64) WriteFileError | ... | @@ -538,14 +518,14 @@ pub fn WriteFile(handle: HANDLE, bytes: []const u8, offset: ?u64) WriteFileError |
| 538 | }, | 518 | }, |
| 539 | }; | 519 | }; |
| 540 | // TODO only call create io completion port once per fd | 520 | // TODO only call create io completion port once per fd |
| 541 | _ = CreateIoCompletionPort(fd, loop.os_data.io_port, undefined, undefined); | 521 | _ = CreateIoCompletionPort(handle, loop.os_data.io_port, undefined, undefined) catch undefined; |
| 542 | loop.beginOneEvent(); | 522 | loop.beginOneEvent(); |
| 543 | suspend { | 523 | suspend { |
| 544 | const adjusted_len = math.cast(windows.DWORD, bytes.len) catch maxInt(windows.DWORD); | 524 | const adjusted_len = math.cast(DWORD, bytes.len) catch maxInt(DWORD); |
| 545 | _ = kernel32.WriteFile(fd, bytes.ptr, adjusted_len, null, &resume_node.base.overlapped); | 525 | _ = kernel32.WriteFile(handle, bytes.ptr, adjusted_len, null, &resume_node.base.overlapped); |
| 546 | } | 526 | } |
| 547 | var bytes_transferred: windows.DWORD = undefined; | 527 | var bytes_transferred: DWORD = undefined; |
| 548 | if (kernel32.GetOverlappedResult(fd, &resume_node.base.overlapped, &bytes_transferred, FALSE) == 0) { | 528 | if (kernel32.GetOverlappedResult(handle, &resume_node.base.overlapped, &bytes_transferred, FALSE) == 0) { |
| 549 | switch (kernel32.GetLastError()) { | 529 | switch (kernel32.GetLastError()) { |
| 550 | .IO_PENDING => unreachable, | 530 | .IO_PENDING => unreachable, |
| 551 | .INVALID_USER_BUFFER => return error.SystemResources, | 531 | .INVALID_USER_BUFFER => return error.SystemResources, |
| ... | @@ -553,9 +533,14 @@ pub fn WriteFile(handle: HANDLE, bytes: []const u8, offset: ?u64) WriteFileError | ... | @@ -553,9 +533,14 @@ pub fn WriteFile(handle: HANDLE, bytes: []const u8, offset: ?u64) WriteFileError |
| 553 | .OPERATION_ABORTED => return error.OperationAborted, | 533 | .OPERATION_ABORTED => return error.OperationAborted, |
| 554 | .NOT_ENOUGH_QUOTA => return error.SystemResources, | 534 | .NOT_ENOUGH_QUOTA => return error.SystemResources, |
| 555 | .BROKEN_PIPE => return error.BrokenPipe, | 535 | .BROKEN_PIPE => return error.BrokenPipe, |
| 556 | else => |err| return windows.unexpectedError(err), | 536 | else => |err| return unexpectedError(err), |
| 557 | } | 537 | } |
| 558 | } | 538 | } |
| 539 | if (offset == null) { | ||
| 540 | // TODO make setting the file position non-blocking | ||
| 541 | const new_off = off + bytes_transferred; | ||
| 542 | try SetFilePointerEx_CURRENT(handle, @bitCast(i64, new_off)); | ||
| 543 | } | ||
| 559 | return bytes_transferred; | 544 | return bytes_transferred; |
| 560 | } else { | 545 | } else { |
| 561 | var bytes_written: DWORD = undefined; | 546 | var bytes_written: DWORD = undefined; |
| ... | @@ -623,7 +608,7 @@ pub fn CreateSymbolicLink( | ... | @@ -623,7 +608,7 @@ pub fn CreateSymbolicLink( |
| 623 | ) CreateSymbolicLinkError!void { | 608 | ) CreateSymbolicLinkError!void { |
| 624 | const sym_link_path_w = try sliceToPrefixedFileW(sym_link_path); | 609 | const sym_link_path_w = try sliceToPrefixedFileW(sym_link_path); |
| 625 | const target_path_w = try sliceToPrefixedFileW(target_path); | 610 | const target_path_w = try sliceToPrefixedFileW(target_path); |
| 626 | return CreateSymbolicLinkW(&sym_link_path_w, &target_path_w, flags); | 611 | return CreateSymbolicLinkW(sym_link_path_w.span().ptr, target_path_w.span().ptr, flags); |
| 627 | } | 612 | } |
| 628 | 613 | ||
| 629 | pub fn CreateSymbolicLinkW( | 614 | pub fn CreateSymbolicLinkW( |
| ... | @@ -648,7 +633,7 @@ pub const DeleteFileError = error{ | ... | @@ -648,7 +633,7 @@ pub const DeleteFileError = error{ |
| 648 | 633 | ||
| 649 | pub fn DeleteFile(filename: []const u8) DeleteFileError!void { | 634 | pub fn DeleteFile(filename: []const u8) DeleteFileError!void { |
| 650 | const filename_w = try sliceToPrefixedFileW(filename); | 635 | const filename_w = try sliceToPrefixedFileW(filename); |
| 651 | return DeleteFileW(&filename_w); | 636 | return DeleteFileW(filename_w.span().ptr); |
| 652 | } | 637 | } |
| 653 | 638 | ||
| 654 | pub fn DeleteFileW(filename: [*:0]const u16) DeleteFileError!void { | 639 | pub fn DeleteFileW(filename: [*:0]const u16) DeleteFileError!void { |
| ... | @@ -670,7 +655,7 @@ pub const MoveFileError = error{Unexpected}; | ... | @@ -670,7 +655,7 @@ pub const MoveFileError = error{Unexpected}; |
| 670 | pub fn MoveFileEx(old_path: []const u8, new_path: []const u8, flags: DWORD) MoveFileError!void { | 655 | pub fn MoveFileEx(old_path: []const u8, new_path: []const u8, flags: DWORD) MoveFileError!void { |
| 671 | const old_path_w = try sliceToPrefixedFileW(old_path); | 656 | const old_path_w = try sliceToPrefixedFileW(old_path); |
| 672 | const new_path_w = try sliceToPrefixedFileW(new_path); | 657 | const new_path_w = try sliceToPrefixedFileW(new_path); |
| 673 | return MoveFileExW(&old_path_w, &new_path_w, flags); | 658 | return MoveFileExW(old_path_w.span().ptr, new_path_w.span().ptr, flags); |
| 674 | } | 659 | } |
| 675 | 660 | ||
| 676 | pub fn MoveFileExW(old_path: [*:0]const u16, new_path: [*:0]const u16, flags: DWORD) MoveFileError!void { | 661 | pub fn MoveFileExW(old_path: [*:0]const u16, new_path: [*:0]const u16, flags: DWORD) MoveFileError!void { |
| ... | @@ -695,7 +680,7 @@ pub const CreateDirectoryError = error{ | ... | @@ -695,7 +680,7 @@ pub const CreateDirectoryError = error{ |
| 695 | /// Returns an open directory handle which the caller is responsible for closing with `CloseHandle`. | 680 | /// Returns an open directory handle which the caller is responsible for closing with `CloseHandle`. |
| 696 | pub fn CreateDirectory(dir: ?HANDLE, pathname: []const u8, sa: ?*SECURITY_ATTRIBUTES) CreateDirectoryError!HANDLE { | 681 | pub fn CreateDirectory(dir: ?HANDLE, pathname: []const u8, sa: ?*SECURITY_ATTRIBUTES) CreateDirectoryError!HANDLE { |
| 697 | const pathname_w = try sliceToPrefixedFileW(pathname); | 682 | const pathname_w = try sliceToPrefixedFileW(pathname); |
| 698 | return CreateDirectoryW(dir, &pathname_w, sa); | 683 | return CreateDirectoryW(dir, pathname_w.span().ptr, sa); |
| 699 | } | 684 | } |
| 700 | 685 | ||
| 701 | /// Same as `CreateDirectory` except takes a WTF-16 encoded path. | 686 | /// Same as `CreateDirectory` except takes a WTF-16 encoded path. |
| ... | @@ -763,7 +748,7 @@ pub const RemoveDirectoryError = error{ | ... | @@ -763,7 +748,7 @@ pub const RemoveDirectoryError = error{ |
| 763 | 748 | ||
| 764 | pub fn RemoveDirectory(dir_path: []const u8) RemoveDirectoryError!void { | 749 | pub fn RemoveDirectory(dir_path: []const u8) RemoveDirectoryError!void { |
| 765 | const dir_path_w = try sliceToPrefixedFileW(dir_path); | 750 | const dir_path_w = try sliceToPrefixedFileW(dir_path); |
| 766 | return RemoveDirectoryW(&dir_path_w); | 751 | return RemoveDirectoryW(dir_path_w.span().ptr); |
| 767 | } | 752 | } |
| 768 | 753 | ||
| 769 | pub fn RemoveDirectoryW(dir_path_w: [*:0]const u16) RemoveDirectoryError!void { | 754 | pub fn RemoveDirectoryW(dir_path_w: [*:0]const u16) RemoveDirectoryError!void { |
| ... | @@ -892,7 +877,7 @@ pub const GetFileAttributesError = error{ | ... | @@ -892,7 +877,7 @@ pub const GetFileAttributesError = error{ |
| 892 | 877 | ||
| 893 | pub fn GetFileAttributes(filename: []const u8) GetFileAttributesError!DWORD { | 878 | pub fn GetFileAttributes(filename: []const u8) GetFileAttributesError!DWORD { |
| 894 | const filename_w = try sliceToPrefixedFileW(filename); | 879 | const filename_w = try sliceToPrefixedFileW(filename); |
| 895 | return GetFileAttributesW(&filename_w); | 880 | return GetFileAttributesW(filename_w.span().ptr); |
| 896 | } | 881 | } |
| 897 | 882 | ||
| 898 | pub fn GetFileAttributesW(lpFileName: [*:0]const u16) GetFileAttributesError!DWORD { | 883 | pub fn GetFileAttributesW(lpFileName: [*:0]const u16) GetFileAttributesError!DWORD { |
| ... | @@ -1232,34 +1217,22 @@ pub fn nanoSecondsToFileTime(ns: i64) FILETIME { | ... | @@ -1232,34 +1217,22 @@ pub fn nanoSecondsToFileTime(ns: i64) FILETIME { |
| 1232 | }; | 1217 | }; |
| 1233 | } | 1218 | } |
| 1234 | 1219 | ||
| 1235 | pub fn cStrToPrefixedFileW(s: [*:0]const u8) ![PATH_MAX_WIDE:0]u16 { | 1220 | pub const PathSpace = struct { |
| 1236 | return sliceToPrefixedFileW(mem.spanZ(s)); | 1221 | data: [PATH_MAX_WIDE:0]u16, |
| 1237 | } | 1222 | len: usize, |
| 1238 | 1223 | ||
| 1239 | pub fn sliceToPrefixedFileW(s: []const u8) ![PATH_MAX_WIDE:0]u16 { | 1224 | pub fn span(self: PathSpace) [:0]const u16 { |
| 1240 | return sliceToPrefixedSuffixedFileW(s, &[_]u16{}); | 1225 | return self.data[0..self.len :0]; |
| 1241 | } | 1226 | } |
| 1242 | 1227 | }; | |
| 1243 | /// Assumes an absolute path. | ||
| 1244 | pub fn wToPrefixedFileW(s: []const u16) ![PATH_MAX_WIDE:0]u16 { | ||
| 1245 | // TODO https://github.com/ziglang/zig/issues/2765 | ||
| 1246 | var result: [PATH_MAX_WIDE:0]u16 = undefined; | ||
| 1247 | 1228 | ||
| 1248 | const start_index = if (mem.startsWith(u16, s, &[_]u16{ '\\', '?' })) 0 else blk: { | 1229 | pub fn cStrToPrefixedFileW(s: [*:0]const u8) !PathSpace { |
| 1249 | const prefix = [_]u16{ '\\', '?', '?', '\\' }; | 1230 | return sliceToPrefixedFileW(mem.spanZ(s)); |
| 1250 | mem.copy(u16, result[0..], &prefix); | ||
| 1251 | break :blk prefix.len; | ||
| 1252 | }; | ||
| 1253 | const end_index = start_index + s.len; | ||
| 1254 | if (end_index + 1 > result.len) return error.NameTooLong; | ||
| 1255 | mem.copy(u16, result[start_index..], s); | ||
| 1256 | result[end_index] = 0; | ||
| 1257 | return result; | ||
| 1258 | } | 1231 | } |
| 1259 | 1232 | ||
| 1260 | pub fn sliceToPrefixedSuffixedFileW(s: []const u8, comptime suffix: []const u16) ![PATH_MAX_WIDE + suffix.len:0]u16 { | 1233 | pub fn sliceToPrefixedFileW(s: []const u8) !PathSpace { |
| 1261 | // TODO https://github.com/ziglang/zig/issues/2765 | 1234 | // TODO https://github.com/ziglang/zig/issues/2765 |
| 1262 | var result: [PATH_MAX_WIDE + suffix.len:0]u16 = undefined; | 1235 | var path_space: PathSpace = undefined; |
| 1263 | for (s) |byte| { | 1236 | for (s) |byte| { |
| 1264 | switch (byte) { | 1237 | switch (byte) { |
| 1265 | '*', '?', '"', '<', '>', '|' => return error.BadPathName, | 1238 | '*', '?', '"', '<', '>', '|' => return error.BadPathName, |
| ... | @@ -1268,25 +1241,46 @@ pub fn sliceToPrefixedSuffixedFileW(s: []const u8, comptime suffix: []const u16) | ... | @@ -1268,25 +1241,46 @@ pub fn sliceToPrefixedSuffixedFileW(s: []const u8, comptime suffix: []const u16) |
| 1268 | } | 1241 | } |
| 1269 | const start_index = if (mem.startsWith(u8, s, "\\?") or !std.fs.path.isAbsolute(s)) 0 else blk: { | 1242 | const start_index = if (mem.startsWith(u8, s, "\\?") or !std.fs.path.isAbsolute(s)) 0 else blk: { |
| 1270 | const prefix = [_]u16{ '\\', '?', '?', '\\' }; | 1243 | const prefix = [_]u16{ '\\', '?', '?', '\\' }; |
| 1271 | mem.copy(u16, result[0..], &prefix); | 1244 | mem.copy(u16, path_space.data[0..], &prefix); |
| 1272 | break :blk prefix.len; | 1245 | break :blk prefix.len; |
| 1273 | }; | 1246 | }; |
| 1274 | const end_index = start_index + try std.unicode.utf8ToUtf16Le(result[start_index..], s); | 1247 | path_space.len = start_index + try std.unicode.utf8ToUtf16Le(path_space.data[start_index..], s); |
| 1275 | if (end_index + suffix.len > result.len) return error.NameTooLong; | 1248 | if (path_space.len > path_space.data.len) return error.NameTooLong; |
| 1276 | // > File I/O functions in the Windows API convert "/" to "\" as part of | 1249 | // > File I/O functions in the Windows API convert "/" to "\" as part of |
| 1277 | // > converting the name to an NT-style name, except when using the "\\?\" | 1250 | // > converting the name to an NT-style name, except when using the "\\?\" |
| 1278 | // > prefix as detailed in the following sections. | 1251 | // > prefix as detailed in the following sections. |
| 1279 | // from https://docs.microsoft.com/en-us/windows/desktop/FileIO/naming-a-file#maximum-path-length-limitation | 1252 | // from https://docs.microsoft.com/en-us/windows/desktop/FileIO/naming-a-file#maximum-path-length-limitation |
| 1280 | // Because we want the larger maximum path length for absolute paths, we | 1253 | // Because we want the larger maximum path length for absolute paths, we |
| 1281 | // convert forward slashes to backward slashes here. | 1254 | // convert forward slashes to backward slashes here. |
| 1282 | for (result[0..end_index]) |*elem| { | 1255 | for (path_space.data[0..path_space.len]) |*elem| { |
| 1283 | if (elem.* == '/') { | 1256 | if (elem.* == '/') { |
| 1284 | elem.* = '\\'; | 1257 | elem.* = '\\'; |
| 1285 | } | 1258 | } |
| 1286 | } | 1259 | } |
| 1287 | mem.copy(u16, result[end_index..], suffix); | 1260 | path_space.data[path_space.len] = 0; |
| 1288 | result[end_index + suffix.len] = 0; | 1261 | return path_space; |
| 1289 | return result; | 1262 | } |
| 1263 | |||
| 1264 | /// Assumes an absolute path. | ||
| 1265 | pub fn wToPrefixedFileW(s: []const u16) !PathSpace { | ||
| 1266 | // TODO https://github.com/ziglang/zig/issues/2765 | ||
| 1267 | var path_space: PathSpace = undefined; | ||
| 1268 | |||
| 1269 | const start_index = if (mem.startsWith(u16, s, &[_]u16{ '\\', '?' })) 0 else blk: { | ||
| 1270 | const prefix = [_]u16{ '\\', '?', '?', '\\' }; | ||
| 1271 | mem.copy(u16, path_space.data[0..], &prefix); | ||
| 1272 | break :blk prefix.len; | ||
| 1273 | }; | ||
| 1274 | path_space.len = start_index + s.len; | ||
| 1275 | if (path_space.len > path_space.data.len) return error.NameTooLong; | ||
| 1276 | mem.copy(u16, path_space.data[start_index..], s); | ||
| 1277 | for (path_space.data[0..path_space.len]) |*elem| { | ||
| 1278 | if (elem.* == '/') { | ||
| 1279 | elem.* = '\\'; | ||
| 1280 | } | ||
| 1281 | } | ||
| 1282 | path_space.data[path_space.len] = 0; | ||
| 1283 | return path_space; | ||
| 1290 | } | 1284 | } |
| 1291 | 1285 | ||
| 1292 | inline fn MAKELANGID(p: c_ushort, s: c_ushort) LANGID { | 1286 | inline fn MAKELANGID(p: c_ushort, s: c_ushort) LANGID { |
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, |
| 471 | 471 | ||
| 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, .{}); | 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; |
| 476 | 476 |
lib/std/reset_event.zig+1-1| ... | @@ -52,7 +52,7 @@ pub const ResetEvent = struct { | ... | @@ -52,7 +52,7 @@ pub const ResetEvent = struct { |
| 52 | 52 | ||
| 53 | /// Wait for the event to be set by blocking the current thread. | 53 | /// Wait for the event to be set by blocking the current thread. |
| 54 | /// A timeout in nanoseconds can be provided as a hint for how | 54 | /// A timeout in nanoseconds can be provided as a hint for how |
| 55 | /// long the thread should block on the unset event before throwind error.TimedOut. | 55 | /// long the thread should block on the unset event before throwing error.TimedOut. |
| 56 | pub fn timedWait(self: *ResetEvent, timeout_ns: u64) !void { | 56 | pub fn timedWait(self: *ResetEvent, timeout_ns: u64) !void { |
| 57 | return self.os_event.wait(timeout_ns); | 57 | return self.os_event.wait(timeout_ns); |
| 58 | } | 58 | } |
src-self-hosted/test.zig+1-1| ... | @@ -96,7 +96,7 @@ pub const TestContext = struct { | ... | @@ -96,7 +96,7 @@ pub const TestContext = struct { |
| 96 | case: ZIRCompareOutputCase, | 96 | case: ZIRCompareOutputCase, |
| 97 | target: std.Target, | 97 | target: std.Target, |
| 98 | ) !void { | 98 | ) !void { |
| 99 | var tmp = std.testing.tmpDir(.{ .share_with_child_process = true }); | 99 | var tmp = std.testing.tmpDir(.{}); |
| 100 | defer tmp.cleanup(); | 100 | defer tmp.cleanup(); |
| 101 | 101 | ||
| 102 | var prg_node = root_node.start(case.name, 4); | 102 | var prg_node = root_node.start(case.name, 4); |