authorgravatar for jacobly@ziglang.orgJacob Young <jacobly@ziglang.org> 2026-09-25 23:36:41-04:00
committergravatar for jacobly@ziglang.orgJacob Young <jacobly@ziglang.org> 2026-09-26 19:08:50-04:00
log44f06cb6005d3ca5e0b94fbffd5a7668e6e98bd7
treed963bdc7bde68055cdbf9b5d64de07a72470f434
parent47291ae1ac71e595cb1571b1245c58e943aaffd7

Threaded: add retries to directory renaming

I think there's a race between a child's exit status being reported and all of its handles being closed, so possibly affects most filesystem operations.

1 files changed, 66 insertions(+), 25 deletions(-)

lib/std/Io/Threaded.zig+66-25
......@@ -7685,6 +7685,8 @@ fn dirRenameWindowsInner(
76857685 defer w.CloseHandle(src_fd);
76867686
76877687 var rc: w.NTSTATUS = undefined;
7688 var attempt: u5 = 0;
7689
76887690 // FileRenameInformationEx has varying levels of support:
76897691 // - FILE_RENAME_INFORMATION_EX requires >= win10_rs1
76907692 // (INVALID_INFO_CLASS is returned if not supported)
......@@ -7708,24 +7710,43 @@ fn dirRenameWindowsInner(
77087710 });
77097711 var io_status_block: w.IO_STATUS_BLOCK = undefined;
77107712 const rename_info_buf = rename_info.toBuffer();
7711 rc = w.ntdll.NtSetInformationFile(
7712 src_fd,
7713 &io_status_block,
7714 rename_info_buf.ptr,
7715 @intCast(rename_info_buf.len),
7716 .RenameEx,
7717 );
7718 switch (rc) {
7719 .SUCCESS => return,
7720 // The filesystem does not support FileDispositionInformationEx
7721 .INVALID_PARAMETER,
7722 // The operating system does not support FileDispositionInformationEx
7723 .INVALID_INFO_CLASS,
7724 // The operating system does not support one of the flags
7725 .NOT_SUPPORTED,
7726 => break :need_fallback true,
7727 // For all other statuses, fall down to the switch below to handle them.
7728 else => break :need_fallback false,
7713 var syscall: Syscall = try .start();
7714 while (true) {
7715 rc = w.ntdll.NtSetInformationFile(
7716 src_fd,
7717 &io_status_block,
7718 rename_info_buf.ptr,
7719 @intCast(rename_info_buf.len),
7720 .RenameEx,
7721 );
7722 switch (rc) {
7723 // For all other statuses, fall down to the switch below to handle them.
7724 else => {
7725 syscall.finish();
7726 break :need_fallback false;
7727 },
7728 .CANCELLED => try syscall.checkCancel(),
7729 // The filesystem does not support FileDispositionInformationEx
7730 .INVALID_PARAMETER,
7731 // The operating system does not support FileDispositionInformationEx
7732 .INVALID_INFO_CLASS,
7733 // The operating system does not support one of the flags
7734 .NOT_SUPPORTED,
7735 => {
7736 syscall.finish();
7737 break :need_fallback true;
7738 },
7739 .ACCESS_DENIED => {
7740 syscall.finish();
7741 if (max_windows_kernel_bug_retries - attempt == 0) break :need_fallback false;
7742 try parking_sleep.sleep(.{ .duration = .{
7743 .raw = .fromMilliseconds((@as(u32, 1) << attempt) >> 1),
7744 .clock = .awake,
7745 } });
7746 attempt += 1;
7747 syscall = try .start();
7748 },
7749 }
77297750 }
77307751 };
77317752
......@@ -7737,13 +7758,33 @@ fn dirRenameWindowsInner(
77377758 });
77387759 var io_status_block: w.IO_STATUS_BLOCK = undefined;
77397760 const rename_info_buf = rename_info.toBuffer();
7740 rc = w.ntdll.NtSetInformationFile(
7741 src_fd,
7742 &io_status_block,
7743 rename_info_buf.ptr,
7744 @intCast(rename_info_buf.len),
7745 .Rename,
7746 );
7761 var syscall: Syscall = try .start();
7762 while (true) {
7763 rc = w.ntdll.NtSetInformationFile(
7764 src_fd,
7765 &io_status_block,
7766 rename_info_buf.ptr,
7767 @intCast(rename_info_buf.len),
7768 .Rename,
7769 );
7770 switch (rc) {
7771 else => {
7772 syscall.finish();
7773 break;
7774 },
7775 .CANCELLED => try syscall.checkCancel(),
7776 .ACCESS_DENIED => {
7777 syscall.finish();
7778 if (max_windows_kernel_bug_retries - attempt == 0) break;
7779 try parking_sleep.sleep(.{ .duration = .{
7780 .raw = .fromMilliseconds((@as(u32, 1) << attempt) >> 1),
7781 .clock = .awake,
7782 } });
7783 attempt += 1;
7784 syscall = try .start();
7785 },
7786 }
7787 }
77477788 }
77487789
77497790 switch (rc) {