authorgravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2025-10-27 13:21:49-07:00
committergravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2025-10-29 06:20:51-07:00
logdf4c30ca1631c26fce3bec1cba6a15a688745433
tree589a2a20defbcef98ed41901074285f5d60bd635
parent02119362d70bb16ae6ab00124928bde89834c83f

link: move the windows kernel bug workaround to Io implementation


2 files changed, 31 insertions(+), 25 deletions(-)

lib/std/Io/Threaded.zig+20-5
...@@ -2081,6 +2081,10 @@ fn dirOpenFileWindowsInner(...@@ -2081,6 +2081,10 @@ fn dirOpenFileWindowsInner(
2081 const create_file_flags: w.ULONG = file_or_dir_flag |2081 const create_file_flags: w.ULONG = file_or_dir_flag |
2082 if (flags.follow_symlinks) blocking_flag else w.FILE_OPEN_REPARSE_POINT;2082 if (flags.follow_symlinks) blocking_flag else w.FILE_OPEN_REPARSE_POINT;
20832083
2084 // There are multiple kernel bugs being worked around with retries.
2085 const max_attempts = 13;
2086 var attempt: u5 = 0;
2087
2084 const handle = while (true) {2088 const handle = while (true) {
2085 try t.checkCancel();2089 try t.checkCancel();
20862090
...@@ -2110,7 +2114,17 @@ fn dirOpenFileWindowsInner(...@@ -2110,7 +2114,17 @@ fn dirOpenFileWindowsInner(
2110 .NO_MEDIA_IN_DEVICE => return error.NoDevice,2114 .NO_MEDIA_IN_DEVICE => return error.NoDevice,
2111 .INVALID_PARAMETER => |err| return w.statusBug(err),2115 .INVALID_PARAMETER => |err| return w.statusBug(err),
2112 .SHARING_VIOLATION => return error.AccessDenied,2116 .SHARING_VIOLATION => return error.AccessDenied,
2113 .ACCESS_DENIED => return error.AccessDenied,2117 .ACCESS_DENIED => {
2118 // This occurs if the file attempting to be opened is a running
2119 // executable. However, there's a kernel bug: the error may be
2120 // incorrectly returned for an indeterminate amount of time
2121 // after an executable file is closed. Here we work around the
2122 // kernel bug with retry attempts.
2123 if (attempt - max_attempts == 0) return error.AccessDenied;
2124 _ = w.kernel32.SleepEx((@as(u32, 1) << attempt) >> 1, w.TRUE);
2125 attempt += 1;
2126 continue;
2127 },
2114 .PIPE_BUSY => return error.PipeBusy,2128 .PIPE_BUSY => return error.PipeBusy,
2115 .PIPE_NOT_AVAILABLE => return error.NoDevice,2129 .PIPE_NOT_AVAILABLE => return error.NoDevice,
2116 .OBJECT_PATH_SYNTAX_BAD => |err| return w.statusBug(err),2130 .OBJECT_PATH_SYNTAX_BAD => |err| return w.statusBug(err),
...@@ -2123,10 +2137,11 @@ fn dirOpenFileWindowsInner(...@@ -2123,10 +2137,11 @@ fn dirOpenFileWindowsInner(
2123 // This error means that there *was* a file in this location on2137 // This error means that there *was* a file in this location on
2124 // the file system, but it was deleted. However, the OS is not2138 // the file system, but it was deleted. However, the OS is not
2125 // finished with the deletion operation, and so this CreateFile2139 // finished with the deletion operation, and so this CreateFile
2126 // call has failed. There is not really a sane way to handle2140 // call has failed. Here, we simulate the kernel bug being
2127 // this other than retrying the creation after the OS finishes2141 // fixed by sleeping and retrying until the error goes away.
2128 // the deletion.2142 if (attempt - max_attempts == 0) return error.AccessDenied;
2129 _ = w.kernel32.SleepEx(1, w.FALSE);2143 _ = w.kernel32.SleepEx((@as(u32, 1) << attempt) >> 1, w.TRUE);
2144 attempt += 1;
2130 continue;2145 continue;
2131 },2146 },
2132 .VIRUS_INFECTED, .VIRUS_DELETED => return error.AntivirusInterference,2147 .VIRUS_INFECTED, .VIRUS_DELETED => return error.AntivirusInterference,
src/link.zig+11-20
...@@ -1,19 +1,22 @@...@@ -1,19 +1,22 @@
1const std = @import("std");
2const build_options = @import("build_options");
3const builtin = @import("builtin");1const builtin = @import("builtin");
2const build_options = @import("build_options");
3
4const std = @import("std");
5const Io = std.Io;
4const assert = std.debug.assert;6const assert = std.debug.assert;
5const fs = std.fs;7const fs = std.fs;
6const mem = std.mem;8const mem = std.mem;
7const log = std.log.scoped(.link);9const log = std.log.scoped(.link);
8const trace = @import("tracy.zig").trace;
9const wasi_libc = @import("libs/wasi_libc.zig");
10
11const Allocator = std.mem.Allocator;10const Allocator = std.mem.Allocator;
12const Cache = std.Build.Cache;11const Cache = std.Build.Cache;
13const Path = std.Build.Cache.Path;12const Path = std.Build.Cache.Path;
14const Directory = std.Build.Cache.Directory;13const Directory = std.Build.Cache.Directory;
15const Compilation = @import("Compilation.zig");14const Compilation = @import("Compilation.zig");
16const LibCInstallation = std.zig.LibCInstallation;15const LibCInstallation = std.zig.LibCInstallation;
16
17const trace = @import("tracy.zig").trace;
18const wasi_libc = @import("libs/wasi_libc.zig");
19
17const Zcu = @import("Zcu.zig");20const Zcu = @import("Zcu.zig");
18const InternPool = @import("InternPool.zig");21const InternPool = @import("InternPool.zig");
19const Type = @import("Type.zig");22const Type = @import("Type.zig");
...@@ -572,6 +575,7 @@ pub const File = struct {...@@ -572,6 +575,7 @@ pub const File = struct {
572 dev.check(.make_writable);575 dev.check(.make_writable);
573 const comp = base.comp;576 const comp = base.comp;
574 const gpa = comp.gpa;577 const gpa = comp.gpa;
578 const io = comp.io;
575 switch (base.tag) {579 switch (base.tag) {
576 .lld => assert(base.file == null),580 .lld => assert(base.file == null),
577 .elf, .macho, .wasm, .goff, .xcoff => {581 .elf, .macho, .wasm, .goff, .xcoff => {
...@@ -616,22 +620,9 @@ pub const File = struct {...@@ -616,22 +620,9 @@ pub const File = struct {
616 &coff.mf620 &coff.mf
617 else621 else
618 unreachable;622 unreachable;
619 var attempt: u5 = 0;623 mf.file = .adaptFromNewApi(try Io.Dir.openFile(base.emit.root_dir.handle.adaptToNewApi(), io, base.emit.sub_path, .{
620 mf.file = while (true) break base.emit.root_dir.handle.openFile(base.emit.sub_path, .{
621 .mode = .read_write,624 .mode = .read_write,
622 }) catch |err| switch (err) {625 }));
623 error.AccessDenied => switch (builtin.os.tag) {
624 .windows => {
625 if (attempt == 13) return error.AccessDenied;
626 // give the kernel a chance to finish closing the executable handle
627 std.os.windows.kernel32.Sleep(@as(u32, 1) << attempt >> 1);
628 attempt += 1;
629 continue;
630 },
631 else => return error.AccessDenied,
632 },
633 else => |e| return e,
634 };
635 base.file = mf.file;626 base.file = mf.file;
636 try mf.ensureTotalCapacity(@intCast(mf.nodes.items[0].location().resolve(mf)[1]));627 try mf.ensureTotalCapacity(@intCast(mf.nodes.items[0].location().resolve(mf)[1]));
637 },628 },