windows: workaround kernel race condition

This was causing flaky CI failures.
This commit is contained in:
Jacob Young 2025-10-09 15:22:14 -04:00 committed by Andrew Kelley
parent c17e18647b
commit b2bc6073c8
3 changed files with 22 additions and 3 deletions

View File

@ -1912,6 +1912,7 @@ pub const CreateProcessError = error{
NameTooLong, NameTooLong,
InvalidExe, InvalidExe,
SystemResources, SystemResources,
FileBusy,
Unexpected, Unexpected,
}; };
@ -1982,6 +1983,7 @@ pub fn CreateProcessW(
.INVALID_PARAMETER => unreachable, .INVALID_PARAMETER => unreachable,
.INVALID_NAME => return error.InvalidName, .INVALID_NAME => return error.InvalidName,
.FILENAME_EXCED_RANGE => return error.NameTooLong, .FILENAME_EXCED_RANGE => return error.NameTooLong,
.SHARING_VIOLATION => return error.FileBusy,
// These are all the system errors that are mapped to ENOEXEC by // These are all the system errors that are mapped to ENOEXEC by
// the undocumented _dosmaperr (old CRT) or __acrt_errno_map_os_error // the undocumented _dosmaperr (old CRT) or __acrt_errno_map_os_error
// (newer CRT) functions. Their code can be found in crt/src/dosmap.c (old SDK) // (newer CRT) functions. Their code can be found in crt/src/dosmap.c (old SDK)

View File

@ -616,9 +616,19 @@ pub const File = struct {
&coff.mf &coff.mf
else else
unreachable; unreachable;
mf.file = try base.emit.root_dir.handle.openFile(base.emit.sub_path, .{ mf.file = for (0..2) |_| break base.emit.root_dir.handle.openFile(base.emit.sub_path, .{
.mode = .read_write, .mode = .read_write,
}); }) catch |err| switch (err) {
error.AccessDenied => switch (builtin.os.tag) {
.windows => {
// give the kernel a chance to finish closing the executable handle
std.os.windows.kernel32.Sleep(0);
continue;
},
else => return error.AccessDenied,
},
else => |e| return e,
} else return error.AccessDenied;
base.file = mf.file; base.file = mf.file;
try mf.ensureTotalCapacity(@intCast(mf.nodes.items[0].location().resolve(mf)[1])); try mf.ensureTotalCapacity(@intCast(mf.nodes.items[0].location().resolve(mf)[1]));
}, },

View File

@ -71,7 +71,14 @@ pub fn main() anyerror!void {
try testExec(allocator, "heLLo", "hello from exe\n"); try testExec(allocator, "heLLo", "hello from exe\n");
// now rename the exe to not have an extension // now rename the exe to not have an extension
try tmp.dir.rename("hello.exe", "hello"); for (0..2) |_| break tmp.dir.rename("hello.exe", "hello") catch |err| switch (err) {
error.AccessDenied => {
// give the kernel a chance to finish closing the executable handle
std.os.windows.kernel32.Sleep(0);
continue;
},
else => |e| return e,
} else return error.AccessDenied;
// with extension should now fail // with extension should now fail
try testExecError(error.FileNotFound, allocator, "hello.exe"); try testExecError(error.FileNotFound, allocator, "hello.exe");