From ca693a7a68f3a73814e894f4c79f4697570f5f68 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Fri, 1 Aug 2025 01:28:50 +0000 Subject: [PATCH 1/9] Initial plan From fdc6c660db70c9603ef88b9720117642f39fb742 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Fri, 1 Aug 2025 01:58:58 +0000 Subject: [PATCH 2/9] Implement alertable waits for WaitHandle to fix APC handling Co-authored-by: jkotas <6668460+jkotas@users.noreply.github.com> --- .../Windows/Kernel32/Interop.Threading.cs | 1 + .../System/Threading/WaitHandle.Windows.cs | 65 +++++++++++++++++-- .../threading/regressions/115178/115178.cs | 1 - 3 files changed, 62 insertions(+), 5 deletions(-) diff --git a/src/libraries/Common/src/Interop/Windows/Kernel32/Interop.Threading.cs b/src/libraries/Common/src/Interop/Windows/Kernel32/Interop.Threading.cs index 14863bf1e4f749..3fddaf366a4182 100644 --- a/src/libraries/Common/src/Interop/Windows/Kernel32/Interop.Threading.cs +++ b/src/libraries/Common/src/Interop/Windows/Kernel32/Interop.Threading.cs @@ -10,6 +10,7 @@ internal static partial class Interop internal static partial class Kernel32 { internal const int WAIT_FAILED = unchecked((int)0xFFFFFFFF); + internal const int WAIT_IO_COMPLETION = 0x000000C0; [LibraryImport(Libraries.Kernel32)] internal static partial uint WaitForMultipleObjectsEx(uint nCount, IntPtr lpHandles, BOOL bWaitAll, uint dwMilliseconds, BOOL bAlertable); diff --git a/src/libraries/System.Private.CoreLib/src/System/Threading/WaitHandle.Windows.cs b/src/libraries/System.Private.CoreLib/src/System/Threading/WaitHandle.Windows.cs index d4d01971437637..e40092ae0145c5 100644 --- a/src/libraries/System.Private.CoreLib/src/System/Threading/WaitHandle.Windows.cs +++ b/src/libraries/System.Private.CoreLib/src/System/Threading/WaitHandle.Windows.cs @@ -49,20 +49,49 @@ private static unsafe int WaitForMultipleObjectsIgnoringSyncContext(IntPtr* pHan Thread currentThread = Thread.CurrentThread; currentThread.SetWaitSleepJoinState(); + ulong startTime = 0; + if (millisecondsTimeout != -1) + { + startTime = Interop.Kernel32.GetTickCount64(); + } + + retry: #if NATIVEAOT int result; if (reentrantWait) { Debug.Assert(!waitAll); - result = RuntimeImports.RhCompatibleReentrantWaitAny(false, millisecondsTimeout, numHandles, pHandles); + result = RuntimeImports.RhCompatibleReentrantWaitAny(true, millisecondsTimeout, numHandles, pHandles); } else { - result = (int)Interop.Kernel32.WaitForMultipleObjectsEx((uint)numHandles, (IntPtr)pHandles, waitAll ? Interop.BOOL.TRUE : Interop.BOOL.FALSE, (uint)millisecondsTimeout, Interop.BOOL.FALSE); + result = (int)Interop.Kernel32.WaitForMultipleObjectsEx((uint)numHandles, (IntPtr)pHandles, waitAll ? Interop.BOOL.TRUE : Interop.BOOL.FALSE, (uint)millisecondsTimeout, Interop.BOOL.TRUE); } #else - int result = (int)Interop.Kernel32.WaitForMultipleObjectsEx((uint)numHandles, (IntPtr)pHandles, waitAll ? Interop.BOOL.TRUE : Interop.BOOL.FALSE, (uint)millisecondsTimeout, Interop.BOOL.FALSE); + int result = (int)Interop.Kernel32.WaitForMultipleObjectsEx((uint)numHandles, (IntPtr)pHandles, waitAll ? Interop.BOOL.TRUE : Interop.BOOL.FALSE, (uint)millisecondsTimeout, Interop.BOOL.TRUE); #endif + + if (result == Interop.Kernel32.WAIT_IO_COMPLETION) + { + // Handle APC completion by adjusting timeout and retrying + if (millisecondsTimeout != -1) + { + ulong currentTime = Interop.Kernel32.GetTickCount64(); + ulong elapsed = currentTime - startTime; + + if (elapsed >= (ulong)millisecondsTimeout) + { + result = Interop.Kernel32.WAIT_TIMEOUT; + goto WaitCompleted; + } + + millisecondsTimeout -= (int)elapsed; + startTime = currentTime; + } + goto retry; + } + + WaitCompleted: currentThread.ClearWaitSleepJoinState(); if (result == Interop.Kernel32.WAIT_FAILED) @@ -102,8 +131,36 @@ private static int SignalAndWaitCore(IntPtr handleToSignal, IntPtr handleToWaitO { Debug.Assert(millisecondsTimeout >= -1); - int ret = (int)Interop.Kernel32.SignalObjectAndWait(handleToSignal, handleToWaitOn, (uint)millisecondsTimeout, Interop.BOOL.FALSE); + ulong startTime = 0; + if (millisecondsTimeout != -1) + { + startTime = Interop.Kernel32.GetTickCount64(); + } + + retry: + int ret = (int)Interop.Kernel32.SignalObjectAndWait(handleToSignal, handleToWaitOn, (uint)millisecondsTimeout, Interop.BOOL.TRUE); + + if (ret == Interop.Kernel32.WAIT_IO_COMPLETION) + { + // Handle APC completion by adjusting timeout and retrying + if (millisecondsTimeout != -1) + { + ulong currentTime = Interop.Kernel32.GetTickCount64(); + ulong elapsed = currentTime - startTime; + + if (elapsed >= (ulong)millisecondsTimeout) + { + ret = Interop.Kernel32.WAIT_TIMEOUT; + goto WaitCompleted; + } + + millisecondsTimeout -= (int)elapsed; + startTime = currentTime; + } + goto retry; + } + WaitCompleted: if (ret == Interop.Kernel32.WAIT_FAILED) { ThrowWaitFailedException(Interop.Kernel32.GetLastError()); diff --git a/src/tests/baseservices/threading/regressions/115178/115178.cs b/src/tests/baseservices/threading/regressions/115178/115178.cs index 5a9fdd46265c4d..3cc3326364648d 100644 --- a/src/tests/baseservices/threading/regressions/115178/115178.cs +++ b/src/tests/baseservices/threading/regressions/115178/115178.cs @@ -284,7 +284,6 @@ private static void RunTestInterruptInfiniteWait() } [ConditionalFact(typeof(TestLibrary.Utilities), nameof(TestLibrary.Utilities.IsWindows))] - [ActiveIssue("https://github.com/dotnet/runtime/issues/118233", typeof(TestLibrary.Utilities), nameof(TestLibrary.Utilities.IsNativeAot))] public static int TestEntryPoint() { RunTestUsingInfiniteWait(); From 614d8842f7e2898dcb89191a69ee70f324b710f4 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Fri, 1 Aug 2025 02:37:38 +0000 Subject: [PATCH 3/9] Refactor WaitHandle methods to eliminate goto labels Co-authored-by: jkotas <6668460+jkotas@users.noreply.github.com> --- .../System/Threading/WaitHandle.Windows.cs | 93 +++++++++---------- 1 file changed, 46 insertions(+), 47 deletions(-) diff --git a/src/libraries/System.Private.CoreLib/src/System/Threading/WaitHandle.Windows.cs b/src/libraries/System.Private.CoreLib/src/System/Threading/WaitHandle.Windows.cs index e40092ae0145c5..04a08770663419 100644 --- a/src/libraries/System.Private.CoreLib/src/System/Threading/WaitHandle.Windows.cs +++ b/src/libraries/System.Private.CoreLib/src/System/Threading/WaitHandle.Windows.cs @@ -55,43 +55,42 @@ private static unsafe int WaitForMultipleObjectsIgnoringSyncContext(IntPtr* pHan startTime = Interop.Kernel32.GetTickCount64(); } - retry: -#if NATIVEAOT int result; - if (reentrantWait) - { - Debug.Assert(!waitAll); - result = RuntimeImports.RhCompatibleReentrantWaitAny(true, millisecondsTimeout, numHandles, pHandles); - } - else + do { - result = (int)Interop.Kernel32.WaitForMultipleObjectsEx((uint)numHandles, (IntPtr)pHandles, waitAll ? Interop.BOOL.TRUE : Interop.BOOL.FALSE, (uint)millisecondsTimeout, Interop.BOOL.TRUE); - } +#if NATIVEAOT + if (reentrantWait) + { + Debug.Assert(!waitAll); + result = RuntimeImports.RhCompatibleReentrantWaitAny(true, millisecondsTimeout, numHandles, pHandles); + } + else + { + result = (int)Interop.Kernel32.WaitForMultipleObjectsEx((uint)numHandles, (IntPtr)pHandles, waitAll ? Interop.BOOL.TRUE : Interop.BOOL.FALSE, (uint)millisecondsTimeout, Interop.BOOL.TRUE); + } #else - int result = (int)Interop.Kernel32.WaitForMultipleObjectsEx((uint)numHandles, (IntPtr)pHandles, waitAll ? Interop.BOOL.TRUE : Interop.BOOL.FALSE, (uint)millisecondsTimeout, Interop.BOOL.TRUE); + result = (int)Interop.Kernel32.WaitForMultipleObjectsEx((uint)numHandles, (IntPtr)pHandles, waitAll ? Interop.BOOL.TRUE : Interop.BOOL.FALSE, (uint)millisecondsTimeout, Interop.BOOL.TRUE); #endif - if (result == Interop.Kernel32.WAIT_IO_COMPLETION) - { - // Handle APC completion by adjusting timeout and retrying - if (millisecondsTimeout != -1) + if (result == Interop.Kernel32.WAIT_IO_COMPLETION) { - ulong currentTime = Interop.Kernel32.GetTickCount64(); - ulong elapsed = currentTime - startTime; - - if (elapsed >= (ulong)millisecondsTimeout) + // Handle APC completion by adjusting timeout and retrying + if (millisecondsTimeout != -1) { - result = Interop.Kernel32.WAIT_TIMEOUT; - goto WaitCompleted; + ulong currentTime = Interop.Kernel32.GetTickCount64(); + ulong elapsed = currentTime - startTime; + + if (elapsed >= (ulong)millisecondsTimeout) + { + result = Interop.Kernel32.WAIT_TIMEOUT; + break; + } + + millisecondsTimeout -= (int)elapsed; + startTime = currentTime; } - - millisecondsTimeout -= (int)elapsed; - startTime = currentTime; } - goto retry; - } - - WaitCompleted: + } while (result == Interop.Kernel32.WAIT_IO_COMPLETION); currentThread.ClearWaitSleepJoinState(); if (result == Interop.Kernel32.WAIT_FAILED) @@ -137,30 +136,30 @@ private static int SignalAndWaitCore(IntPtr handleToSignal, IntPtr handleToWaitO startTime = Interop.Kernel32.GetTickCount64(); } - retry: - int ret = (int)Interop.Kernel32.SignalObjectAndWait(handleToSignal, handleToWaitOn, (uint)millisecondsTimeout, Interop.BOOL.TRUE); - - if (ret == Interop.Kernel32.WAIT_IO_COMPLETION) + int ret; + do { - // Handle APC completion by adjusting timeout and retrying - if (millisecondsTimeout != -1) + ret = (int)Interop.Kernel32.SignalObjectAndWait(handleToSignal, handleToWaitOn, (uint)millisecondsTimeout, Interop.BOOL.TRUE); + + if (ret == Interop.Kernel32.WAIT_IO_COMPLETION) { - ulong currentTime = Interop.Kernel32.GetTickCount64(); - ulong elapsed = currentTime - startTime; - - if (elapsed >= (ulong)millisecondsTimeout) + // Handle APC completion by adjusting timeout and retrying + if (millisecondsTimeout != -1) { - ret = Interop.Kernel32.WAIT_TIMEOUT; - goto WaitCompleted; + ulong currentTime = Interop.Kernel32.GetTickCount64(); + ulong elapsed = currentTime - startTime; + + if (elapsed >= (ulong)millisecondsTimeout) + { + ret = Interop.Kernel32.WAIT_TIMEOUT; + break; + } + + millisecondsTimeout -= (int)elapsed; + startTime = currentTime; } - - millisecondsTimeout -= (int)elapsed; - startTime = currentTime; } - goto retry; - } - - WaitCompleted: + } while (ret == Interop.Kernel32.WAIT_IO_COMPLETION); if (ret == Interop.Kernel32.WAIT_FAILED) { ThrowWaitFailedException(Interop.Kernel32.GetLastError()); From a017fdd0b2c5cbf5091c8371f4e5607370f2cac4 Mon Sep 17 00:00:00 2001 From: Jan Kotas Date: Thu, 31 Jul 2025 19:52:40 -0700 Subject: [PATCH 4/9] Apply suggestions from code review --- .../System/Threading/WaitHandle.Windows.cs | 64 +++++++++---------- 1 file changed, 32 insertions(+), 32 deletions(-) diff --git a/src/libraries/System.Private.CoreLib/src/System/Threading/WaitHandle.Windows.cs b/src/libraries/System.Private.CoreLib/src/System/Threading/WaitHandle.Windows.cs index 04a08770663419..53524b64017348 100644 --- a/src/libraries/System.Private.CoreLib/src/System/Threading/WaitHandle.Windows.cs +++ b/src/libraries/System.Private.CoreLib/src/System/Threading/WaitHandle.Windows.cs @@ -56,7 +56,7 @@ private static unsafe int WaitForMultipleObjectsIgnoringSyncContext(IntPtr* pHan } int result; - do + while (true) { #if NATIVEAOT if (reentrantWait) @@ -72,25 +72,25 @@ private static unsafe int WaitForMultipleObjectsIgnoringSyncContext(IntPtr* pHan result = (int)Interop.Kernel32.WaitForMultipleObjectsEx((uint)numHandles, (IntPtr)pHandles, waitAll ? Interop.BOOL.TRUE : Interop.BOOL.FALSE, (uint)millisecondsTimeout, Interop.BOOL.TRUE); #endif - if (result == Interop.Kernel32.WAIT_IO_COMPLETION) + if (result != Interop.Kernel32.WAIT_IO_COMPLETION) + break; + + // Handle APC completion by adjusting timeout and retrying + if (millisecondsTimeout != -1) { - // Handle APC completion by adjusting timeout and retrying - if (millisecondsTimeout != -1) - { - ulong currentTime = Interop.Kernel32.GetTickCount64(); - ulong elapsed = currentTime - startTime; - - if (elapsed >= (ulong)millisecondsTimeout) - { - result = Interop.Kernel32.WAIT_TIMEOUT; - break; - } + ulong currentTime = Interop.Kernel32.GetTickCount64(); + ulong elapsed = currentTime - startTime; - millisecondsTimeout -= (int)elapsed; - startTime = currentTime; + if (elapsed >= (ulong)millisecondsTimeout) + { + result = Interop.Kernel32.WAIT_TIMEOUT; + break; } + + millisecondsTimeout -= (int)elapsed; + startTime = currentTime; } - } while (result == Interop.Kernel32.WAIT_IO_COMPLETION); + } currentThread.ClearWaitSleepJoinState(); if (result == Interop.Kernel32.WAIT_FAILED) @@ -137,29 +137,29 @@ private static int SignalAndWaitCore(IntPtr handleToSignal, IntPtr handleToWaitO } int ret; - do + while (true) { ret = (int)Interop.Kernel32.SignalObjectAndWait(handleToSignal, handleToWaitOn, (uint)millisecondsTimeout, Interop.BOOL.TRUE); - if (ret == Interop.Kernel32.WAIT_IO_COMPLETION) + if (ret != Interop.Kernel32.WAIT_IO_COMPLETION) + break; + + // Handle APC completion by adjusting timeout and retrying + if (millisecondsTimeout != -1) { - // Handle APC completion by adjusting timeout and retrying - if (millisecondsTimeout != -1) + ulong currentTime = Interop.Kernel32.GetTickCount64(); + ulong elapsed = currentTime - startTime; + + if (elapsed >= (ulong)millisecondsTimeout) { - ulong currentTime = Interop.Kernel32.GetTickCount64(); - ulong elapsed = currentTime - startTime; - - if (elapsed >= (ulong)millisecondsTimeout) - { - ret = Interop.Kernel32.WAIT_TIMEOUT; - break; - } - - millisecondsTimeout -= (int)elapsed; - startTime = currentTime; + ret = Interop.Kernel32.WAIT_TIMEOUT; + break; } + + millisecondsTimeout -= (int)elapsed; + startTime = currentTime; } - } while (ret == Interop.Kernel32.WAIT_IO_COMPLETION); + } if (ret == Interop.Kernel32.WAIT_FAILED) { ThrowWaitFailedException(Interop.Kernel32.GetLastError()); From 0dd29ae04aa2e4be9596e7a8c61480e1b5df3cb4 Mon Sep 17 00:00:00 2001 From: Jan Kotas Date: Thu, 31 Jul 2025 19:53:43 -0700 Subject: [PATCH 5/9] Update src/libraries/System.Private.CoreLib/src/System/Threading/WaitHandle.Windows.cs --- .../src/System/Threading/WaitHandle.Windows.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/libraries/System.Private.CoreLib/src/System/Threading/WaitHandle.Windows.cs b/src/libraries/System.Private.CoreLib/src/System/Threading/WaitHandle.Windows.cs index 53524b64017348..f9968b2ba8022b 100644 --- a/src/libraries/System.Private.CoreLib/src/System/Threading/WaitHandle.Windows.cs +++ b/src/libraries/System.Private.CoreLib/src/System/Threading/WaitHandle.Windows.cs @@ -153,7 +153,7 @@ private static int SignalAndWaitCore(IntPtr handleToSignal, IntPtr handleToWaitO if (elapsed >= (ulong)millisecondsTimeout) { ret = Interop.Kernel32.WAIT_TIMEOUT; - break; + break; } millisecondsTimeout -= (int)elapsed; From 7027a50c7928115b6862c5505c7c873428ceb1d9 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Fri, 1 Aug 2025 03:34:33 +0000 Subject: [PATCH 6/9] Use Environment.TickCount64 and long types to avoid ulong cast Co-authored-by: jkotas <6668460+jkotas@users.noreply.github.com> --- .../System/Threading/WaitHandle.Windows.cs | 20 +++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/src/libraries/System.Private.CoreLib/src/System/Threading/WaitHandle.Windows.cs b/src/libraries/System.Private.CoreLib/src/System/Threading/WaitHandle.Windows.cs index f9968b2ba8022b..2163bbab75b732 100644 --- a/src/libraries/System.Private.CoreLib/src/System/Threading/WaitHandle.Windows.cs +++ b/src/libraries/System.Private.CoreLib/src/System/Threading/WaitHandle.Windows.cs @@ -49,10 +49,10 @@ private static unsafe int WaitForMultipleObjectsIgnoringSyncContext(IntPtr* pHan Thread currentThread = Thread.CurrentThread; currentThread.SetWaitSleepJoinState(); - ulong startTime = 0; + long startTime = 0; if (millisecondsTimeout != -1) { - startTime = Interop.Kernel32.GetTickCount64(); + startTime = Environment.TickCount64; } int result; @@ -78,10 +78,10 @@ private static unsafe int WaitForMultipleObjectsIgnoringSyncContext(IntPtr* pHan // Handle APC completion by adjusting timeout and retrying if (millisecondsTimeout != -1) { - ulong currentTime = Interop.Kernel32.GetTickCount64(); - ulong elapsed = currentTime - startTime; + long currentTime = Environment.TickCount64; + long elapsed = currentTime - startTime; - if (elapsed >= (ulong)millisecondsTimeout) + if (elapsed >= millisecondsTimeout) { result = Interop.Kernel32.WAIT_TIMEOUT; break; @@ -130,10 +130,10 @@ private static int SignalAndWaitCore(IntPtr handleToSignal, IntPtr handleToWaitO { Debug.Assert(millisecondsTimeout >= -1); - ulong startTime = 0; + long startTime = 0; if (millisecondsTimeout != -1) { - startTime = Interop.Kernel32.GetTickCount64(); + startTime = Environment.TickCount64; } int ret; @@ -147,10 +147,10 @@ private static int SignalAndWaitCore(IntPtr handleToSignal, IntPtr handleToWaitO // Handle APC completion by adjusting timeout and retrying if (millisecondsTimeout != -1) { - ulong currentTime = Interop.Kernel32.GetTickCount64(); - ulong elapsed = currentTime - startTime; + long currentTime = Environment.TickCount64; + long elapsed = currentTime - startTime; - if (elapsed >= (ulong)millisecondsTimeout) + if (elapsed >= millisecondsTimeout) { ret = Interop.Kernel32.WAIT_TIMEOUT; break; From 4141456440585f32cc10569fc9d2578289f682f1 Mon Sep 17 00:00:00 2001 From: Jan Kotas Date: Thu, 31 Jul 2025 21:03:47 -0700 Subject: [PATCH 7/9] Apply suggestions from code review --- .../src/System/Threading/WaitHandle.Windows.cs | 3 --- 1 file changed, 3 deletions(-) diff --git a/src/libraries/System.Private.CoreLib/src/System/Threading/WaitHandle.Windows.cs b/src/libraries/System.Private.CoreLib/src/System/Threading/WaitHandle.Windows.cs index 2163bbab75b732..0ab0bc0cbbb95e 100644 --- a/src/libraries/System.Private.CoreLib/src/System/Threading/WaitHandle.Windows.cs +++ b/src/libraries/System.Private.CoreLib/src/System/Threading/WaitHandle.Windows.cs @@ -80,13 +80,11 @@ private static unsafe int WaitForMultipleObjectsIgnoringSyncContext(IntPtr* pHan { long currentTime = Environment.TickCount64; long elapsed = currentTime - startTime; - if (elapsed >= millisecondsTimeout) { result = Interop.Kernel32.WAIT_TIMEOUT; break; } - millisecondsTimeout -= (int)elapsed; startTime = currentTime; } @@ -155,7 +153,6 @@ private static int SignalAndWaitCore(IntPtr handleToSignal, IntPtr handleToWaitO ret = Interop.Kernel32.WAIT_TIMEOUT; break; } - millisecondsTimeout -= (int)elapsed; startTime = currentTime; } From 54a9bb5cbf729242035a9099fce9dbda959bc280 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Fri, 1 Aug 2025 04:27:12 +0000 Subject: [PATCH 8/9] Fix SignalAndWaitCore to use WaitForSingleObjectEx for APC retry logic Co-authored-by: jkotas <6668460+jkotas@users.noreply.github.com> --- .../Windows/Kernel32/Interop.Threading.cs | 3 +++ .../src/System/Threading/WaitHandle.Windows.cs | 16 ++++++++-------- 2 files changed, 11 insertions(+), 8 deletions(-) diff --git a/src/libraries/Common/src/Interop/Windows/Kernel32/Interop.Threading.cs b/src/libraries/Common/src/Interop/Windows/Kernel32/Interop.Threading.cs index 3fddaf366a4182..b323cacc6bcaee 100644 --- a/src/libraries/Common/src/Interop/Windows/Kernel32/Interop.Threading.cs +++ b/src/libraries/Common/src/Interop/Windows/Kernel32/Interop.Threading.cs @@ -18,6 +18,9 @@ internal static partial class Kernel32 [LibraryImport(Libraries.Kernel32)] internal static partial uint WaitForSingleObject(IntPtr hHandle, uint dwMilliseconds); + [LibraryImport(Libraries.Kernel32)] + internal static partial uint WaitForSingleObjectEx(IntPtr hHandle, uint dwMilliseconds, BOOL bAlertable); + [LibraryImport(Libraries.Kernel32)] internal static partial uint SignalObjectAndWait(IntPtr hObjectToSignal, IntPtr hObjectToWaitOn, uint dwMilliseconds, BOOL bAlertable); diff --git a/src/libraries/System.Private.CoreLib/src/System/Threading/WaitHandle.Windows.cs b/src/libraries/System.Private.CoreLib/src/System/Threading/WaitHandle.Windows.cs index 0ab0bc0cbbb95e..26ab6b4278b2bf 100644 --- a/src/libraries/System.Private.CoreLib/src/System/Threading/WaitHandle.Windows.cs +++ b/src/libraries/System.Private.CoreLib/src/System/Threading/WaitHandle.Windows.cs @@ -134,15 +134,12 @@ private static int SignalAndWaitCore(IntPtr handleToSignal, IntPtr handleToWaitO startTime = Environment.TickCount64; } - int ret; - while (true) - { - ret = (int)Interop.Kernel32.SignalObjectAndWait(handleToSignal, handleToWaitOn, (uint)millisecondsTimeout, Interop.BOOL.TRUE); - - if (ret != Interop.Kernel32.WAIT_IO_COMPLETION) - break; + // Signal the object and wait for the first time + int ret = (int)Interop.Kernel32.SignalObjectAndWait(handleToSignal, handleToWaitOn, (uint)millisecondsTimeout, Interop.BOOL.TRUE); - // Handle APC completion by adjusting timeout and retrying + // Handle APC completion by retrying with WaitForSingleObjectEx (without signaling again) + while (ret == Interop.Kernel32.WAIT_IO_COMPLETION) + { if (millisecondsTimeout != -1) { long currentTime = Environment.TickCount64; @@ -156,6 +153,9 @@ private static int SignalAndWaitCore(IntPtr handleToSignal, IntPtr handleToWaitO millisecondsTimeout -= (int)elapsed; startTime = currentTime; } + + // For retries, only wait on the handle (don't signal again) + ret = (int)Interop.Kernel32.WaitForSingleObjectEx(handleToWaitOn, (uint)millisecondsTimeout, Interop.BOOL.TRUE); } if (ret == Interop.Kernel32.WAIT_FAILED) { From 4436c4e9da5de5a8522f403004fd2513471ad7dc Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Fri, 1 Aug 2025 22:22:33 +0000 Subject: [PATCH 9/9] Disable RunTestInterruptInfiniteWait for NativeAOT due to unimplemented Thread.Interrupt Co-authored-by: jkotas <6668460+jkotas@users.noreply.github.com> --- .../baseservices/threading/regressions/115178/115178.cs | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/src/tests/baseservices/threading/regressions/115178/115178.cs b/src/tests/baseservices/threading/regressions/115178/115178.cs index 3cc3326364648d..34aba004040a9d 100644 --- a/src/tests/baseservices/threading/regressions/115178/115178.cs +++ b/src/tests/baseservices/threading/regressions/115178/115178.cs @@ -288,7 +288,13 @@ public static int TestEntryPoint() { RunTestUsingInfiniteWait(); RunTestUsingTimedWait(); - RunTestInterruptInfiniteWait(); + + // Thread.Interrupt is not implemented on NativeAOT - https://github.com/dotnet/runtime/issues/69919 + if (!TestLibrary.Utilities.IsNativeAot) + { + RunTestInterruptInfiniteWait(); + } + return result; } }