From 10dd4ec9eab6a8fc682f5281ac9fe819013ba2fe Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Sun, 22 Mar 2026 04:32:15 +0000 Subject: [PATCH 1/4] Initial plan From 45ac68142d2381483b8cb2e922772b0abed98fb0 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Sun, 22 Mar 2026 04:45:12 +0000 Subject: [PATCH 2/4] Fix MountVolume test flakiness by replacing fixed delays with polling loops - Delete_MountVolume.cs: Replace 7 fixed Task.Delay(300).Wait() calls with WaitForDirectoryGone() polling helper (10s timeout, 100ms intervals using Environment.TickCount64) before Directory.Exists assertions. Also replace Task.Delay in DeleteDir retry loop with Thread.Sleep. - ReparsePoints_MountVolume.cs: Add retry logic to DeleteDir with IOException handling and Thread.Sleep backoff to handle mount teardown timing. Fixes dotnet/runtime#125295, dotnet/runtime#125624 Co-authored-by: danmoseley <6385855+danmoseley@users.noreply.github.com> Agent-Logs-Url: https://github.com/dotnet/runtime/sessions/90f5ab9b-bd66-4286-9b2c-72778993ada3 --- .../Directory/Delete_MountVolume.cs | 26 ++++++++++++------- .../Directory/ReparsePoints_MountVolume.cs | 19 ++++++++++++-- 2 files changed, 34 insertions(+), 11 deletions(-) diff --git a/src/libraries/System.Runtime/tests/System.IO.FileSystem.Tests/Directory/Delete_MountVolume.cs b/src/libraries/System.Runtime/tests/System.IO.FileSystem.Tests/Directory/Delete_MountVolume.cs index ed74831de984f6..0e17c0c7fdb55d 100644 --- a/src/libraries/System.Runtime/tests/System.IO.FileSystem.Tests/Directory/Delete_MountVolume.cs +++ b/src/libraries/System.Runtime/tests/System.IO.FileSystem.Tests/Directory/Delete_MountVolume.cs @@ -13,7 +13,6 @@ This testcase attempts to delete some directories in a mounted volume using System.Text; using System.Reflection; using System.Threading; -using System.Threading.Tasks; using Microsoft.DotNet.XUnitExtensions; using Xunit; @@ -73,7 +72,7 @@ public static void RunTest() dirNameWithoutRoot = dirName.Substring(3); dirNameReferredFromMountedDrive = Path.Combine(mountedDirName, dirNameWithoutRoot); Directory.Delete(dirNameReferredFromMountedDrive, true); - Task.Delay(300).Wait(); + WaitForDirectoryGone(dirName); Eval(!Directory.Exists(dirName), "Err_20387g! Directory {0} still exist: {1}", dirName, Directory.Exists(dirName)); } } @@ -121,7 +120,7 @@ public static void RunTest() dirNameWithoutRoot = dirName.Substring(3); dirNameReferredFromMountedDrive = Path.Combine(mountedDirName, dirNameWithoutRoot); Directory.Delete(dirNameReferredFromMountedDrive, true); - Task.Delay(300).Wait(); + WaitForDirectoryGone(dirName); Eval(!Directory.Exists(dirName), "Err_794aiu! Directory {0} still exist: {1}", dirName, Directory.Exists(dirName)); } } @@ -166,7 +165,7 @@ public static void RunTest() dirNameWithoutRoot = dirName.Substring(3); dirNameReferredFromMountedDrive = Path.Combine(mountedDirName, dirNameWithoutRoot); Directory.Delete(dirNameReferredFromMountedDrive, true); - Task.Delay(300).Wait(); + WaitForDirectoryGone(dirName); Eval(!Directory.Exists(dirName), "Err_195whv! Directory {0} still exist: {1}", dirName, Directory.Exists(dirName)); } } @@ -211,7 +210,7 @@ public static void RunTest() dirNameWithoutRoot = dirName.Substring(3); dirNameReferredFromMountedDrive = Path.Combine(mountedDirName, dirNameWithoutRoot); Directory.Delete(dirNameReferredFromMountedDrive, true); - Task.Delay(300).Wait(); + WaitForDirectoryGone(dirName); Eval(!Directory.Exists(dirName), "Err_493yin! Directory {0} still exist: {1}", dirName, Directory.Exists(dirName)); } } @@ -250,7 +249,7 @@ public static void RunTest() MountHelper.Mount(Directory.GetCurrentDirectory().Substring(0, 2), mountedDirName); Directory.Delete(mountedDirName, true); - Task.Delay(300).Wait(); + WaitForDirectoryGone(mountedDirName); } finally { @@ -298,7 +297,7 @@ public static void RunTest() MountHelper.Mount(Directory.GetCurrentDirectory().Substring(0, 2), mountedDirName); //now lets call delete on the parent directory Directory.Delete(dirName, true); - Task.Delay(300).Wait(); + WaitForDirectoryGone(dirName); Eval(!Directory.Exists(dirName), "Err_006jsf! Directory {0} still exist: {1}", dirName, Directory.Exists(dirName)); Console.WriteLine("Completed Scenario 3.4"); } @@ -354,7 +353,7 @@ public static void RunTest() MountHelper.Mount(Directory.GetCurrentDirectory().Substring(0, 2), mountedDirName); //now lets call delete on the parent directory Directory.Delete(dirName, true); - Task.Delay(300).Wait(); + WaitForDirectoryGone(dirName); Eval(!Directory.Exists(dirName), "Err_900edl! Directory {0} still exist: {1}", dirName, Directory.Exists(dirName)); Console.WriteLine("Completed Scenario 3.5: {0}", mountedDirName); } @@ -412,12 +411,21 @@ private static void DeleteDir(string debugFileName, bool sub) if (--maxAttempts == 0) throw; else - Task.Delay(300).Wait(); + Thread.Sleep(300); } } } } + private static void WaitForDirectoryGone(string path) + { + const int TimeoutMs = 10_000; + const int PollIntervalMs = 100; + long start = Environment.TickCount64; + while (Directory.Exists(path) && Environment.TickCount64 - start < TimeoutMs) + Thread.Sleep(PollIntervalMs); + } + //Checks for error private static bool Eval(bool expression, string msg, params object[] values) { diff --git a/src/libraries/System.Runtime/tests/System.IO.FileSystem.Tests/Directory/ReparsePoints_MountVolume.cs b/src/libraries/System.Runtime/tests/System.IO.FileSystem.Tests/Directory/ReparsePoints_MountVolume.cs index d6d02b7ab7b1ff..093a76cf429b53 100644 --- a/src/libraries/System.Runtime/tests/System.IO.FileSystem.Tests/Directory/ReparsePoints_MountVolume.cs +++ b/src/libraries/System.Runtime/tests/System.IO.FileSystem.Tests/Directory/ReparsePoints_MountVolume.cs @@ -10,6 +10,7 @@ This testcase attempts to checks GetDirectories/GetFiles with the following Repa using System.Text; using System.Collections.Generic; using System.Diagnostics; +using System.Threading; using Microsoft.DotNet.XUnitExtensions; using Xunit; @@ -370,8 +371,22 @@ private static void DeleteFile(string fileName) private static void DeleteDir(string fileName, bool sub) { - if (Directory.Exists(fileName)) - Directory.Delete(fileName, sub); + const int MaxAttempts = 10; + const int PollIntervalMs = 200; + int attempts = MaxAttempts; + while (Directory.Exists(fileName)) + { + try + { + Directory.Delete(fileName, sub); + return; + } + catch (IOException) when (attempts > 1) + { + attempts--; + Thread.Sleep(PollIntervalMs); + } + } } //Checks for error From 7348e6411cd2a58e86c37cbb95d8904fdabaf96f Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Sun, 22 Mar 2026 05:33:58 +0000 Subject: [PATCH 3/4] Add comment explaining IOException in DeleteDir retry loop Co-authored-by: danmoseley <6385855+danmoseley@users.noreply.github.com> Agent-Logs-Url: https://github.com/dotnet/runtime/sessions/1cc6fac5-ff28-4293-b1dc-b35cdb75dd67 --- .../Directory/ReparsePoints_MountVolume.cs | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/libraries/System.Runtime/tests/System.IO.FileSystem.Tests/Directory/ReparsePoints_MountVolume.cs b/src/libraries/System.Runtime/tests/System.IO.FileSystem.Tests/Directory/ReparsePoints_MountVolume.cs index 093a76cf429b53..dbacb4199b70d0 100644 --- a/src/libraries/System.Runtime/tests/System.IO.FileSystem.Tests/Directory/ReparsePoints_MountVolume.cs +++ b/src/libraries/System.Runtime/tests/System.IO.FileSystem.Tests/Directory/ReparsePoints_MountVolume.cs @@ -383,6 +383,10 @@ private static void DeleteDir(string fileName, bool sub) } catch (IOException) when (attempts > 1) { + // IOException can occur transiently here when the volume is still being + // unmounted (e.g. the reparse point directory is momentarily locked by + // the kernel while the mount is being torn down). Retry with a short + // delay to let the unmount complete. attempts--; Thread.Sleep(PollIntervalMs); } From 932c175081ace7862678c93db71c501239b8cffd Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Mon, 23 Mar 2026 03:28:54 +0000 Subject: [PATCH 4/4] Use Stopwatch instead of TickCount64 and extend timeout to 60s in WaitForDirectoryGone Co-authored-by: danmoseley <6385855+danmoseley@users.noreply.github.com> Agent-Logs-Url: https://github.com/dotnet/runtime/sessions/b0df647a-d5ae-4269-8916-d11d32a84cd5 --- .../Directory/Delete_MountVolume.cs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/libraries/System.Runtime/tests/System.IO.FileSystem.Tests/Directory/Delete_MountVolume.cs b/src/libraries/System.Runtime/tests/System.IO.FileSystem.Tests/Directory/Delete_MountVolume.cs index 0e17c0c7fdb55d..50cf2f7796b25d 100644 --- a/src/libraries/System.Runtime/tests/System.IO.FileSystem.Tests/Directory/Delete_MountVolume.cs +++ b/src/libraries/System.Runtime/tests/System.IO.FileSystem.Tests/Directory/Delete_MountVolume.cs @@ -9,6 +9,7 @@ This testcase attempts to delete some directories in a mounted volume - refer to the directory in a recursive manner in addition to the normal one **/ using System; +using System.Diagnostics; using System.IO; using System.Text; using System.Reflection; @@ -419,10 +420,9 @@ private static void DeleteDir(string debugFileName, bool sub) private static void WaitForDirectoryGone(string path) { - const int TimeoutMs = 10_000; const int PollIntervalMs = 100; - long start = Environment.TickCount64; - while (Directory.Exists(path) && Environment.TickCount64 - start < TimeoutMs) + Stopwatch sw = Stopwatch.StartNew(); + while (Directory.Exists(path) && sw.Elapsed < TimeSpan.FromSeconds(60)) Thread.Sleep(PollIntervalMs); }