authorgravatar for jacobly@ziglang.orgJacob Young <jacobly@ziglang.org> 2025-10-09 15:22:14-04:00
committergravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2025-10-10 22:47:36-07:00
logb2bc6073c8ada065906da9e3b5a4a2e7db04c21d
treeddaba6a7a70c69ae72bad8c6c5644596e427040c
parentc17e18647bf55bae38a1837a6afb19b0f2393892

windows: workaround kernel race condition

This was causing flaky CI failures.

3 files changed, 22 insertions(+), 3 deletions(-)

lib/std/os/windows.zig+2
......@@ -1912,6 +1912,7 @@ pub const CreateProcessError = error{
19121912 NameTooLong,
19131913 InvalidExe,
19141914 SystemResources,
1915 FileBusy,
19151916 Unexpected,
19161917};
19171918
......@@ -1982,6 +1983,7 @@ pub fn CreateProcessW(
19821983 .INVALID_PARAMETER => unreachable,
19831984 .INVALID_NAME => return error.InvalidName,
19841985 .FILENAME_EXCED_RANGE => return error.NameTooLong,
1986 .SHARING_VIOLATION => return error.FileBusy,
19851987 // These are all the system errors that are mapped to ENOEXEC by
19861988 // the undocumented _dosmaperr (old CRT) or __acrt_errno_map_os_error
19871989 // (newer CRT) functions. Their code can be found in crt/src/dosmap.c (old SDK)
src/link.zig+12-2
......@@ -616,9 +616,19 @@ pub const File = struct {
616616 &coff.mf
617617 else
618618 unreachable;
619 mf.file = try base.emit.root_dir.handle.openFile(base.emit.sub_path, .{
619 mf.file = for (0..2) |_| break base.emit.root_dir.handle.openFile(base.emit.sub_path, .{
620620 .mode = .read_write,
621 });
621 }) catch |err| switch (err) {
622 error.AccessDenied => switch (builtin.os.tag) {
623 .windows => {
624 // give the kernel a chance to finish closing the executable handle
625 std.os.windows.kernel32.Sleep(0);
626 continue;
627 },
628 else => return error.AccessDenied,
629 },
630 else => |e| return e,
631 } else return error.AccessDenied;
622632 base.file = mf.file;
623633 try mf.ensureTotalCapacity(@intCast(mf.nodes.items[0].location().resolve(mf)[1]));
624634 },
test/standalone/windows_spawn/main.zig+8-1
......@@ -71,7 +71,14 @@ pub fn main() anyerror!void {
7171 try testExec(allocator, "heLLo", "hello from exe\n");
7272
7373 // now rename the exe to not have an extension
74 try tmp.dir.rename("hello.exe", "hello");
74 for (0..2) |_| break tmp.dir.rename("hello.exe", "hello") catch |err| switch (err) {
75 error.AccessDenied => {
76 // give the kernel a chance to finish closing the executable handle
77 std.os.windows.kernel32.Sleep(0);
78 continue;
79 },
80 else => |e| return e,
81 } else return error.AccessDenied;
7582
7683 // with extension should now fail
7784 try testExecError(error.FileNotFound, allocator, "hello.exe");