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{...@@ -1912,6 +1912,7 @@ pub const CreateProcessError = error{
1912 NameTooLong,1912 NameTooLong,
1913 InvalidExe,1913 InvalidExe,
1914 SystemResources,1914 SystemResources,
1915 FileBusy,
1915 Unexpected,1916 Unexpected,
1916};1917};
19171918
...@@ -1982,6 +1983,7 @@ pub fn CreateProcessW(...@@ -1982,6 +1983,7 @@ pub fn CreateProcessW(
1982 .INVALID_PARAMETER => unreachable,1983 .INVALID_PARAMETER => unreachable,
1983 .INVALID_NAME => return error.InvalidName,1984 .INVALID_NAME => return error.InvalidName,
1984 .FILENAME_EXCED_RANGE => return error.NameTooLong,1985 .FILENAME_EXCED_RANGE => return error.NameTooLong,
1986 .SHARING_VIOLATION => return error.FileBusy,
1985 // These are all the system errors that are mapped to ENOEXEC by1987 // These are all the system errors that are mapped to ENOEXEC by
1986 // the undocumented _dosmaperr (old CRT) or __acrt_errno_map_os_error1988 // the undocumented _dosmaperr (old CRT) or __acrt_errno_map_os_error
1987 // (newer CRT) functions. Their code can be found in crt/src/dosmap.c (old SDK)1989 // (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 {...@@ -616,9 +616,19 @@ pub const File = struct {
616 &coff.mf616 &coff.mf
617 else617 else
618 unreachable;618 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, .{
620 .mode = .read_write,620 .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;
622 base.file = mf.file;632 base.file = mf.file;
623 try mf.ensureTotalCapacity(@intCast(mf.nodes.items[0].location().resolve(mf)[1]));633 try mf.ensureTotalCapacity(@intCast(mf.nodes.items[0].location().resolve(mf)[1]));
624 },634 },
test/standalone/windows_spawn/main.zig+8-1
...@@ -71,7 +71,14 @@ pub fn main() anyerror!void {...@@ -71,7 +71,14 @@ pub fn main() anyerror!void {
71 try testExec(allocator, "heLLo", "hello from exe\n");71 try testExec(allocator, "heLLo", "hello from exe\n");
7272
73 // now rename the exe to not have an extension73 // 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
76 // with extension should now fail83 // with extension should now fail
77 try testExecError(error.FileNotFound, allocator, "hello.exe");84 try testExecError(error.FileNotFound, allocator, "hello.exe");