authorgravatar for jacobly@ziglang.orgJacob Young <jacobly@ziglang.org> 2025-10-14 23:16:26-04:00
committergravatar for jacobly@ziglang.orgJacob Young <jacobly@ziglang.org> 2025-10-14 23:16:36-04:00
logcc3c4d1069c7b60a5588cb348f9fd927772b069b
tree0e82f7875722d8ad7f238522c2bbe8a432bfa544
parent595cd8935a8a1660518eb35ba4edd92a90e48f71

windows: workaround kernel race condition in more places


1 files changed, 20 insertions(+), 18 deletions(-)

test/standalone/windows_spawn/main.zig+20-18
......@@ -71,19 +71,7 @@ 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 {
75 var attempt: u5 = 0;
76 while (true) break tmp.dir.rename("hello.exe", "hello") catch |err| switch (err) {
77 error.AccessDenied => {
78 if (attempt == 13) return error.AccessDenied;
79 // give the kernel a chance to finish closing the executable handle
80 std.os.windows.kernel32.Sleep(@as(u32, 1) << attempt >> 1);
81 attempt += 1;
82 continue;
83 },
84 else => |e| return e,
85 };
86 }
74 try renameExe(tmp.dir, "hello.exe", "hello");
8775
8876 // with extension should now fail
8977 try testExecError(error.FileNotFound, allocator, "hello.exe");
......@@ -91,7 +79,7 @@ pub fn main() anyerror!void {
9179 try testExec(allocator, "heLLo", "hello from exe\n");
9280
9381 try tmp.dir.makeDir("something");
94 try tmp.dir.rename("hello", "something/hello.exe");
82 try renameExe(tmp.dir, "hello", "something/hello.exe");
9583
9684 const relative_path_no_ext = try std.fs.path.join(allocator, &.{ tmp_relative_path, "something/hello" });
9785 defer allocator.free(relative_path_no_ext);
......@@ -118,14 +106,14 @@ pub fn main() anyerror!void {
118106 try testExecError(error.InvalidExe, allocator, "hello");
119107
120108 // If we now rename hello.exe to have no extension, it will behave differently
121 try tmp.dir.rename("hello.exe", "hello");
109 try renameExe(tmp.dir, "hello.exe", "hello");
122110
123111 // Now, trying to execute it without an extension should treat InvalidExe as recoverable
124112 // and skip over it and find hello.bat and execute that
125113 try testExec(allocator, "hello", "hello from bat\r\n");
126114
127115 // If we rename the invalid exe to something else
128 try tmp.dir.rename("hello", "goodbye");
116 try renameExe(tmp.dir, "hello", "goodbye");
129117 // Then we should now get FileNotFound when trying to execute 'goodbye',
130118 // since that is what the original error will be after searching for 'goodbye'
131119 // in the cwd. It will try to execute 'goodbye' from the PATH but the InvalidExe error
......@@ -151,7 +139,7 @@ pub fn main() anyerror!void {
151139 try testExec(allocator, "hello", "hello from bat\r\n");
152140
153141 // If we rename something/hello.exe to something/goodbye.exe
154 try tmp.dir.rename("something/hello.exe", "something/goodbye.exe");
142 try renameExe(tmp.dir, "something/hello.exe", "something/goodbye.exe");
155143 // And try to execute goodbye, then the one in something should be found
156144 // since the one in cwd is an invalid executable
157145 try testExec(allocator, "goodbye", "hello from exe\n");
......@@ -196,7 +184,7 @@ pub fn main() anyerror!void {
196184 var subdir_cwd = try tmp.dir.openDir(denormed_something_subdir_wtf8, .{});
197185 defer subdir_cwd.close();
198186
199 try tmp.dir.rename("something/goodbye.exe", "hello.exe");
187 try renameExe(tmp.dir, "something/goodbye.exe", "hello.exe");
200188 try subdir_cwd.setAsCwd();
201189
202190 // clear the PATH again
......@@ -229,3 +217,17 @@ fn testExecWithCwd(allocator: std.mem.Allocator, command: []const u8, cwd: ?[]co
229217 try std.testing.expectEqualStrings("", result.stderr);
230218 try std.testing.expectEqualStrings(expected_stdout, result.stdout);
231219}
220
221fn renameExe(dir: std.fs.Dir, old_sub_path: []const u8, new_sub_path: []const u8) !void {
222 var attempt: u5 = 0;
223 while (true) break dir.rename(old_sub_path, new_sub_path) catch |err| switch (err) {
224 error.AccessDenied => {
225 if (attempt == 13) return error.AccessDenied;
226 // give the kernel a chance to finish closing the executable handle
227 std.os.windows.kernel32.Sleep(@as(u32, 1) << attempt >> 1);
228 attempt += 1;
229 continue;
230 },
231 else => |e| return e,
232 };
233}