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(...@@ -7685,6 +7685,8 @@ fn dirRenameWindowsInner(
7685 defer w.CloseHandle(src_fd);7685 defer w.CloseHandle(src_fd);
76867686
7687 var rc: w.NTSTATUS = undefined;7687 var rc: w.NTSTATUS = undefined;
7688 var attempt: u5 = 0;
7689
7688 // FileRenameInformationEx has varying levels of support:7690 // FileRenameInformationEx has varying levels of support:
7689 // - FILE_RENAME_INFORMATION_EX requires >= win10_rs17691 // - FILE_RENAME_INFORMATION_EX requires >= win10_rs1
7690 // (INVALID_INFO_CLASS is returned if not supported)7692 // (INVALID_INFO_CLASS is returned if not supported)
...@@ -7708,24 +7710,43 @@ fn dirRenameWindowsInner(...@@ -7708,24 +7710,43 @@ fn dirRenameWindowsInner(
7708 });7710 });
7709 var io_status_block: w.IO_STATUS_BLOCK = undefined;7711 var io_status_block: w.IO_STATUS_BLOCK = undefined;
7710 const rename_info_buf = rename_info.toBuffer();7712 const rename_info_buf = rename_info.toBuffer();
7711 rc = w.ntdll.NtSetInformationFile(7713 var syscall: Syscall = try .start();
7712 src_fd,7714 while (true) {
7713 &io_status_block,7715 rc = w.ntdll.NtSetInformationFile(
7714 rename_info_buf.ptr,7716 src_fd,
7715 @intCast(rename_info_buf.len),7717 &io_status_block,
7716 .RenameEx,7718 rename_info_buf.ptr,
7717 );7719 @intCast(rename_info_buf.len),
7718 switch (rc) {7720 .RenameEx,
7719 .SUCCESS => return,7721 );
7720 // The filesystem does not support FileDispositionInformationEx7722 switch (rc) {
7721 .INVALID_PARAMETER,7723 // For all other statuses, fall down to the switch below to handle them.
7722 // The operating system does not support FileDispositionInformationEx7724 else => {
7723 .INVALID_INFO_CLASS,7725 syscall.finish();
7724 // The operating system does not support one of the flags7726 break :need_fallback false;
7725 .NOT_SUPPORTED,7727 },
7726 => break :need_fallback true,7728 .CANCELLED => try syscall.checkCancel(),
7727 // For all other statuses, fall down to the switch below to handle them.7729 // The filesystem does not support FileDispositionInformationEx
7728 else => break :need_fallback false,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 }
7729 }7750 }
7730 };7751 };
77317752
...@@ -7737,13 +7758,33 @@ fn dirRenameWindowsInner(...@@ -7737,13 +7758,33 @@ fn dirRenameWindowsInner(
7737 });7758 });
7738 var io_status_block: w.IO_STATUS_BLOCK = undefined;7759 var io_status_block: w.IO_STATUS_BLOCK = undefined;
7739 const rename_info_buf = rename_info.toBuffer();7760 const rename_info_buf = rename_info.toBuffer();
7740 rc = w.ntdll.NtSetInformationFile(7761 var syscall: Syscall = try .start();
7741 src_fd,7762 while (true) {
7742 &io_status_block,7763 rc = w.ntdll.NtSetInformationFile(
7743 rename_info_buf.ptr,7764 src_fd,
7744 @intCast(rename_info_buf.len),7765 &io_status_block,
7745 .Rename,7766 rename_info_buf.ptr,
7746 );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 }
7747 }7788 }
77487789
7749 switch (rc) {7790 switch (rc) {