| 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 | 49 | /// Set to change the current working directory when spawning the child process. |
| 50 | 50 | /// This is not yet implemented for Windows. See https://github.com/ziglang/zig/issues/5190 |
| 51 | 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 | 52 | cwd_dir: ?fs.Dir = null, |
| 55 | 53 | |
| 56 | 54 | err_pipe: if (builtin.os.tag == .windows) void else [2]os.fd_t, |
| ... | ... | @@ -443,26 +441,17 @@ pub const ChildProcess = struct { |
| 443 | 441 | // we are the parent |
| 444 | 442 | const pid = @intCast(i32, pid_result); |
| 445 | 443 | if (self.stdin_behavior == StdIo.Pipe) { |
| 446 | self.stdin = File{ | |
| 447 | .handle = stdin_pipe[1], | |
| 448 | .io_mode = std.io.mode, | |
| 449 | }; | |
| 444 | self.stdin = File{ .handle = stdin_pipe[1] }; | |
| 450 | 445 | } else { |
| 451 | 446 | self.stdin = null; |
| 452 | 447 | } |
| 453 | 448 | if (self.stdout_behavior == StdIo.Pipe) { |
| 454 | self.stdout = File{ | |
| 455 | .handle = stdout_pipe[0], | |
| 456 | .io_mode = std.io.mode, | |
| 457 | }; | |
| 449 | self.stdout = File{ .handle = stdout_pipe[0] }; | |
| 458 | 450 | } else { |
| 459 | 451 | self.stdout = null; |
| 460 | 452 | } |
| 461 | 453 | if (self.stderr_behavior == StdIo.Pipe) { |
| 462 | self.stderr = File{ | |
| 463 | .handle = stderr_pipe[0], | |
| 464 | .io_mode = std.io.mode, | |
| 465 | }; | |
| 454 | self.stderr = File{ .handle = stderr_pipe[0] }; | |
| 466 | 455 | } else { |
| 467 | 456 | self.stderr = null; |
| 468 | 457 | } |
| ... | ... | @@ -686,26 +675,17 @@ pub const ChildProcess = struct { |
| 686 | 675 | }; |
| 687 | 676 | |
| 688 | 677 | if (g_hChildStd_IN_Wr) |h| { |
| 689 | self.stdin = File{ | |
| 690 | .handle = h, | |
| 691 | .io_mode = io.mode, | |
| 692 | }; | |
| 678 | self.stdin = File{ .handle = h }; | |
| 693 | 679 | } else { |
| 694 | 680 | self.stdin = null; |
| 695 | 681 | } |
| 696 | 682 | if (g_hChildStd_OUT_Rd) |h| { |
| 697 | self.stdout = File{ | |
| 698 | .handle = h, | |
| 699 | .io_mode = io.mode, | |
| 700 | }; | |
| 683 | self.stdout = File{ .handle = h }; | |
| 701 | 684 | } else { |
| 702 | 685 | self.stdout = null; |
| 703 | 686 | } |
| 704 | 687 | if (g_hChildStd_ERR_Rd) |h| { |
| 705 | self.stderr = File{ | |
| 706 | .handle = h, | |
| 707 | .io_mode = io.mode, | |
| 708 | }; | |
| 688 | self.stderr = File{ .handle = h }; | |
| 709 | 689 | } else { |
| 710 | 690 | self.stderr = null; |
| 711 | 691 | } |
| ... | ... | @@ -845,8 +825,8 @@ const ErrInt = std.meta.Int(false, @sizeOf(anyerror) * 8); |
| 845 | 825 | fn writeIntFd(fd: i32, value: ErrInt) !void { |
| 846 | 826 | const file = File{ |
| 847 | 827 | .handle = fd, |
| 848 | .io_mode = .blocking, | |
| 849 | .async_block_allowed = File.async_block_allowed_yes, | |
| 828 | .capable_io_mode = .blocking, | |
| 829 | .intended_io_mode = .blocking, | |
| 850 | 830 | }; |
| 851 | 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 | 834 | fn readIntFd(fd: i32) !ErrInt { |
| 855 | 835 | const file = File{ |
| 856 | 836 | .handle = fd, |
| 857 | .io_mode = .blocking, | |
| 858 | .async_block_allowed = File.async_block_allowed_yes, | |
| 837 | .capable_io_mode = .blocking, | |
| 838 | .intended_io_mode = .blocking, | |
| 859 | 839 | }; |
| 860 | 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 | 112 | /// Tries to print the current stack trace to stderr, unbuffered, and ignores any error returned. |
| 113 | 113 | /// TODO multithreaded awareness |
| 114 | 114 | pub fn dumpCurrentStackTrace(start_addr: ?usize) void { |
| 115 | const stderr = getStderrStream(); | |
| 116 | if (builtin.strip_debug_info) { | |
| 117 | noasync stderr.print("Unable to dump stack trace: debug info stripped\n", .{}) catch return; | |
| 118 | return; | |
| 115 | noasync { | |
| 116 | const stderr = getStderrStream(); | |
| 117 | if (builtin.strip_debug_info) { | |
| 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 | 132 | /// Tries to print the stack trace starting from the supplied base pointer to stderr, |
| 131 | 133 | /// unbuffered, and ignores any error returned. |
| 132 | 134 | /// TODO multithreaded awareness |
| 133 | 135 | pub fn dumpStackTraceFromBase(bp: usize, ip: usize) void { |
| 134 | const stderr = getStderrStream(); | |
| 135 | if (builtin.strip_debug_info) { | |
| 136 | noasync stderr.print("Unable to dump stack trace: debug info stripped\n", .{}) catch return; | |
| 137 | return; | |
| 138 | } | |
| 139 | const debug_info = getSelfDebugInfo() catch |err| { | |
| 140 | noasync stderr.print("Unable to dump stack trace: Unable to open debug info: {}\n", .{@errorName(err)}) catch return; | |
| 141 | return; | |
| 142 | }; | |
| 143 | const tty_config = detectTTYConfig(); | |
| 144 | printSourceAtAddress(debug_info, stderr, ip, tty_config) catch return; | |
| 145 | var it = StackIterator.init(null, bp); | |
| 146 | while (it.next()) |return_address| { | |
| 147 | printSourceAtAddress(debug_info, stderr, return_address - 1, tty_config) catch return; | |
| 136 | noasync { | |
| 137 | const stderr = getStderrStream(); | |
| 138 | if (builtin.strip_debug_info) { | |
| 139 | stderr.print("Unable to dump stack trace: debug info stripped\n", .{}) catch return; | |
| 140 | return; | |
| 141 | } | |
| 142 | const debug_info = getSelfDebugInfo() catch |err| { | |
| 143 | stderr.print("Unable to dump stack trace: Unable to open debug info: {}\n", .{@errorName(err)}) catch return; | |
| 144 | return; | |
| 145 | }; | |
| 146 | const tty_config = detectTTYConfig(); | |
| 147 | printSourceAtAddress(debug_info, stderr, ip, tty_config) catch return; | |
| 148 | var it = StackIterator.init(null, bp); | |
| 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 | 203 | /// Tries to print a stack trace to stderr, unbuffered, and ignores any error returned. |
| 200 | 204 | /// TODO multithreaded awareness |
| 201 | 205 | pub fn dumpStackTrace(stack_trace: builtin.StackTrace) void { |
| 202 | const stderr = getStderrStream(); | |
| 203 | if (builtin.strip_debug_info) { | |
| 204 | noasync stderr.print("Unable to dump stack trace: debug info stripped\n", .{}) catch return; | |
| 205 | return; | |
| 206 | noasync { | |
| 207 | const stderr = getStderrStream(); | |
| 208 | if (builtin.strip_debug_info) { | |
| 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 | 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 | 261 | resetSegfaultHandler(); |
| 256 | 262 | } |
| 257 | 263 | |
| 258 | switch (panic_stage) { | |
| 264 | noasync switch (panic_stage) { | |
| 259 | 265 | 0 => { |
| 260 | 266 | panic_stage = 1; |
| 261 | 267 | |
| ... | ... | @@ -267,7 +273,7 @@ pub fn panicExtra(trace: ?*const builtin.StackTrace, first_trace_addr: ?usize, c |
| 267 | 273 | defer held.release(); |
| 268 | 274 | |
| 269 | 275 | const stderr = getStderrStream(); |
| 270 | noasync stderr.print(format ++ "\n", args) catch os.abort(); | |
| 276 | stderr.print(format ++ "\n", args) catch os.abort(); | |
| 271 | 277 | if (trace) |t| { |
| 272 | 278 | dumpStackTrace(t.*); |
| 273 | 279 | } |
| ... | ... | @@ -292,12 +298,12 @@ pub fn panicExtra(trace: ?*const builtin.StackTrace, first_trace_addr: ?usize, c |
| 292 | 298 | // we're still holding the mutex but that's fine as we're going to |
| 293 | 299 | // call abort() |
| 294 | 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 | 303 | else => { |
| 298 | 304 | // Panicked while printing "Panicked during a panic." |
| 299 | 305 | }, |
| 300 | } | |
| 306 | }; | |
| 301 | 307 | |
| 302 | 308 | os.abort(); |
| 303 | 309 | } |
| ... | ... | @@ -666,158 +672,160 @@ pub fn openSelfDebugInfo(allocator: *mem.Allocator) anyerror!DebugInfo { |
| 666 | 672 | |
| 667 | 673 | /// TODO resources https://github.com/ziglang/zig/issues/4353 |
| 668 | 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, .{}); | |
| 670 | errdefer coff_file.close(); | |
| 675 | noasync { | |
| 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); | |
| 673 | coff_obj.* = coff.Coff.init(allocator, coff_file); | |
| 679 | const coff_obj = try allocator.create(coff.Coff); | |
| 680 | coff_obj.* = coff.Coff.init(allocator, coff_file); | |
| 674 | 681 | |
| 675 | var di = ModuleDebugInfo{ | |
| 676 | .base_address = undefined, | |
| 677 | .coff = coff_obj, | |
| 678 | .pdb = undefined, | |
| 679 | .sect_contribs = undefined, | |
| 680 | .modules = undefined, | |
| 681 | }; | |
| 682 | var di = ModuleDebugInfo{ | |
| 683 | .base_address = undefined, | |
| 684 | .coff = coff_obj, | |
| 685 | .pdb = undefined, | |
| 686 | .sect_contribs = undefined, | |
| 687 | .modules = undefined, | |
| 688 | }; | |
| 682 | 689 | |
| 683 | try di.coff.loadHeader(); | |
| 690 | try di.coff.loadHeader(); | |
| 684 | 691 | |
| 685 | var path_buf: [windows.MAX_PATH]u8 = undefined; | |
| 686 | const len = try di.coff.getPdbPath(path_buf[0..]); | |
| 687 | const raw_path = path_buf[0..len]; | |
| 692 | var path_buf: [windows.MAX_PATH]u8 = undefined; | |
| 693 | const len = try di.coff.getPdbPath(path_buf[0..]); | |
| 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; | |
| 694 | const version = try pdb_stream.inStream().readIntLittle(u32); | |
| 695 | const signature = try pdb_stream.inStream().readIntLittle(u32); | |
| 696 | const age = try pdb_stream.inStream().readIntLittle(u32); | |
| 697 | var guid: [16]u8 = undefined; | |
| 698 | try pdb_stream.inStream().readNoEof(&guid); | |
| 699 | if (version != 20000404) // VC70, only value observed by LLVM team | |
| 700 | return error.UnknownPDBVersion; | |
| 701 | if (!mem.eql(u8, &di.coff.guid, &guid) or di.coff.age != age) | |
| 702 | return error.PDBMismatch; | |
| 703 | // We validated the executable and pdb match. | |
| 700 | var pdb_stream = di.pdb.getStream(pdb.StreamType.Pdb) orelse return error.InvalidDebugInfo; | |
| 701 | const version = try pdb_stream.inStream().readIntLittle(u32); | |
| 702 | const signature = try pdb_stream.inStream().readIntLittle(u32); | |
| 703 | const age = try pdb_stream.inStream().readIntLittle(u32); | |
| 704 | var guid: [16]u8 = undefined; | |
| 705 | try pdb_stream.inStream().readNoEof(&guid); | |
| 706 | if (version != 20000404) // VC70, only value observed by LLVM team | |
| 707 | return error.UnknownPDBVersion; | |
| 708 | if (!mem.eql(u8, &di.coff.guid, &guid) or di.coff.age != age) | |
| 709 | return error.PDBMismatch; | |
| 710 | // We validated the executable and pdb match. | |
| 704 | 711 | |
| 705 | const string_table_index = str_tab_index: { | |
| 706 | const name_bytes_len = try pdb_stream.inStream().readIntLittle(u32); | |
| 707 | const name_bytes = try allocator.alloc(u8, name_bytes_len); | |
| 708 | try pdb_stream.inStream().readNoEof(name_bytes); | |
| 712 | const string_table_index = str_tab_index: { | |
| 713 | const name_bytes_len = try pdb_stream.inStream().readIntLittle(u32); | |
| 714 | const name_bytes = try allocator.alloc(u8, name_bytes_len); | |
| 715 | try pdb_stream.inStream().readNoEof(name_bytes); | |
| 709 | 716 | |
| 710 | const HashTableHeader = packed struct { | |
| 711 | Size: u32, | |
| 712 | Capacity: u32, | |
| 717 | const HashTableHeader = packed struct { | |
| 718 | Size: u32, | |
| 719 | Capacity: u32, | |
| 713 | 720 | |
| 714 | fn maxLoad(cap: u32) u32 { | |
| 715 | return cap * 2 / 3 + 1; | |
| 716 | } | |
| 717 | }; | |
| 718 | const hash_tbl_hdr = try pdb_stream.inStream().readStruct(HashTableHeader); | |
| 719 | if (hash_tbl_hdr.Capacity == 0) | |
| 720 | return error.InvalidDebugInfo; | |
| 721 | fn maxLoad(cap: u32) u32 { | |
| 722 | return cap * 2 / 3 + 1; | |
| 723 | } | |
| 724 | }; | |
| 725 | const hash_tbl_hdr = try pdb_stream.inStream().readStruct(HashTableHeader); | |
| 726 | if (hash_tbl_hdr.Capacity == 0) | |
| 727 | return error.InvalidDebugInfo; | |
| 721 | 728 | |
| 722 | if (hash_tbl_hdr.Size > HashTableHeader.maxLoad(hash_tbl_hdr.Capacity)) | |
| 723 | return error.InvalidDebugInfo; | |
| 729 | if (hash_tbl_hdr.Size > HashTableHeader.maxLoad(hash_tbl_hdr.Capacity)) | |
| 730 | return error.InvalidDebugInfo; | |
| 724 | 731 | |
| 725 | const present = try readSparseBitVector(&pdb_stream.inStream(), allocator); | |
| 726 | if (present.len != hash_tbl_hdr.Size) | |
| 727 | return error.InvalidDebugInfo; | |
| 728 | const deleted = try readSparseBitVector(&pdb_stream.inStream(), allocator); | |
| 732 | const present = try readSparseBitVector(&pdb_stream.inStream(), allocator); | |
| 733 | if (present.len != hash_tbl_hdr.Size) | |
| 734 | return error.InvalidDebugInfo; | |
| 735 | const deleted = try readSparseBitVector(&pdb_stream.inStream(), allocator); | |
| 729 | 736 | |
| 730 | const Bucket = struct { | |
| 731 | first: u32, | |
| 732 | second: u32, | |
| 733 | }; | |
| 734 | const bucket_list = try allocator.alloc(Bucket, present.len); | |
| 735 | for (present) |_| { | |
| 736 | const name_offset = try pdb_stream.inStream().readIntLittle(u32); | |
| 737 | const name_index = try pdb_stream.inStream().readIntLittle(u32); | |
| 738 | const name = mem.spanZ(@ptrCast([*:0]u8, name_bytes.ptr + name_offset)); | |
| 739 | if (mem.eql(u8, name, "/names")) { | |
| 740 | break :str_tab_index name_index; | |
| 737 | const Bucket = struct { | |
| 738 | first: u32, | |
| 739 | second: u32, | |
| 740 | }; | |
| 741 | const bucket_list = try allocator.alloc(Bucket, present.len); | |
| 742 | for (present) |_| { | |
| 743 | const name_offset = try pdb_stream.inStream().readIntLittle(u32); | |
| 744 | const name_index = try pdb_stream.inStream().readIntLittle(u32); | |
| 745 | const name = mem.spanZ(@ptrCast([*:0]u8, name_bytes.ptr + name_offset)); | |
| 746 | if (mem.eql(u8, name, "/names")) { | |
| 747 | break :str_tab_index name_index; | |
| 748 | } | |
| 741 | 749 | } |
| 742 | } | |
| 743 | return error.MissingDebugInfo; | |
| 744 | }; | |
| 750 | return error.MissingDebugInfo; | |
| 751 | }; | |
| 745 | 752 | |
| 746 | 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; | |
| 753 | di.pdb.string_table = di.pdb.getStreamById(string_table_index) 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 | |
| 752 | const dbi_stream_header = try dbi.inStream().readStruct(pdb.DbiStreamHeader); | |
| 753 | if (dbi_stream_header.VersionHeader != 19990903) // V70, only value observed by LLVM team | |
| 754 | return error.UnknownPDBVersion; | |
| 755 | if (dbi_stream_header.Age != age) | |
| 756 | return error.UnmatchingPDB; | |
| 758 | // Dbi Header | |
| 759 | const dbi_stream_header = try dbi.inStream().readStruct(pdb.DbiStreamHeader); | |
| 760 | if (dbi_stream_header.VersionHeader != 19990903) // V70, only value observed by LLVM team | |
| 761 | return error.UnknownPDBVersion; | |
| 762 | if (dbi_stream_header.Age != age) | |
| 763 | return error.UnmatchingPDB; | |
| 757 | 764 | |
| 758 | const mod_info_size = dbi_stream_header.ModInfoSize; | |
| 759 | const section_contrib_size = dbi_stream_header.SectionContributionSize; | |
| 765 | const mod_info_size = dbi_stream_header.ModInfoSize; | |
| 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 | |
| 764 | var mod_info_offset: usize = 0; | |
| 765 | while (mod_info_offset != mod_info_size) { | |
| 766 | const mod_info = try dbi.inStream().readStruct(pdb.ModInfo); | |
| 767 | var this_record_len: usize = @sizeOf(pdb.ModInfo); | |
| 770 | // Module Info Substream | |
| 771 | var mod_info_offset: usize = 0; | |
| 772 | while (mod_info_offset != mod_info_size) { | |
| 773 | const mod_info = try dbi.inStream().readStruct(pdb.ModInfo); | |
| 774 | var this_record_len: usize = @sizeOf(pdb.ModInfo); | |
| 768 | 775 | |
| 769 | const module_name = try dbi.readNullTermString(allocator); | |
| 770 | this_record_len += module_name.len + 1; | |
| 776 | const module_name = try dbi.readNullTermString(allocator); | |
| 777 | this_record_len += module_name.len + 1; | |
| 771 | 778 | |
| 772 | const obj_file_name = try dbi.readNullTermString(allocator); | |
| 773 | this_record_len += obj_file_name.len + 1; | |
| 779 | const obj_file_name = try dbi.readNullTermString(allocator); | |
| 780 | this_record_len += obj_file_name.len + 1; | |
| 774 | 781 | |
| 775 | if (this_record_len % 4 != 0) { | |
| 776 | const round_to_next_4 = (this_record_len | 0x3) + 1; | |
| 777 | const march_forward_bytes = round_to_next_4 - this_record_len; | |
| 778 | try dbi.seekBy(@intCast(isize, march_forward_bytes)); | |
| 779 | this_record_len += march_forward_bytes; | |
| 780 | } | |
| 782 | if (this_record_len % 4 != 0) { | |
| 783 | const round_to_next_4 = (this_record_len | 0x3) + 1; | |
| 784 | const march_forward_bytes = round_to_next_4 - this_record_len; | |
| 785 | try dbi.seekBy(@intCast(isize, march_forward_bytes)); | |
| 786 | this_record_len += march_forward_bytes; | |
| 787 | } | |
| 781 | 788 | |
| 782 | try modules.append(Module{ | |
| 783 | .mod_info = mod_info, | |
| 784 | .module_name = module_name, | |
| 785 | .obj_file_name = obj_file_name, | |
| 789 | try modules.append(Module{ | |
| 790 | .mod_info = mod_info, | |
| 791 | .module_name = module_name, | |
| 792 | .obj_file_name = obj_file_name, | |
| 786 | 793 | |
| 787 | .populated = false, | |
| 788 | .symbols = undefined, | |
| 789 | .subsect_info = undefined, | |
| 790 | .checksum_offset = null, | |
| 791 | }); | |
| 794 | .populated = false, | |
| 795 | .symbols = undefined, | |
| 796 | .subsect_info = undefined, | |
| 797 | .checksum_offset = null, | |
| 798 | }); | |
| 792 | 799 | |
| 793 | mod_info_offset += this_record_len; | |
| 794 | if (mod_info_offset > mod_info_size) | |
| 795 | return error.InvalidDebugInfo; | |
| 796 | } | |
| 800 | mod_info_offset += this_record_len; | |
| 801 | if (mod_info_offset > mod_info_size) | |
| 802 | return error.InvalidDebugInfo; | |
| 803 | } | |
| 797 | 804 | |
| 798 | di.modules = modules.toOwnedSlice(); | |
| 805 | di.modules = modules.toOwnedSlice(); | |
| 799 | 806 | |
| 800 | // Section Contribution Substream | |
| 801 | var sect_contribs = ArrayList(pdb.SectionContribEntry).init(allocator); | |
| 802 | var sect_cont_offset: usize = 0; | |
| 803 | if (section_contrib_size != 0) { | |
| 804 | const ver = @intToEnum(pdb.SectionContrSubstreamVersion, try dbi.inStream().readIntLittle(u32)); | |
| 805 | if (ver != pdb.SectionContrSubstreamVersion.Ver60) | |
| 806 | return error.InvalidDebugInfo; | |
| 807 | sect_cont_offset += @sizeOf(u32); | |
| 808 | } | |
| 809 | while (sect_cont_offset != section_contrib_size) { | |
| 810 | const entry = try sect_contribs.addOne(); | |
| 811 | entry.* = try dbi.inStream().readStruct(pdb.SectionContribEntry); | |
| 812 | sect_cont_offset += @sizeOf(pdb.SectionContribEntry); | |
| 807 | // Section Contribution Substream | |
| 808 | var sect_contribs = ArrayList(pdb.SectionContribEntry).init(allocator); | |
| 809 | var sect_cont_offset: usize = 0; | |
| 810 | if (section_contrib_size != 0) { | |
| 811 | const ver = @intToEnum(pdb.SectionContrSubstreamVersion, try dbi.inStream().readIntLittle(u32)); | |
| 812 | if (ver != pdb.SectionContrSubstreamVersion.Ver60) | |
| 813 | return error.InvalidDebugInfo; | |
| 814 | sect_cont_offset += @sizeOf(u32); | |
| 815 | } | |
| 816 | while (sect_cont_offset != section_contrib_size) { | |
| 817 | const entry = try sect_contribs.addOne(); | |
| 818 | entry.* = try dbi.inStream().readStruct(pdb.SectionContribEntry); | |
| 819 | sect_cont_offset += @sizeOf(pdb.SectionContribEntry); | |
| 813 | 820 | |
| 814 | if (sect_cont_offset > section_contrib_size) | |
| 815 | return error.InvalidDebugInfo; | |
| 816 | } | |
| 821 | if (sect_cont_offset > section_contrib_size) | |
| 822 | return error.InvalidDebugInfo; | |
| 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 | 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 | 1009 | fn printLineFromFileAnyOs(out_stream: var, line_info: LineInfo) !void { |
| 1002 | 1010 | // Need this to always block even in async I/O mode, because this could potentially |
| 1003 | 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 | 1013 | defer f.close(); |
| 1006 | 1014 | // TODO fstat and make sure that the file has the correct size |
| 1007 | 1015 | |
| ... | ... | @@ -1049,7 +1057,7 @@ const MachoSymbol = struct { |
| 1049 | 1057 | |
| 1050 | 1058 | fn mapWholeFile(path: []const u8) ![]align(mem.page_size) const u8 { |
| 1051 | 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 | 1061 | defer file.close(); |
| 1054 | 1062 | |
| 1055 | 1063 | const file_len = try math.cast(usize, try file.getEndPos()); |
| ... | ... | @@ -1410,59 +1418,61 @@ pub const ModuleDebugInfo = switch (builtin.os.tag) { |
| 1410 | 1418 | } |
| 1411 | 1419 | |
| 1412 | 1420 | fn getSymbolAtAddress(self: *@This(), address: usize) !SymbolInfo { |
| 1413 | // Translate the VA into an address into this object | |
| 1414 | const relocated_address = address - self.base_address; | |
| 1415 | assert(relocated_address >= 0x100000000); | |
| 1416 | ||
| 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..]); | |
| 1421 | noasync { | |
| 1422 | // Translate the VA into an address into this object | |
| 1423 | const relocated_address = address - self.base_address; | |
| 1424 | assert(relocated_address >= 0x100000000); | |
| 1424 | 1425 | |
| 1425 | if (symbol.ofile == null) | |
| 1426 | return SymbolInfo{ .symbol_name = stab_symbol }; | |
| 1426 | // Find the .o file where this symbol is defined | |
| 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 | |
| 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 | => { | |
| 1434 | if (symbol.ofile == null) | |
| 1437 | 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 | |
| 1443 | // .o file | |
| 1444 | const relocated_address_o = relocated_address - symbol.reloc; | |
| 1437 | const o_file_path = mem.spanZ(self.strings[symbol.ofile.?.n_strx..]); | |
| 1445 | 1438 | |
| 1446 | if (o_file_di.findCompileUnit(relocated_address_o)) |compile_unit| { | |
| 1447 | return SymbolInfo{ | |
| 1448 | .symbol_name = o_file_di.getSymbolName(relocated_address_o) orelse "???", | |
| 1449 | .compile_unit_name = compile_unit.die.getAttrString(&o_file_di, DW.AT_name) catch |err| switch (err) { | |
| 1450 | error.MissingDebugInfo, error.InvalidDebugInfo => "???", | |
| 1451 | else => return err, | |
| 1439 | // Check if its debug infos are already in the cache | |
| 1440 | var o_file_di = self.ofiles.getValue(o_file_path) orelse | |
| 1441 | (self.loadOFile(o_file_path) catch |err| switch (err) { | |
| 1442 | error.FileNotFound, | |
| 1443 | error.MissingDebugInfo, | |
| 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) { | |
| 1454 | error.MissingDebugInfo, error.InvalidDebugInfo => null, | |
| 1455 | else => return err, | |
| 1448 | else => return err, | |
| 1449 | }); | |
| 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 | }; | |
| 1458 | } else |err| switch (err) { | |
| 1459 | error.MissingDebugInfo, error.InvalidDebugInfo => { | |
| 1460 | return SymbolInfo{ .symbol_name = stab_symbol }; | |
| 1461 | }, | |
| 1462 | else => return err, | |
| 1463 | } | |
| 1471 | else => return err, | |
| 1472 | } | |
| 1464 | 1473 | |
| 1465 | unreachable; | |
| 1474 | unreachable; | |
| 1475 | } | |
| 1466 | 1476 | } |
| 1467 | 1477 | }, |
| 1468 | 1478 | .uefi, .windows => struct { |
lib/std/dynamic_library.zig+2-2| ... | ... | @@ -328,14 +328,14 @@ pub const WindowsDynLib = struct { |
| 328 | 328 | |
| 329 | 329 | pub fn open(path: []const u8) !WindowsDynLib { |
| 330 | 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 | 334 | pub const openC = @compileError("deprecated: renamed to openZ"); |
| 335 | 335 | |
| 336 | 336 | pub fn openZ(path_c: [*:0]const u8) !WindowsDynLib { |
| 337 | 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 | 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 | 4 | const assert = std.debug.assert; |
| 5 | 5 | const testing = std.testing; |
| 6 | 6 | const mem = std.mem; |
| 7 | const AtomicRmwOp = builtin.AtomicRmwOp; | |
| 8 | const AtomicOrder = builtin.AtomicOrder; | |
| 9 | 7 | const os = std.os; |
| 10 | 8 | const windows = os.windows; |
| 11 | 9 | const maxInt = std.math.maxInt; |
| 12 | 10 | const Thread = std.Thread; |
| 13 | 11 | |
| 12 | const is_windows = std.Target.current.os.tag == .windows; | |
| 13 | ||
| 14 | 14 | pub const Loop = struct { |
| 15 | 15 | next_tick_queue: std.atomic.Queue(anyframe), |
| 16 | 16 | os_data: OsData, |
| 17 | 17 | final_resume_node: ResumeNode, |
| 18 | 18 | pending_event_count: usize, |
| 19 | 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 | 29 | /// For resources that have the same lifetime as the `Loop`. |
| 22 | 30 | /// This is only used by `Loop` for the thread pool and associated resources. |
| ... | ... | @@ -143,7 +151,12 @@ pub const Loop = struct { |
| 143 | 151 | .handle = undefined, |
| 144 | 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 | 160 | errdefer self.arena.deinit(); |
| 148 | 161 | |
| 149 | 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 | 171 | |
| 159 | 172 | try self.initOsData(extra_thread_count); |
| 160 | 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 | 184 | pub fn deinit(self: *Loop) void { |
| 164 | 185 | self.deinitOsData(); |
| 186 | self.fs_thread_wakeup.deinit(); | |
| 165 | 187 | self.arena.deinit(); |
| 166 | 188 | self.* = undefined; |
| 167 | 189 | } |
| ... | ... | @@ -173,21 +195,10 @@ pub const Loop = struct { |
| 173 | 195 | const wakeup_bytes = [_]u8{0x1} ** 8; |
| 174 | 196 | |
| 175 | 197 | fn initOsData(self: *Loop, extra_thread_count: usize) InitOsDataError!void { |
| 176 | switch (builtin.os.tag) { | |
| 198 | noasync switch (builtin.os.tag) { | |
| 177 | 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 | 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 | 203 | for (self.eventfd_resume_nodes) |*eventfd_node| { |
| 193 | 204 | eventfd_node.* = std.atomic.Stack(ResumeNode.EventFd).Node{ |
| ... | ... | @@ -206,10 +217,10 @@ pub const Loop = struct { |
| 206 | 217 | } |
| 207 | 218 | |
| 208 | 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 | 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 | 225 | self.os_data.final_eventfd_event = os.epoll_event{ |
| 215 | 226 | .events = os.EPOLLIN, |
| ... | ... | @@ -222,12 +233,6 @@ pub const Loop = struct { |
| 222 | 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 | 236 | if (builtin.single_threaded) { |
| 232 | 237 | assert(extra_thread_count == 0); |
| 233 | 238 | return; |
| ... | ... | @@ -236,7 +241,7 @@ pub const Loop = struct { |
| 236 | 241 | var extra_thread_index: usize = 0; |
| 237 | 242 | errdefer { |
| 238 | 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 | 245 | assert(amt == wakeup_bytes.len); |
| 241 | 246 | while (extra_thread_index != 0) { |
| 242 | 247 | extra_thread_index -= 1; |
| ... | ... | @@ -249,22 +254,7 @@ pub const Loop = struct { |
| 249 | 254 | }, |
| 250 | 255 | .macosx, .freebsd, .netbsd, .dragonfly => { |
| 251 | 256 | self.os_data.kqfd = try os.kqueue(); |
| 252 | errdefer noasync 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 | }; | |
| 257 | errdefer os.close(self.os_data.kqfd); | |
| 268 | 258 | |
| 269 | 259 | const empty_kevs = &[0]os.Kevent{}; |
| 270 | 260 | |
| ... | ... | @@ -310,30 +300,6 @@ pub const Loop = struct { |
| 310 | 300 | self.os_data.final_kevent.flags = os.EV_ENABLE; |
| 311 | 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 | 303 | if (builtin.single_threaded) { |
| 338 | 304 | assert(extra_thread_count == 0); |
| 339 | 305 | return; |
| ... | ... | @@ -401,25 +367,24 @@ pub const Loop = struct { |
| 401 | 367 | } |
| 402 | 368 | }, |
| 403 | 369 | else => {}, |
| 404 | } | |
| 370 | }; | |
| 405 | 371 | } |
| 406 | 372 | |
| 407 | 373 | fn deinitOsData(self: *Loop) void { |
| 408 | switch (builtin.os.tag) { | |
| 374 | noasync switch (builtin.os.tag) { | |
| 409 | 375 | .linux => { |
| 410 | noasync os.close(self.os_data.final_eventfd); | |
| 411 | while (self.available_eventfd_resume_nodes.pop()) |node| noasync os.close(node.data.eventfd); | |
| 412 | noasync os.close(self.os_data.epollfd); | |
| 376 | os.close(self.os_data.final_eventfd); | |
| 377 | while (self.available_eventfd_resume_nodes.pop()) |node| os.close(node.data.eventfd); | |
| 378 | os.close(self.os_data.epollfd); | |
| 413 | 379 | }, |
| 414 | 380 | .macosx, .freebsd, .netbsd, .dragonfly => { |
| 415 | noasync os.close(self.os_data.kqfd); | |
| 416 | noasync os.close(self.os_data.fs_kqfd); | |
| 381 | os.close(self.os_data.kqfd); | |
| 417 | 382 | }, |
| 418 | 383 | .windows => { |
| 419 | 384 | windows.CloseHandle(self.os_data.io_port); |
| 420 | 385 | }, |
| 421 | 386 | else => {}, |
| 422 | } | |
| 387 | }; | |
| 423 | 388 | } |
| 424 | 389 | |
| 425 | 390 | /// resume_node must live longer than the anyframe that it holds a reference to. |
| ... | ... | @@ -657,7 +622,7 @@ pub const Loop = struct { |
| 657 | 622 | .freebsd, |
| 658 | 623 | .netbsd, |
| 659 | 624 | .dragonfly, |
| 660 | => self.os_data.fs_thread.wait(), | |
| 625 | => self.fs_thread.wait(), | |
| 661 | 626 | else => {}, |
| 662 | 627 | } |
| 663 | 628 | |
| ... | ... | @@ -694,23 +659,25 @@ pub const Loop = struct { |
| 694 | 659 | |
| 695 | 660 | /// call finishOneEvent when done |
| 696 | 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 | 665 | pub fn finishOneEvent(self: *Loop) void { |
| 701 | const prev = @atomicRmw(usize, &self.pending_event_count, AtomicRmwOp.Sub, 1, AtomicOrder.SeqCst); | |
| 702 | if (prev == 1) { | |
| 666 | noasync { | |
| 667 | const prev = @atomicRmw(usize, &self.pending_event_count, .Sub, 1, .SeqCst); | |
| 668 | if (prev != 1) return; | |
| 669 | ||
| 703 | 670 | // cause all the threads to stop |
| 671 | self.posixFsRequest(&self.fs_end_request); | |
| 672 | ||
| 704 | 673 | switch (builtin.os.tag) { |
| 705 | 674 | .linux => { |
| 706 | self.posixFsRequest(&self.os_data.fs_end_request); | |
| 707 | 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 | 677 | assert(amt == wakeup_bytes.len); |
| 710 | 678 | return; |
| 711 | 679 | }, |
| 712 | 680 | .macosx, .freebsd, .netbsd, .dragonfly => { |
| 713 | self.posixFsRequest(&self.os_data.fs_end_request); | |
| 714 | 681 | const final_kevent = @as(*const [1]os.Kevent, &self.os_data.final_kevent); |
| 715 | 682 | const empty_kevs = &[0]os.Kevent{}; |
| 716 | 683 | // cannot fail because we already added it and this just enables it |
| ... | ... | @@ -1063,73 +1030,55 @@ pub const Loop = struct { |
| 1063 | 1030 | |
| 1064 | 1031 | fn posixFsRequest(self: *Loop, request_node: *Request.Node) void { |
| 1065 | 1032 | self.beginOneEvent(); // finished in posixFsRun after processing the msg |
| 1066 | self.os_data.fs_queue.put(request_node); | |
| 1067 | switch (builtin.os.tag) { | |
| 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 | } | |
| 1033 | self.fs_queue.put(request_node); | |
| 1034 | self.fs_thread_wakeup.set(); | |
| 1084 | 1035 | } |
| 1085 | 1036 | |
| 1086 | 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 | 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 | 1043 | fn posixFsRun(self: *Loop) void { |
| 1095 | while (true) { | |
| 1096 | if (builtin.os.tag == .linux) { | |
| 1097 | @atomicStore(i32, &self.os_data.fs_queue_item, 0, .SeqCst); | |
| 1098 | } | |
| 1099 | while (self.os_data.fs_queue.get()) |node| { | |
| 1044 | noasync while (true) { | |
| 1045 | self.fs_thread_wakeup.reset(); | |
| 1046 | while (self.fs_queue.get()) |node| { | |
| 1100 | 1047 | switch (node.data.msg) { |
| 1101 | 1048 | .end => return, |
| 1102 | 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 | 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 | 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 | 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 | 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 | 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 | 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 | 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 | 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 | 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 | 1083 | switch (node.data.finish) { |
| 1135 | 1084 | .TickNode => |*tick_node| self.onNextTick(tick_node), |
| ... | ... | @@ -1137,22 +1086,8 @@ pub const Loop = struct { |
| 1137 | 1086 | } |
| 1138 | 1087 | self.finishOneEvent(); |
| 1139 | 1088 | } |
| 1140 | switch (builtin.os.tag) { | |
| 1141 | .linux => { | |
| 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 | } | |
| 1089 | self.fs_thread_wakeup.wait(); | |
| 1090 | }; | |
| 1156 | 1091 | } |
| 1157 | 1092 | |
| 1158 | 1093 | const OsData = switch (builtin.os.tag) { |
| ... | ... | @@ -1168,22 +1103,12 @@ pub const Loop = struct { |
| 1168 | 1103 | const KEventData = struct { |
| 1169 | 1104 | kqfd: i32, |
| 1170 | 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 | 1108 | const LinuxOsData = struct { |
| 1180 | 1109 | epollfd: i32, |
| 1181 | 1110 | final_eventfd: i32, |
| 1182 | 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 | 1114 | pub const Request = struct { |
| ... | ... | @@ -1324,11 +1249,11 @@ test "std.event.Loop - basic" { |
| 1324 | 1249 | loop.run(); |
| 1325 | 1250 | } |
| 1326 | 1251 | |
| 1327 | async fn testEventLoop() i32 { | |
| 1252 | fn testEventLoop() i32 { | |
| 1328 | 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 | 1257 | const value = await h; |
| 1333 | 1258 | testing.expect(value == 1234); |
| 1334 | 1259 | did_it.* = true; |
lib/std/fs.zig+78-71| ... | ... | @@ -8,6 +8,8 @@ const Allocator = std.mem.Allocator; |
| 8 | 8 | const assert = std.debug.assert; |
| 9 | 9 | const math = std.math; |
| 10 | 10 | |
| 11 | const is_darwin = std.Target.current.os.tag.isDarwin(); | |
| 12 | ||
| 11 | 13 | pub const path = @import("fs/path.zig"); |
| 12 | 14 | pub const File = @import("fs/file.zig").File; |
| 13 | 15 | |
| ... | ... | @@ -197,7 +199,7 @@ pub const AtomicFile = struct { |
| 197 | 199 | if (std.Target.current.os.tag == .windows) { |
| 198 | 200 | const dest_path_w = try os.windows.sliceToPrefixedFileW(self.dest_basename); |
| 199 | 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 | 203 | self.file_exists = false; |
| 202 | 204 | } else { |
| 203 | 205 | const dest_path_c = try os.toPosixPath(self.dest_basename); |
| ... | ... | @@ -580,7 +582,7 @@ pub const Dir = struct { |
| 580 | 582 | pub fn openFile(self: Dir, sub_path: []const u8, flags: File.OpenFlags) File.OpenError!File { |
| 581 | 583 | if (builtin.os.tag == .windows) { |
| 582 | 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 | 587 | const path_c = try os.toPosixPath(sub_path); |
| 586 | 588 | return self.openFileZ(&path_c, flags); |
| ... | ... | @@ -592,13 +594,16 @@ pub const Dir = struct { |
| 592 | 594 | pub fn openFileZ(self: Dir, sub_path: [*:0]const u8, flags: File.OpenFlags) File.OpenError!File { |
| 593 | 595 | if (builtin.os.tag == .windows) { |
| 594 | 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 | 600 | // Use the O_ locking flags if the os supports them |
| 599 | 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(); | |
| 601 | const nonblocking_lock_flag = if (has_flock_open_flags and flags.lock_nonblocking) (os.O_NONBLOCK | os.O_SYNC) else @as(u32, 0); | |
| 602 | const has_flock_open_flags = @hasDecl(os, "O_EXLOCK") and !is_darwin; | |
| 603 | const nonblocking_lock_flag = if (has_flock_open_flags and flags.lock_nonblocking) | |
| 604 | os.O_NONBLOCK | os.O_SYNC | |
| 605 | else | |
| 606 | @as(u32, 0); | |
| 602 | 607 | const lock_flag: u32 = if (has_flock_open_flags) switch (flags.lock) { |
| 603 | 608 | .None => @as(u32, 0), |
| 604 | 609 | .Shared => os.O_SHLOCK | nonblocking_lock_flag, |
| ... | ... | @@ -606,14 +611,13 @@ pub const Dir = struct { |
| 606 | 611 | } else 0; |
| 607 | 612 | |
| 608 | 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; | |
| 610 | const os_flags = lock_flag | O_LARGEFILE | O_CLOEXEC | if (flags.write and flags.read) | |
| 614 | const os_flags = lock_flag | O_LARGEFILE | os.O_CLOEXEC | if (flags.write and flags.read) | |
| 611 | 615 | @as(u32, os.O_RDWR) |
| 612 | 616 | else if (flags.write) |
| 613 | 617 | @as(u32, os.O_WRONLY) |
| 614 | 618 | else |
| 615 | 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 | 621 | try std.event.Loop.instance.?.openatZ(self.fd, sub_path, os_flags, 0) |
| 618 | 622 | else |
| 619 | 623 | try os.openatZ(self.fd, sub_path, os_flags, 0); |
| ... | ... | @@ -630,31 +634,32 @@ pub const Dir = struct { |
| 630 | 634 | |
| 631 | 635 | return File{ |
| 632 | 636 | .handle = fd, |
| 633 | .io_mode = .blocking, | |
| 634 | .async_block_allowed = if (flags.always_blocking) | |
| 635 | File.async_block_allowed_yes | |
| 636 | else | |
| 637 | File.async_block_allowed_no, | |
| 637 | .capable_io_mode = .blocking, | |
| 638 | .intended_io_mode = flags.intended_io_mode, | |
| 638 | 639 | }; |
| 639 | 640 | } |
| 640 | 641 | |
| 641 | 642 | /// Same as `openFile` but Windows-only and the path parameter is |
| 642 | 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 | 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 | 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), | |
| 657 | .io_mode = .blocking, | |
| 647 | .handle = try os.windows.OpenFile(sub_path_w, .{ | |
| 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 | 669 | pub fn createFile(self: Dir, sub_path: []const u8, flags: File.CreateFlags) File.OpenError!File { |
| 665 | 670 | if (builtin.os.tag == .windows) { |
| 666 | 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 | 674 | const path_c = try os.toPosixPath(sub_path); |
| 670 | 675 | return self.createFileZ(&path_c, flags); |
| ... | ... | @@ -676,13 +681,16 @@ pub const Dir = struct { |
| 676 | 681 | pub fn createFileZ(self: Dir, sub_path_c: [*:0]const u8, flags: File.CreateFlags) File.OpenError!File { |
| 677 | 682 | if (builtin.os.tag == .windows) { |
| 678 | 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 | 687 | // Use the O_ locking flags if the os supports them |
| 683 | 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(); | |
| 685 | const nonblocking_lock_flag = if (has_flock_open_flags and flags.lock_nonblocking) (os.O_NONBLOCK | os.O_SYNC) else @as(u32, 0); | |
| 689 | const has_flock_open_flags = @hasDecl(os, "O_EXLOCK") and !is_darwin; | |
| 690 | const nonblocking_lock_flag: u32 = if (has_flock_open_flags and flags.lock_nonblocking) | |
| 691 | os.O_NONBLOCK | os.O_SYNC | |
| 692 | else | |
| 693 | 0; | |
| 686 | 694 | const lock_flag: u32 = if (has_flock_open_flags) switch (flags.lock) { |
| 687 | 695 | .None => @as(u32, 0), |
| 688 | 696 | .Shared => os.O_SHLOCK, |
| ... | ... | @@ -690,12 +698,11 @@ pub const Dir = struct { |
| 690 | 698 | } else 0; |
| 691 | 699 | |
| 692 | 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; | |
| 694 | const os_flags = lock_flag | O_LARGEFILE | os.O_CREAT | O_CLOEXEC | | |
| 701 | const os_flags = lock_flag | O_LARGEFILE | os.O_CREAT | os.O_CLOEXEC | | |
| 695 | 702 | (if (flags.truncate) @as(u32, os.O_TRUNC) else 0) | |
| 696 | 703 | (if (flags.read) @as(u32, os.O_RDWR) else os.O_WRONLY) | |
| 697 | 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 | 706 | try std.event.Loop.instance.?.openatZ(self.fd, sub_path_c, os_flags, flags.mode) |
| 700 | 707 | else |
| 701 | 708 | try os.openatZ(self.fd, sub_path_c, os_flags, flags.mode); |
| ... | ... | @@ -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 | 727 | /// Same as `createFile` but Windows-only and the path parameter is |
| 717 | 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 | 730 | const w = os.windows; |
| 720 | const access_mask = w.SYNCHRONIZE | w.GENERIC_WRITE | | |
| 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 | ||
| 731 | const read_flag = if (flags.read) @as(u32, w.GENERIC_READ) else 0; | |
| 735 | 732 | return @as(File, .{ |
| 736 | .handle = try os.windows.OpenFileW(self.fd, sub_path_w, null, access_mask, share_access, flags.lock_nonblocking, creation), | |
| 737 | .io_mode = .blocking, | |
| 733 | .handle = try os.windows.OpenFile(sub_path_w, .{ | |
| 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 | 832 | /// `true` means the opened directory can be scanned for the files and sub-directories |
| 819 | 833 | /// of the result. It means the `iterate` function can be called. |
| 820 | 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 | 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 | 841 | pub fn openDir(self: Dir, sub_path: []const u8, args: OpenDirOptions) OpenError!Dir { |
| 833 | 842 | if (builtin.os.tag == .windows) { |
| 834 | 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 | 845 | } else { |
| 837 | 846 | const sub_path_c = try os.toPosixPath(sub_path); |
| 838 | 847 | return self.openDirZ(&sub_path_c, args); |
| ... | ... | @@ -845,14 +854,12 @@ pub const Dir = struct { |
| 845 | 854 | pub fn openDirZ(self: Dir, sub_path_c: [*:0]const u8, args: OpenDirOptions) OpenError!Dir { |
| 846 | 855 | if (builtin.os.tag == .windows) { |
| 847 | 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 | 858 | } else if (!args.iterate) { |
| 850 | 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; | |
| 852 | return self.openDirFlagsZ(sub_path_c, os.O_DIRECTORY | os.O_RDONLY | O_CLOEXEC | O_PATH); | |
| 860 | return self.openDirFlagsZ(sub_path_c, os.O_DIRECTORY | os.O_RDONLY | os.O_CLOEXEC | O_PATH); | |
| 853 | 861 | } else { |
| 854 | const O_CLOEXEC: u32 = if (args.share_with_child_process) 0 else os.O_CLOEXEC; | |
| 855 | return self.openDirFlagsZ(sub_path_c, os.O_DIRECTORY | os.O_RDONLY | O_CLOEXEC); | |
| 862 | return self.openDirFlagsZ(sub_path_c, os.O_DIRECTORY | os.O_RDONLY | os.O_CLOEXEC); | |
| 856 | 863 | } |
| 857 | 864 | } |
| 858 | 865 | |
| ... | ... | @@ -989,7 +996,7 @@ pub const Dir = struct { |
| 989 | 996 | pub fn deleteDir(self: Dir, sub_path: []const u8) DeleteDirError!void { |
| 990 | 997 | if (builtin.os.tag == .windows) { |
| 991 | 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 | 1001 | const sub_path_c = try os.toPosixPath(sub_path); |
| 995 | 1002 | return self.deleteDirZ(&sub_path_c); |
| ... | ... | @@ -1248,7 +1255,7 @@ pub const Dir = struct { |
| 1248 | 1255 | pub fn access(self: Dir, sub_path: []const u8, flags: File.OpenFlags) AccessError!void { |
| 1249 | 1256 | if (builtin.os.tag == .windows) { |
| 1250 | 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 | 1260 | const path_c = try os.toPosixPath(sub_path); |
| 1254 | 1261 | return self.accessZ(&path_c, flags); |
| ... | ... | @@ -1258,7 +1265,7 @@ pub const Dir = struct { |
| 1258 | 1265 | pub fn accessZ(self: Dir, sub_path: [*:0]const u8, flags: File.OpenFlags) AccessError!void { |
| 1259 | 1266 | if (builtin.os.tag == .windows) { |
| 1260 | 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 | 1270 | const os_mode = if (flags.write and flags.read) |
| 1264 | 1271 | @as(u32, os.R_OK | os.W_OK) |
| ... | ... | @@ -1266,7 +1273,7 @@ pub const Dir = struct { |
| 1266 | 1273 | @as(u32, os.W_OK) |
| 1267 | 1274 | else |
| 1268 | 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 | 1277 | std.event.Loop.instance.?.faccessatZ(self.fd, sub_path, os_mode, 0) |
| 1271 | 1278 | else |
| 1272 | 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 | 1415 | } |
| 1409 | 1416 | |
| 1410 | 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 { | |
| 1412 | assert(path.isAbsoluteWindowsW(absolute_path_w)); | |
| 1418 | pub fn openFileAbsoluteW(absolute_path_w: []const u16, flags: File.OpenFlags) File.OpenError!File { | |
| 1419 | assert(path.isAbsoluteWindowsWTF16(absolute_path_w)); | |
| 1413 | 1420 | return cwd().openFileW(absolute_path_w, flags); |
| 1414 | 1421 | } |
| 1415 | 1422 | |
| ... | ... | @@ -1598,7 +1605,7 @@ pub fn openSelfExe() OpenSelfExeError!File { |
| 1598 | 1605 | if (builtin.os.tag == .windows) { |
| 1599 | 1606 | const wide_slice = selfExePathW(); |
| 1600 | 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 | 1610 | var buf: [MAX_PATH_BYTES]u8 = undefined; |
| 1604 | 1611 | const self_exe_path = try selfExePath(&buf); |
| ... | ... | @@ -1626,7 +1633,7 @@ pub fn selfExePathAlloc(allocator: *Allocator) ![]u8 { |
| 1626 | 1633 | /// been deleted, the file path looks something like `/a/b/c/exe (deleted)`. |
| 1627 | 1634 | /// TODO make the return type of this a null terminated pointer |
| 1628 | 1635 | pub fn selfExePath(out_buffer: *[MAX_PATH_BYTES]u8) SelfExePathError![]u8 { |
| 1629 | if (comptime std.Target.current.isDarwin()) { | |
| 1636 | if (is_darwin) { | |
| 1630 | 1637 | var u32_len: u32 = out_buffer.len; |
| 1631 | 1638 | const rc = std.c._NSGetExecutablePath(out_buffer, &u32_len); |
| 1632 | 1639 | if (rc != 0) return error.NameTooLong; |
lib/std/fs/file.zig+62-34| ... | ... | @@ -8,7 +8,7 @@ const assert = std.debug.assert; |
| 8 | 8 | const windows = os.windows; |
| 9 | 9 | const Os = builtin.Os; |
| 10 | 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 | 13 | pub const File = struct { |
| 14 | 14 | /// The OS-specific file descriptor or file handle. |
| ... | ... | @@ -17,15 +17,14 @@ pub const File = struct { |
| 17 | 17 | /// On some systems, such as Linux, file system file descriptors are incapable of non-blocking I/O. |
| 18 | 18 | /// This forces us to perform asynchronous I/O on a dedicated thread, to achieve non-blocking |
| 19 | 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. | |
| 21 | io_mode: io.Mode, | |
| 20 | /// or, more specifically, whether the I/O is always blocking. | |
| 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 | |
| 24 | /// not by default. For example, when printing a stack trace to stderr. | |
| 25 | async_block_allowed: @TypeOf(async_block_allowed_no) = async_block_allowed_no, | |
| 26 | ||
| 27 | pub const async_block_allowed_yes = if (io.is_async) true else {}; | |
| 28 | pub const async_block_allowed_no = if (io.is_async) false else {}; | |
| 23 | /// Furthermore, even when `std.io.mode` is async, it is still sometimes desirable to perform blocking I/O, | |
| 24 | /// although not by default. For example, when printing a stack trace to stderr. | |
| 25 | /// This field tracks both by acting as an overriding I/O mode. When not building in async I/O mode, | |
| 26 | /// the type only has the `.blocking` tag, making it a zero-bit type. | |
| 27 | intended_io_mode: io.ModeOverride = io.default_mode, | |
| 29 | 28 | |
| 30 | 29 | pub const Mode = os.mode_t; |
| 31 | 30 | |
| ... | ... | @@ -36,9 +35,7 @@ pub const File = struct { |
| 36 | 35 | |
| 37 | 36 | pub const OpenError = windows.CreateFileError || os.OpenError || os.FlockError; |
| 38 | 37 | |
| 39 | pub const Lock = enum { | |
| 40 | None, Shared, Exclusive | |
| 41 | }; | |
| 38 | pub const Lock = enum { None, Shared, Exclusive }; | |
| 42 | 39 | |
| 43 | 40 | /// TODO https://github.com/ziglang/zig/issues/3802 |
| 44 | 41 | pub const OpenFlags = struct { |
| ... | ... | @@ -63,17 +60,15 @@ pub const File = struct { |
| 63 | 60 | /// Sets whether or not to wait until the file is locked to return. If set to true, |
| 64 | 61 | /// `error.WouldBlock` will be returned. Otherwise, the file will wait until the file |
| 65 | 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 | 66 | lock_nonblocking: bool = false, |
| 67 | 67 | |
| 68 | /// This prevents `O_NONBLOCK` from being passed even if `std.io.is_async`. | |
| 69 | /// It allows the use of `noasync` when calling functions related to opening | |
| 70 | /// the file, reading, and writing. | |
| 71 | always_blocking: bool = false, | |
| 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, | |
| 68 | /// Setting this to `.blocking` prevents `O_NONBLOCK` from being passed even | |
| 69 | /// if `std.io.is_async`. It allows the use of `noasync` when calling functions | |
| 70 | /// related to opening the file, reading, writing, and locking. | |
| 71 | intended_io_mode: io.ModeOverride = io.default_mode, | |
| 77 | 72 | }; |
| 78 | 73 | |
| 79 | 74 | /// TODO https://github.com/ziglang/zig/issues/3802 |
| ... | ... | @@ -107,22 +102,27 @@ pub const File = struct { |
| 107 | 102 | /// Sets whether or not to wait until the file is locked to return. If set to true, |
| 108 | 103 | /// `error.WouldBlock` will be returned. Otherwise, the file will wait until the file |
| 109 | 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 | 108 | lock_nonblocking: bool = false, |
| 111 | 109 | |
| 112 | 110 | /// For POSIX systems this is the file system mode the file will |
| 113 | 111 | /// be created with. |
| 114 | 112 | mode: Mode = default_mode, |
| 115 | 113 | |
| 116 | /// `true` means the opened directory can be passed to a child process. | |
| 117 | /// `false` means the directory handle is considered to be closed when a child | |
| 118 | /// process is spawned. This corresponds to the inverse of `O_CLOEXEC` on POSIX. | |
| 119 | share_with_child_process: bool = false, | |
| 114 | /// Setting this to `.blocking` prevents `O_NONBLOCK` from being passed even | |
| 115 | /// if `std.io.is_async`. It allows the use of `noasync` when calling functions | |
| 116 | /// related to opening the file, reading, writing, and locking. | |
| 117 | intended_io_mode: io.ModeOverride = io.default_mode, | |
| 120 | 118 | }; |
| 121 | 119 | |
| 122 | 120 | /// Upon success, the stream is in an uninitialized state. To continue using it, |
| 123 | 121 | /// you must use the open() function. |
| 124 | 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 | 126 | std.event.Loop.instance.?.close(self.handle); |
| 127 | 127 | } else { |
| 128 | 128 | os.close(self.handle); |
| ... | ... | @@ -305,7 +305,9 @@ pub const File = struct { |
| 305 | 305 | pub const PReadError = os.PReadError; |
| 306 | 306 | |
| 307 | 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 | 311 | return std.event.Loop.instance.?.read(self.handle, buffer); |
| 310 | 312 | } else { |
| 311 | 313 | return os.read(self.handle, buffer); |
| ... | ... | @@ -325,7 +327,9 @@ pub const File = struct { |
| 325 | 327 | } |
| 326 | 328 | |
| 327 | 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 | 333 | return std.event.Loop.instance.?.pread(self.handle, buffer, offset); |
| 330 | 334 | } else { |
| 331 | 335 | return os.pread(self.handle, buffer, offset); |
| ... | ... | @@ -345,7 +349,12 @@ pub const File = struct { |
| 345 | 349 | } |
| 346 | 350 | |
| 347 | 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 | 358 | return std.event.Loop.instance.?.readv(self.handle, iovecs); |
| 350 | 359 | } else { |
| 351 | 360 | return os.readv(self.handle, iovecs); |
| ... | ... | @@ -379,7 +388,12 @@ pub const File = struct { |
| 379 | 388 | } |
| 380 | 389 | |
| 381 | 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 | 397 | return std.event.Loop.instance.?.preadv(self.handle, iovecs, offset); |
| 384 | 398 | } else { |
| 385 | 399 | return os.preadv(self.handle, iovecs, offset); |
| ... | ... | @@ -416,7 +430,9 @@ pub const File = struct { |
| 416 | 430 | pub const PWriteError = os.PWriteError; |
| 417 | 431 | |
| 418 | 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 | 436 | return std.event.Loop.instance.?.write(self.handle, bytes); |
| 421 | 437 | } else { |
| 422 | 438 | return os.write(self.handle, bytes); |
| ... | ... | @@ -431,7 +447,9 @@ pub const File = struct { |
| 431 | 447 | } |
| 432 | 448 | |
| 433 | 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 | 453 | return std.event.Loop.instance.?.pwrite(self.handle, bytes, offset); |
| 436 | 454 | } else { |
| 437 | 455 | return os.pwrite(self.handle, bytes, offset); |
| ... | ... | @@ -446,7 +464,12 @@ pub const File = struct { |
| 446 | 464 | } |
| 447 | 465 | |
| 448 | 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 | 473 | return std.event.Loop.instance.?.writev(self.handle, iovecs); |
| 451 | 474 | } else { |
| 452 | 475 | return os.writev(self.handle, iovecs); |
| ... | ... | @@ -472,7 +495,12 @@ pub const File = struct { |
| 472 | 495 | } |
| 473 | 496 | |
| 474 | 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 | 504 | return std.event.Loop.instance.?.pwritev(self.handle, iovecs, offset); |
| 477 | 505 | } else { |
| 478 | 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 | 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 | 184 | pub const isAbsoluteWindowsC = @compileError("deprecated: renamed to isAbsoluteWindowsZ"); |
| 181 | 185 | |
| 182 | 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 | 27 | } |
| 28 | 28 | |
| 29 | 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 | 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 | 63 | test "create file, lock and read from multiple process at once" { |
| 59 | 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 | 71 | if (true) { |
| 62 | 72 | // https://github.com/ziglang/zig/issues/5006 |
| 63 | 73 | return error.SkipZigTest; |
lib/std/io.zig+17-4| ... | ... | @@ -30,6 +30,11 @@ else |
| 30 | 30 | Mode.blocking; |
| 31 | 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 | 38 | fn getStdOutHandle() os.fd_t { |
| 34 | 39 | if (builtin.os.tag == .windows) { |
| 35 | 40 | return os.windows.peb().ProcessParameters.hStdOutput; |
| ... | ... | @@ -42,10 +47,13 @@ fn getStdOutHandle() os.fd_t { |
| 42 | 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 | 52 | pub fn getStdOut() File { |
| 46 | 53 | return File{ |
| 47 | 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 | 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 | 74 | pub fn getStdErr() File { |
| 65 | 75 | return File{ |
| 66 | 76 | .handle = getStdErrHandle(), |
| 67 | .io_mode = .blocking, | |
| 68 | .async_block_allowed = File.async_block_allowed_yes, | |
| 77 | .capable_io_mode = .blocking, | |
| 78 | .intended_io_mode = .blocking, | |
| 69 | 79 | }; |
| 70 | 80 | } |
| 71 | 81 | |
| ... | ... | @@ -81,10 +91,13 @@ fn getStdInHandle() os.fd_t { |
| 81 | 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 | 96 | pub fn getStdIn() File { |
| 85 | 97 | return File{ |
| 86 | 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 | 412 | errdefer os.close(sockfd); |
| 413 | 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 | 418 | /// Call `AddressList.deinit` on the result. |
| ... | ... | @@ -1381,10 +1381,7 @@ pub const StreamServer = struct { |
| 1381 | 1381 | var adr_len: os.socklen_t = @sizeOf(Address); |
| 1382 | 1382 | if (os.accept4(self.sockfd.?, &accepted_addr.any, &adr_len, accept_flags)) |fd| { |
| 1383 | 1383 | return Connection{ |
| 1384 | .file = fs.File{ | |
| 1385 | .handle = fd, | |
| 1386 | .io_mode = std.io.mode, | |
| 1387 | }, | |
| 1384 | .file = fs.File{ .handle = fd }, | |
| 1388 | 1385 | .address = accepted_addr, |
| 1389 | 1386 | }; |
| 1390 | 1387 | } else |err| switch (err) { |
lib/std/os.zig+59-44| ... | ... | @@ -177,8 +177,8 @@ fn getRandomBytesDevURandom(buf: []u8) !void { |
| 177 | 177 | |
| 178 | 178 | const file = std.fs.File{ |
| 179 | 179 | .handle = fd, |
| 180 | .io_mode = .blocking, | |
| 181 | .async_block_allowed = std.fs.File.async_block_allowed_yes, | |
| 180 | .capable_io_mode = .blocking, | |
| 181 | .intended_io_mode = .blocking, | |
| 182 | 182 | }; |
| 183 | 183 | const stream = file.inStream(); |
| 184 | 184 | stream.readNoEof(buf) catch return error.Unexpected; |
| ... | ... | @@ -309,7 +309,7 @@ pub const ReadError = error{ |
| 309 | 309 | /// For POSIX the limit is `math.maxInt(isize)`. |
| 310 | 310 | pub fn read(fd: fd_t, buf: []u8) ReadError!usize { |
| 311 | 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 | 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 | 369 | /// On these systems, the read races with concurrent writes to the same file descriptor. |
| 370 | 370 | pub fn readv(fd: fd_t, iov: []const iovec) ReadError!usize { |
| 371 | 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 | 373 | if (iov.len == 0) return @as(usize, 0); |
| 374 | 374 | const first = iov[0]; |
| 375 | 375 | return read(fd, first.iov_base[0..first.iov_len]); |
| ... | ... | @@ -412,7 +412,7 @@ pub const PReadError = ReadError || error{Unseekable}; |
| 412 | 412 | /// used to perform the I/O. `error.WouldBlock` is not possible on Windows. |
| 413 | 413 | pub fn pread(fd: fd_t, buf: []u8, offset: u64) PReadError!usize { |
| 414 | 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 | 418 | while (true) { |
| ... | ... | @@ -588,7 +588,7 @@ pub const WriteError = error{ |
| 588 | 588 | /// The corresponding POSIX limit is `math.maxInt(isize)`. |
| 589 | 589 | pub fn write(fd: fd_t, bytes: []const u8) WriteError!usize { |
| 590 | 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 | 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 | 655 | /// If `iov.len` is larger than will fit in a `u31`, a partial write will occur. |
| 656 | 656 | pub fn writev(fd: fd_t, iov: []const iovec_const) WriteError!usize { |
| 657 | 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 | 659 | if (iov.len == 0) return @as(usize, 0); |
| 660 | 660 | const first = iov[0]; |
| 661 | 661 | return write(fd, first.iov_base[0..first.iov_len]); |
| ... | ... | @@ -713,7 +713,7 @@ pub const PWriteError = WriteError || error{Unseekable}; |
| 713 | 713 | /// The corresponding POSIX limit is `math.maxInt(isize)`. |
| 714 | 714 | pub fn pwrite(fd: fd_t, bytes: []const u8, offset: u64) PWriteError!usize { |
| 715 | 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 | 719 | // Prevent EINVAL. |
| ... | ... | @@ -858,8 +858,11 @@ pub const OpenError = error{ |
| 858 | 858 | |
| 859 | 859 | /// Open and possibly create a file. Keeps trying if it gets interrupted. |
| 860 | 860 | /// See also `openC`. |
| 861 | /// TODO support windows | |
| 862 | 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 | 866 | const file_path_c = try toPosixPath(file_path); |
| 864 | 867 | return openZ(&file_path_c, flags, perm); |
| 865 | 868 | } |
| ... | ... | @@ -868,8 +871,11 @@ pub const openC = @compileError("deprecated: renamed to openZ"); |
| 868 | 871 | |
| 869 | 872 | /// Open and possibly create a file. Keeps trying if it gets interrupted. |
| 870 | 873 | /// See also `open`. |
| 871 | /// TODO support windows | |
| 872 | 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 | 879 | while (true) { |
| 874 | 880 | const rc = system.open(file_path, flags, perm); |
| 875 | 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 | 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 | 915 | /// Open and possibly create a file. Keeps trying if it gets interrupted. |
| 903 | 916 | /// `file_path` is relative to the open directory handle `dir_fd`. |
| 904 | 917 | /// See also `openatC`. |
| ... | ... | @@ -1308,7 +1321,7 @@ pub fn symlink(target_path: []const u8, sym_link_path: []const u8) SymLinkError! |
| 1308 | 1321 | if (builtin.os.tag == .windows) { |
| 1309 | 1322 | const target_path_w = try windows.sliceToPrefixedFileW(target_path); |
| 1310 | 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 | 1325 | } else { |
| 1313 | 1326 | const target_path_c = try toPosixPath(target_path); |
| 1314 | 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 | 1337 | if (builtin.os.tag == .windows) { |
| 1325 | 1338 | const target_path_w = try windows.cStrToPrefixedFileW(target_path); |
| 1326 | 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 | 1342 | switch (errno(system.symlink(target_path, sym_link_path))) { |
| 1330 | 1343 | 0 => return, |
| ... | ... | @@ -1400,7 +1413,7 @@ pub const UnlinkError = error{ |
| 1400 | 1413 | pub fn unlink(file_path: []const u8) UnlinkError!void { |
| 1401 | 1414 | if (builtin.os.tag == .windows) { |
| 1402 | 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 | 1417 | } else { |
| 1405 | 1418 | const file_path_c = try toPosixPath(file_path); |
| 1406 | 1419 | return unlinkZ(&file_path_c); |
| ... | ... | @@ -1413,7 +1426,7 @@ pub const unlinkC = @compileError("deprecated: renamed to unlinkZ"); |
| 1413 | 1426 | pub fn unlinkZ(file_path: [*:0]const u8) UnlinkError!void { |
| 1414 | 1427 | if (builtin.os.tag == .windows) { |
| 1415 | 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 | 1431 | switch (errno(system.unlink(file_path))) { |
| 1419 | 1432 | 0 => return, |
| ... | ... | @@ -1444,7 +1457,7 @@ pub const UnlinkatError = UnlinkError || error{ |
| 1444 | 1457 | pub fn unlinkat(dirfd: fd_t, file_path: []const u8, flags: u32) UnlinkatError!void { |
| 1445 | 1458 | if (builtin.os.tag == .windows) { |
| 1446 | 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 | 1462 | const file_path_c = try toPosixPath(file_path); |
| 1450 | 1463 | return unlinkatZ(dirfd, &file_path_c, flags); |
| ... | ... | @@ -1456,7 +1469,7 @@ pub const unlinkatC = @compileError("deprecated: renamed to unlinkatZ"); |
| 1456 | 1469 | pub fn unlinkatZ(dirfd: fd_t, file_path_c: [*:0]const u8, flags: u32) UnlinkatError!void { |
| 1457 | 1470 | if (builtin.os.tag == .windows) { |
| 1458 | 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 | 1474 | switch (errno(system.unlinkat(dirfd, file_path_c, flags))) { |
| 1462 | 1475 | 0 => return, |
| ... | ... | @@ -1571,7 +1584,7 @@ pub fn rename(old_path: []const u8, new_path: []const u8) RenameError!void { |
| 1571 | 1584 | if (builtin.os.tag == .windows) { |
| 1572 | 1585 | const old_path_w = try windows.sliceToPrefixedFileW(old_path); |
| 1573 | 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 | 1588 | } else { |
| 1576 | 1589 | const old_path_c = try toPosixPath(old_path); |
| 1577 | 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 | 1599 | if (builtin.os.tag == .windows) { |
| 1587 | 1600 | const old_path_w = try windows.cStrToPrefixedFileW(old_path); |
| 1588 | 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 | 1604 | switch (errno(system.rename(old_path, new_path))) { |
| 1592 | 1605 | 0 => return, |
| ... | ... | @@ -1629,7 +1642,7 @@ pub fn renameat( |
| 1629 | 1642 | if (builtin.os.tag == .windows) { |
| 1630 | 1643 | const old_path_w = try windows.sliceToPrefixedFileW(old_path); |
| 1631 | 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 | 1646 | } else { |
| 1634 | 1647 | const old_path_c = try toPosixPath(old_path); |
| 1635 | 1648 | const new_path_c = try toPosixPath(new_path); |
| ... | ... | @@ -1647,7 +1660,7 @@ pub fn renameatZ( |
| 1647 | 1660 | if (builtin.os.tag == .windows) { |
| 1648 | 1661 | const old_path_w = try windows.cStrToPrefixedFileW(old_path); |
| 1649 | 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 | 1666 | switch (errno(system.renameat(old_dir_fd, old_path, new_dir_fd, new_path))) { |
| ... | ... | @@ -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. | |
| 1678 | /// Assumes target is Windows. | |
| 1679 | /// TODO these args can actually be slices when using ntdll. audit the rest of the W functions too. | |
| 1690 | /// Same as `renameat` but Windows-only and the path parameters are | |
| 1691 | /// [WTF-16](https://simonsapin.github.io/wtf-8/#potentially-ill-formed-utf-16) encoded. | |
| 1680 | 1692 | pub fn renameatW( |
| 1681 | 1693 | old_dir_fd: fd_t, |
| 1682 | old_path: [*:0]const u16, | |
| 1694 | old_path_w: []const u16, | |
| 1683 | 1695 | new_dir_fd: fd_t, |
| 1684 | new_path_w: [*:0]const u16, | |
| 1696 | new_path_w: []const u16, | |
| 1685 | 1697 | ReplaceIfExists: windows.BOOLEAN, |
| 1686 | 1698 | ) RenameError!void { |
| 1687 | const access_mask = windows.SYNCHRONIZE | windows.GENERIC_WRITE | windows.DELETE; | |
| 1688 | const src_fd = windows.OpenFileW(old_dir_fd, old_path, null, access_mask, null, false, windows.FILE_OPEN) catch |err| switch (err) { | |
| 1689 | error.WouldBlock => unreachable, | |
| 1699 | const src_fd = windows.OpenFile(old_path_w, .{ | |
| 1700 | .dir = old_dir_fd, | |
| 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 | 1706 | else => |e| return e, |
| 1691 | 1707 | }; |
| 1692 | 1708 | defer windows.CloseHandle(src_fd); |
| 1693 | 1709 | |
| 1694 | 1710 | const struct_buf_len = @sizeOf(windows.FILE_RENAME_INFORMATION) + (MAX_PATH_BYTES - 1); |
| 1695 | 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); | |
| 1697 | const struct_len = @sizeOf(windows.FILE_RENAME_INFORMATION) - 1 + new_path.len * 2; | |
| 1712 | const struct_len = @sizeOf(windows.FILE_RENAME_INFORMATION) - 1 + new_path_w.len * 2; | |
| 1698 | 1713 | if (struct_len > struct_buf_len) return error.NameTooLong; |
| 1699 | 1714 | |
| 1700 | 1715 | const rename_info = @ptrCast(*windows.FILE_RENAME_INFORMATION, &rename_info_buf); |
| 1701 | 1716 | |
| 1702 | 1717 | rename_info.* = .{ |
| 1703 | 1718 | .ReplaceIfExists = ReplaceIfExists, |
| 1704 | .RootDirectory = if (std.fs.path.isAbsoluteWindowsW(new_path_w)) null else new_dir_fd, | |
| 1705 | .FileNameLength = @intCast(u32, new_path.len * 2), // already checked error.NameTooLong | |
| 1719 | .RootDirectory = if (std.fs.path.isAbsoluteWindowsWTF16(new_path_w)) null else new_dir_fd, | |
| 1720 | .FileNameLength = @intCast(u32, new_path_w.len * 2), // already checked error.NameTooLong | |
| 1706 | 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 | 1725 | var io_status_block: windows.IO_STATUS_BLOCK = undefined; |
| 1711 | 1726 | |
| ... | ... | @@ -1749,7 +1764,7 @@ pub const MakeDirError = error{ |
| 1749 | 1764 | pub fn mkdirat(dir_fd: fd_t, sub_dir_path: []const u8, mode: u32) MakeDirError!void { |
| 1750 | 1765 | if (builtin.os.tag == .windows) { |
| 1751 | 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 | 1768 | } else { |
| 1754 | 1769 | const sub_dir_path_c = try toPosixPath(sub_dir_path); |
| 1755 | 1770 | return mkdiratZ(dir_fd, &sub_dir_path_c, mode); |
| ... | ... | @@ -1761,7 +1776,7 @@ pub const mkdiratC = @compileError("deprecated: renamed to mkdiratZ"); |
| 1761 | 1776 | pub fn mkdiratZ(dir_fd: fd_t, sub_dir_path: [*:0]const u8, mode: u32) MakeDirError!void { |
| 1762 | 1777 | if (builtin.os.tag == .windows) { |
| 1763 | 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 | 1781 | switch (errno(system.mkdirat(dir_fd, sub_dir_path, mode))) { |
| 1767 | 1782 | 0 => return, |
| ... | ... | @@ -1805,7 +1820,7 @@ pub fn mkdir(dir_path: []const u8, mode: u32) MakeDirError!void { |
| 1805 | 1820 | pub fn mkdirZ(dir_path: [*:0]const u8, mode: u32) MakeDirError!void { |
| 1806 | 1821 | if (builtin.os.tag == .windows) { |
| 1807 | 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 | 1824 | windows.CloseHandle(sub_dir_handle); |
| 1810 | 1825 | return; |
| 1811 | 1826 | } |
| ... | ... | @@ -1846,7 +1861,7 @@ pub const DeleteDirError = error{ |
| 1846 | 1861 | pub fn rmdir(dir_path: []const u8) DeleteDirError!void { |
| 1847 | 1862 | if (builtin.os.tag == .windows) { |
| 1848 | 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 | 1865 | } else { |
| 1851 | 1866 | const dir_path_c = try toPosixPath(dir_path); |
| 1852 | 1867 | return rmdirZ(&dir_path_c); |
| ... | ... | @@ -1859,7 +1874,7 @@ pub const rmdirC = @compileError("deprecated: renamed to rmdirZ"); |
| 1859 | 1874 | pub fn rmdirZ(dir_path: [*:0]const u8) DeleteDirError!void { |
| 1860 | 1875 | if (builtin.os.tag == .windows) { |
| 1861 | 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 | 1879 | switch (errno(system.rmdir(dir_path))) { |
| 1865 | 1880 | 0 => return, |
| ... | ... | @@ -2869,7 +2884,7 @@ pub const AccessError = error{ |
| 2869 | 2884 | pub fn access(path: []const u8, mode: u32) AccessError!void { |
| 2870 | 2885 | if (builtin.os.tag == .windows) { |
| 2871 | 2886 | const path_w = try windows.sliceToPrefixedFileW(path); |
| 2872 | _ = try windows.GetFileAttributesW(&path_w); | |
| 2887 | _ = try windows.GetFileAttributesW(path_w.span().ptr); | |
| 2873 | 2888 | return; |
| 2874 | 2889 | } |
| 2875 | 2890 | const path_c = try toPosixPath(path); |
| ... | ... | @@ -2882,7 +2897,7 @@ pub const accessC = @compileError("Deprecated in favor of `accessZ`"); |
| 2882 | 2897 | pub fn accessZ(path: [*:0]const u8, mode: u32) AccessError!void { |
| 2883 | 2898 | if (builtin.os.tag == .windows) { |
| 2884 | 2899 | const path_w = try windows.cStrToPrefixedFileW(path); |
| 2885 | _ = try windows.GetFileAttributesW(&path_w); | |
| 2900 | _ = try windows.GetFileAttributesW(path_w.span().ptr); | |
| 2886 | 2901 | return; |
| 2887 | 2902 | } |
| 2888 | 2903 | switch (errno(system.access(path, mode))) { |
| ... | ... | @@ -2923,7 +2938,7 @@ pub fn accessW(path: [*:0]const u16, mode: u32) windows.GetFileAttributesError!v |
| 2923 | 2938 | pub fn faccessat(dirfd: fd_t, path: []const u8, mode: u32, flags: u32) AccessError!void { |
| 2924 | 2939 | if (builtin.os.tag == .windows) { |
| 2925 | 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 | 2943 | const path_c = try toPosixPath(path); |
| 2929 | 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 | 2948 | pub fn faccessatZ(dirfd: fd_t, path: [*:0]const u8, mode: u32, flags: u32) AccessError!void { |
| 2934 | 2949 | if (builtin.os.tag == .windows) { |
| 2935 | 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 | 2953 | switch (errno(system.faccessat(dirfd, path, mode, flags))) { |
| 2939 | 2954 | 0 => return, |
| ... | ... | @@ -3288,7 +3303,7 @@ pub const RealPathError = error{ |
| 3288 | 3303 | pub fn realpath(pathname: []const u8, out_buffer: *[MAX_PATH_BYTES]u8) RealPathError![]u8 { |
| 3289 | 3304 | if (builtin.os.tag == .windows) { |
| 3290 | 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 | 3308 | const pathname_c = try toPosixPath(pathname); |
| 3294 | 3309 | return realpathZ(&pathname_c, out_buffer); |
| ... | ... | @@ -3300,7 +3315,7 @@ pub const realpathC = @compileError("deprecated: renamed realpathZ"); |
| 3300 | 3315 | pub fn realpathZ(pathname: [*:0]const u8, out_buffer: *[MAX_PATH_BYTES]u8) RealPathError![]u8 { |
| 3301 | 3316 | if (builtin.os.tag == .windows) { |
| 3302 | 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 | 3320 | if (builtin.os.tag == .linux and !builtin.link_libc) { |
| 3306 | 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 | 59 | hTemplateFile: ?HANDLE, |
| 60 | 60 | ) CreateFileError!HANDLE { |
| 61 | 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 | 65 | pub fn CreateFileW( |
| ... | ... | @@ -103,57 +103,59 @@ pub const OpenError = error{ |
| 103 | 103 | WouldBlock, |
| 104 | 104 | }; |
| 105 | 105 | |
| 106 | /// TODO rename to CreateFileW | |
| 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, | |
| 106 | pub const OpenFileOptions = struct { | |
| 112 | 107 | access_mask: ACCESS_MASK, |
| 113 | share_access_opt: ?ULONG, | |
| 114 | share_access_nonblocking: bool, | |
| 108 | dir: ?HANDLE = null, | |
| 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 | 112 | creation: ULONG, |
| 116 | ) OpenError!HANDLE { | |
| 117 | if (sub_path_w[0] == '.' and sub_path_w[1] == 0) { | |
| 113 | io_mode: std.io.ModeOverride, | |
| 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 | 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 | 123 | return error.IsDir; |
| 122 | 124 | } |
| 123 | 125 | |
| 124 | 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 | 129 | error.Overflow => return error.NameTooLong, |
| 128 | 130 | }; |
| 129 | 131 | var nt_name = UNICODE_STRING{ |
| 130 | 132 | .Length = path_len_bytes, |
| 131 | 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 | 136 | var attr = OBJECT_ATTRIBUTES{ |
| 135 | 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 | 139 | .Attributes = 0, // Note we do not use OBJ_CASE_INSENSITIVE here. |
| 138 | 140 | .ObjectName = &nt_name, |
| 139 | .SecurityDescriptor = if (sa) |ptr| ptr.lpSecurityDescriptor else null, | |
| 141 | .SecurityDescriptor = if (options.sa) |ptr| ptr.lpSecurityDescriptor else null, | |
| 140 | 142 | .SecurityQualityOfService = null, |
| 141 | 143 | }; |
| 142 | 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 | 146 | var delay: usize = 1; |
| 146 | 147 | while (true) { |
| 148 | const blocking_flag: ULONG = if (options.io_mode == .blocking) FILE_SYNCHRONOUS_IO_NONALERT else 0; | |
| 147 | 149 | const rc = ntdll.NtCreateFile( |
| 148 | 150 | &result, |
| 149 | access_mask, | |
| 151 | options.access_mask, | |
| 150 | 152 | &attr, |
| 151 | 153 | &io, |
| 152 | 154 | null, |
| 153 | 155 | FILE_ATTRIBUTE_NORMAL, |
| 154 | share_access, | |
| 155 | creation, | |
| 156 | FILE_NON_DIRECTORY_FILE | FILE_SYNCHRONOUS_IO_NONALERT, | |
| 156 | options.share_access, | |
| 157 | options.creation, | |
| 158 | FILE_NON_DIRECTORY_FILE | blocking_flag, | |
| 157 | 159 | null, |
| 158 | 160 | 0, |
| 159 | 161 | ); |
| ... | ... | @@ -165,14 +167,16 @@ pub fn OpenFileW( |
| 165 | 167 | .NO_MEDIA_IN_DEVICE => return error.NoDevice, |
| 166 | 168 | .INVALID_PARAMETER => unreachable, |
| 167 | 169 | .SHARING_VIOLATION => { |
| 168 | if (share_access_nonblocking) { | |
| 170 | if (options.share_access_nonblocking) { | |
| 169 | 171 | return error.WouldBlock; |
| 170 | 172 | } |
| 173 | // TODO sleep in a way that is interruptable | |
| 174 | // TODO integrate with async I/O | |
| 171 | 175 | std.time.sleep(delay); |
| 172 | 176 | if (delay < 1 * std.time.ns_per_s) { |
| 173 | 177 | delay *= 2; |
| 174 | 178 | } |
| 175 | continue; // TODO: don't loop for async | |
| 179 | continue; | |
| 176 | 180 | }, |
| 177 | 181 | .ACCESS_DENIED => return error.AccessDenied, |
| 178 | 182 | .PIPE_BUSY => return error.PipeBusy, |
| ... | ... | @@ -195,7 +199,7 @@ pub fn CreatePipe(rd: *HANDLE, wr: *HANDLE, sattr: *const SECURITY_ATTRIBUTES) C |
| 195 | 199 | |
| 196 | 200 | pub fn CreateEventEx(attributes: ?*SECURITY_ATTRIBUTES, name: []const u8, flags: DWORD, desired_access: DWORD) !HANDLE { |
| 197 | 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 | 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 | 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 | 335 | pub const CreateIoCompletionPortError = error{Unexpected}; |
| 368 | 336 | |
| 369 | 337 | pub fn CreateIoCompletionPort( |
| ... | ... | @@ -447,10 +415,11 @@ pub const ReadFileError = error{ |
| 447 | 415 | |
| 448 | 416 | /// If buffer's length exceeds what a Windows DWORD integer can hold, it will be broken into |
| 449 | 417 | /// multiple non-atomic reads. |
| 450 | pub fn ReadFile(in_hFile: HANDLE, buffer: []u8, offset: ?u64) ReadFileError!usize { | |
| 451 | if (std.event.Loop.instance) |loop| { | |
| 452 | // TODO support async ReadFile with no offset | |
| 453 | const off = offset.?; | |
| 418 | pub fn ReadFile(in_hFile: HANDLE, buffer: []u8, offset: ?u64, io_mode: std.io.ModeOverride) ReadFileError!usize { | |
| 419 | if (io_mode != .blocking) { | |
| 420 | const loop = std.event.Loop.instance.?; | |
| 421 | // TODO make getting the file position non-blocking | |
| 422 | const off = if (offset) |o| o else try SetFilePointerEx_CURRENT_get(in_hFile); | |
| 454 | 423 | var resume_node = std.event.Loop.ResumeNode.Basic{ |
| 455 | 424 | .base = .{ |
| 456 | 425 | .id = .Basic, |
| ... | ... | @@ -465,22 +434,27 @@ pub fn ReadFile(in_hFile: HANDLE, buffer: []u8, offset: ?u64) ReadFileError!usiz |
| 465 | 434 | }, |
| 466 | 435 | }; |
| 467 | 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 | 438 | loop.beginOneEvent(); |
| 470 | 439 | suspend { |
| 471 | 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; | |
| 475 | if (windows.kernel32.GetOverlappedResult(fd, &resume_node.base.overlapped, &bytes_transferred, windows.FALSE) == 0) { | |
| 476 | switch (windows.kernel32.GetLastError()) { | |
| 443 | var bytes_transferred: DWORD = undefined; | |
| 444 | if (kernel32.GetOverlappedResult(in_hFile, &resume_node.base.overlapped, &bytes_transferred, FALSE) == 0) { | |
| 445 | switch (kernel32.GetLastError()) { | |
| 477 | 446 | .IO_PENDING => unreachable, |
| 478 | 447 | .OPERATION_ABORTED => return error.OperationAborted, |
| 479 | 448 | .BROKEN_PIPE => return error.BrokenPipe, |
| 480 | 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 | 458 | return @as(usize, bytes_transferred); |
| 485 | 459 | } else { |
| 486 | 460 | var index: usize = 0; |
| ... | ... | @@ -520,10 +494,16 @@ pub const WriteFileError = error{ |
| 520 | 494 | Unexpected, |
| 521 | 495 | }; |
| 522 | 496 | |
| 523 | pub fn WriteFile(handle: HANDLE, bytes: []const u8, offset: ?u64) WriteFileError!usize { | |
| 524 | if (std.event.Loop.instance) |loop| { | |
| 525 | // TODO support async WriteFile with no offset | |
| 526 | const off = offset.?; | |
| 497 | pub fn WriteFile( | |
| 498 | handle: HANDLE, | |
| 499 | bytes: []const u8, | |
| 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 | 507 | var resume_node = std.event.Loop.ResumeNode.Basic{ |
| 528 | 508 | .base = .{ |
| 529 | 509 | .id = .Basic, |
| ... | ... | @@ -538,14 +518,14 @@ pub fn WriteFile(handle: HANDLE, bytes: []const u8, offset: ?u64) WriteFileError |
| 538 | 518 | }, |
| 539 | 519 | }; |
| 540 | 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 | 522 | loop.beginOneEvent(); |
| 543 | 523 | suspend { |
| 544 | const adjusted_len = math.cast(windows.DWORD, bytes.len) catch maxInt(windows.DWORD); | |
| 545 | _ = kernel32.WriteFile(fd, bytes.ptr, adjusted_len, null, &resume_node.base.overlapped); | |
| 524 | const adjusted_len = math.cast(DWORD, bytes.len) catch maxInt(DWORD); | |
| 525 | _ = kernel32.WriteFile(handle, bytes.ptr, adjusted_len, null, &resume_node.base.overlapped); | |
| 546 | 526 | } |
| 547 | var bytes_transferred: windows.DWORD = undefined; | |
| 548 | if (kernel32.GetOverlappedResult(fd, &resume_node.base.overlapped, &bytes_transferred, FALSE) == 0) { | |
| 527 | var bytes_transferred: DWORD = undefined; | |
| 528 | if (kernel32.GetOverlappedResult(handle, &resume_node.base.overlapped, &bytes_transferred, FALSE) == 0) { | |
| 549 | 529 | switch (kernel32.GetLastError()) { |
| 550 | 530 | .IO_PENDING => unreachable, |
| 551 | 531 | .INVALID_USER_BUFFER => return error.SystemResources, |
| ... | ... | @@ -553,9 +533,14 @@ pub fn WriteFile(handle: HANDLE, bytes: []const u8, offset: ?u64) WriteFileError |
| 553 | 533 | .OPERATION_ABORTED => return error.OperationAborted, |
| 554 | 534 | .NOT_ENOUGH_QUOTA => return error.SystemResources, |
| 555 | 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 | 544 | return bytes_transferred; |
| 560 | 545 | } else { |
| 561 | 546 | var bytes_written: DWORD = undefined; |
| ... | ... | @@ -623,7 +608,7 @@ pub fn CreateSymbolicLink( |
| 623 | 608 | ) CreateSymbolicLinkError!void { |
| 624 | 609 | const sym_link_path_w = try sliceToPrefixedFileW(sym_link_path); |
| 625 | 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 | 614 | pub fn CreateSymbolicLinkW( |
| ... | ... | @@ -648,7 +633,7 @@ pub const DeleteFileError = error{ |
| 648 | 633 | |
| 649 | 634 | pub fn DeleteFile(filename: []const u8) DeleteFileError!void { |
| 650 | 635 | const filename_w = try sliceToPrefixedFileW(filename); |
| 651 | return DeleteFileW(&filename_w); | |
| 636 | return DeleteFileW(filename_w.span().ptr); | |
| 652 | 637 | } |
| 653 | 638 | |
| 654 | 639 | pub fn DeleteFileW(filename: [*:0]const u16) DeleteFileError!void { |
| ... | ... | @@ -670,7 +655,7 @@ pub const MoveFileError = error{Unexpected}; |
| 670 | 655 | pub fn MoveFileEx(old_path: []const u8, new_path: []const u8, flags: DWORD) MoveFileError!void { |
| 671 | 656 | const old_path_w = try sliceToPrefixedFileW(old_path); |
| 672 | 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 | 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 | 680 | /// Returns an open directory handle which the caller is responsible for closing with `CloseHandle`. |
| 696 | 681 | pub fn CreateDirectory(dir: ?HANDLE, pathname: []const u8, sa: ?*SECURITY_ATTRIBUTES) CreateDirectoryError!HANDLE { |
| 697 | 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 | 686 | /// Same as `CreateDirectory` except takes a WTF-16 encoded path. |
| ... | ... | @@ -763,7 +748,7 @@ pub const RemoveDirectoryError = error{ |
| 763 | 748 | |
| 764 | 749 | pub fn RemoveDirectory(dir_path: []const u8) RemoveDirectoryError!void { |
| 765 | 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 | 754 | pub fn RemoveDirectoryW(dir_path_w: [*:0]const u16) RemoveDirectoryError!void { |
| ... | ... | @@ -892,7 +877,7 @@ pub const GetFileAttributesError = error{ |
| 892 | 877 | |
| 893 | 878 | pub fn GetFileAttributes(filename: []const u8) GetFileAttributesError!DWORD { |
| 894 | 879 | const filename_w = try sliceToPrefixedFileW(filename); |
| 895 | return GetFileAttributesW(&filename_w); | |
| 880 | return GetFileAttributesW(filename_w.span().ptr); | |
| 896 | 881 | } |
| 897 | 882 | |
| 898 | 883 | pub fn GetFileAttributesW(lpFileName: [*:0]const u16) GetFileAttributesError!DWORD { |
| ... | ... | @@ -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 { | |
| 1236 | return sliceToPrefixedFileW(mem.spanZ(s)); | |
| 1237 | } | |
| 1220 | pub const PathSpace = struct { | |
| 1221 | data: [PATH_MAX_WIDE:0]u16, | |
| 1222 | len: usize, | |
| 1238 | 1223 | |
| 1239 | pub fn sliceToPrefixedFileW(s: []const u8) ![PATH_MAX_WIDE:0]u16 { | |
| 1240 | return sliceToPrefixedSuffixedFileW(s, &[_]u16{}); | |
| 1241 | } | |
| 1242 | ||
| 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; | |
| 1224 | pub fn span(self: PathSpace) [:0]const u16 { | |
| 1225 | return self.data[0..self.len :0]; | |
| 1226 | } | |
| 1227 | }; | |
| 1247 | 1228 | |
| 1248 | const start_index = if (mem.startsWith(u16, s, &[_]u16{ '\\', '?' })) 0 else blk: { | |
| 1249 | const prefix = [_]u16{ '\\', '?', '?', '\\' }; | |
| 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; | |
| 1229 | pub fn cStrToPrefixedFileW(s: [*:0]const u8) !PathSpace { | |
| 1230 | return sliceToPrefixedFileW(mem.spanZ(s)); | |
| 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 | 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 | 1236 | for (s) |byte| { |
| 1264 | 1237 | switch (byte) { |
| 1265 | 1238 | '*', '?', '"', '<', '>', '|' => return error.BadPathName, |
| ... | ... | @@ -1268,25 +1241,46 @@ pub fn sliceToPrefixedSuffixedFileW(s: []const u8, comptime suffix: []const u16) |
| 1268 | 1241 | } |
| 1269 | 1242 | const start_index = if (mem.startsWith(u8, s, "\\?") or !std.fs.path.isAbsolute(s)) 0 else blk: { |
| 1270 | 1243 | const prefix = [_]u16{ '\\', '?', '?', '\\' }; |
| 1271 | mem.copy(u16, result[0..], &prefix); | |
| 1244 | mem.copy(u16, path_space.data[0..], &prefix); | |
| 1272 | 1245 | break :blk prefix.len; |
| 1273 | 1246 | }; |
| 1274 | const end_index = start_index + try std.unicode.utf8ToUtf16Le(result[start_index..], s); | |
| 1275 | if (end_index + suffix.len > result.len) return error.NameTooLong; | |
| 1247 | path_space.len = start_index + try std.unicode.utf8ToUtf16Le(path_space.data[start_index..], s); | |
| 1248 | if (path_space.len > path_space.data.len) return error.NameTooLong; | |
| 1276 | 1249 | // > File I/O functions in the Windows API convert "/" to "\" as part of |
| 1277 | 1250 | // > converting the name to an NT-style name, except when using the "\\?\" |
| 1278 | 1251 | // > prefix as detailed in the following sections. |
| 1279 | 1252 | // from https://docs.microsoft.com/en-us/windows/desktop/FileIO/naming-a-file#maximum-path-length-limitation |
| 1280 | 1253 | // Because we want the larger maximum path length for absolute paths, we |
| 1281 | 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 | 1256 | if (elem.* == '/') { |
| 1284 | 1257 | elem.* = '\\'; |
| 1285 | 1258 | } |
| 1286 | 1259 | } |
| 1287 | mem.copy(u16, result[end_index..], suffix); | |
| 1288 | result[end_index + suffix.len] = 0; | |
| 1289 | return result; | |
| 1260 | path_space.data[path_space.len] = 0; | |
| 1261 | return path_space; | |
| 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 | 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 | 470 | msf: Msf, |
| 471 | 471 | |
| 472 | 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 | 474 | self.allocator = coff_ptr.allocator; |
| 475 | 475 | self.coff = coff_ptr; |
| 476 | 476 |
lib/std/reset_event.zig+1-1| ... | ... | @@ -52,7 +52,7 @@ pub const ResetEvent = struct { |
| 52 | 52 | |
| 53 | 53 | /// Wait for the event to be set by blocking the current thread. |
| 54 | 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 | 56 | pub fn timedWait(self: *ResetEvent, timeout_ns: u64) !void { |
| 57 | 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 | 96 | case: ZIRCompareOutputCase, |
| 97 | 97 | target: std.Target, |
| 98 | 98 | ) !void { |
| 99 | var tmp = std.testing.tmpDir(.{ .share_with_child_process = true }); | |
| 99 | var tmp = std.testing.tmpDir(.{}); | |
| 100 | 100 | defer tmp.cleanup(); |
| 101 | 101 | |
| 102 | 102 | var prg_node = root_node.start(case.name, 4); |