link: move the windows kernel bug workaround to Io implementation

This commit is contained in:
Andrew Kelley 2025-10-27 13:21:49 -07:00
parent 02119362d7
commit df4c30ca16
2 changed files with 31 additions and 25 deletions

View file

@ -2081,6 +2081,10 @@ fn dirOpenFileWindowsInner(
const create_file_flags: w.ULONG = file_or_dir_flag |
if (flags.follow_symlinks) blocking_flag else w.FILE_OPEN_REPARSE_POINT;
// There are multiple kernel bugs being worked around with retries.
const max_attempts = 13;
var attempt: u5 = 0;
const handle = while (true) {
try t.checkCancel();
@ -2110,7 +2114,17 @@ fn dirOpenFileWindowsInner(
.NO_MEDIA_IN_DEVICE => return error.NoDevice,
.INVALID_PARAMETER => |err| return w.statusBug(err),
.SHARING_VIOLATION => return error.AccessDenied,
.ACCESS_DENIED => return error.AccessDenied,
.ACCESS_DENIED => {
// This occurs if the file attempting to be opened is a running
// executable. However, there's a kernel bug: the error may be
// incorrectly returned for an indeterminate amount of time
// after an executable file is closed. Here we work around the
// kernel bug with retry attempts.
if (attempt - max_attempts == 0) return error.AccessDenied;
_ = w.kernel32.SleepEx((@as(u32, 1) << attempt) >> 1, w.TRUE);
attempt += 1;
continue;
},
.PIPE_BUSY => return error.PipeBusy,
.PIPE_NOT_AVAILABLE => return error.NoDevice,
.OBJECT_PATH_SYNTAX_BAD => |err| return w.statusBug(err),
@ -2123,10 +2137,11 @@ fn dirOpenFileWindowsInner(
// This error means that there *was* a file in this location on
// the file system, but it was deleted. However, the OS is not
// finished with the deletion operation, and so this CreateFile
// call has failed. There is not really a sane way to handle
// this other than retrying the creation after the OS finishes
// the deletion.
_ = w.kernel32.SleepEx(1, w.FALSE);
// call has failed. Here, we simulate the kernel bug being
// fixed by sleeping and retrying until the error goes away.
if (attempt - max_attempts == 0) return error.AccessDenied;
_ = w.kernel32.SleepEx((@as(u32, 1) << attempt) >> 1, w.TRUE);
attempt += 1;
continue;
},
.VIRUS_INFECTED, .VIRUS_DELETED => return error.AntivirusInterference,

View file

@ -1,19 +1,22 @@
const std = @import("std");
const build_options = @import("build_options");
const builtin = @import("builtin");
const build_options = @import("build_options");
const std = @import("std");
const Io = std.Io;
const assert = std.debug.assert;
const fs = std.fs;
const mem = std.mem;
const log = std.log.scoped(.link);
const trace = @import("tracy.zig").trace;
const wasi_libc = @import("libs/wasi_libc.zig");
const Allocator = std.mem.Allocator;
const Cache = std.Build.Cache;
const Path = std.Build.Cache.Path;
const Directory = std.Build.Cache.Directory;
const Compilation = @import("Compilation.zig");
const LibCInstallation = std.zig.LibCInstallation;
const trace = @import("tracy.zig").trace;
const wasi_libc = @import("libs/wasi_libc.zig");
const Zcu = @import("Zcu.zig");
const InternPool = @import("InternPool.zig");
const Type = @import("Type.zig");
@ -572,6 +575,7 @@ pub const File = struct {
dev.check(.make_writable);
const comp = base.comp;
const gpa = comp.gpa;
const io = comp.io;
switch (base.tag) {
.lld => assert(base.file == null),
.elf, .macho, .wasm, .goff, .xcoff => {
@ -616,22 +620,9 @@ pub const File = struct {
&coff.mf
else
unreachable;
var attempt: u5 = 0;
mf.file = while (true) break base.emit.root_dir.handle.openFile(base.emit.sub_path, .{
mf.file = .adaptFromNewApi(try Io.Dir.openFile(base.emit.root_dir.handle.adaptToNewApi(), io, base.emit.sub_path, .{
.mode = .read_write,
}) catch |err| switch (err) {
error.AccessDenied => switch (builtin.os.tag) {
.windows => {
if (attempt == 13) return error.AccessDenied;
// give the kernel a chance to finish closing the executable handle
std.os.windows.kernel32.Sleep(@as(u32, 1) << attempt >> 1);
attempt += 1;
continue;
},
else => return error.AccessDenied,
},
else => |e| return e,
};
}));
base.file = mf.file;
try mf.ensureTotalCapacity(@intCast(mf.nodes.items[0].location().resolve(mf)[1]));
},