From 86953da4c4b0b60c58bdcf623c3ec77cb6ed1201 Mon Sep 17 00:00:00 2001 From: Koundinya Veluri Date: Thu, 25 Jul 2019 15:21:00 -0700 Subject: [PATCH] Relax a mutex test condition (dotnet/corefx#39786) Fixes https://github.com/dotnet/corefx/issues/39694 - When doing a `WaitAny()` with mutexes that are expected to be abandoned and an event that is signaled, it has been seen in the CI on Windows that sometimes the `WaitAny()` may succeed due to event rather than identifying an abandoned mutex. The CLR doesn't do anything special and just delegates to the OS, the only way that could happen is if there could be a delay between the thread exiting and the mutex being released and abandoned. I haven't been able to repro that behavior or the test failure on my machine, it may not be easy to repro, even in the CI it fails only occasionally. - It's not a bug as long as the mutex eventually gets released and abandoned. Relaxed the condition for `WaitAny()` with an event following a mutex to account for a delay. Commit migrated from https://github.com/dotnet/corefx/commit/97d5957fc71853961082e22873d926269b291b92 --- src/libraries/System.Threading/tests/MutexTests.cs | 51 ++++++++++++++++++++-- 1 file changed, 47 insertions(+), 4 deletions(-) diff --git a/src/libraries/System.Threading/tests/MutexTests.cs b/src/libraries/System.Threading/tests/MutexTests.cs index e787d16..a4262d2 100644 --- a/src/libraries/System.Threading/tests/MutexTests.cs +++ b/src/libraries/System.Threading/tests/MutexTests.cs @@ -309,21 +309,64 @@ namespace System.Threading.Tests Assert.Equal(0, WaitHandle.WaitAny(waitHandles, 0)); AssertExtensions.Throws( () => m.WaitOne(ThreadTestHelpers.UnexpectedTimeoutMilliseconds)); + AssertExtensions.Throws( + () => m2.WaitOne(ThreadTestHelpers.UnexpectedTimeoutMilliseconds)); + break; + } + + if (waitCount != 1 && isNotAbandonedWaitObjectSignaled && notAbandonedWaitIndex != 0) + { + ame = + Assert.Throws(() => + { + ThreadTestHelpers.WaitForCondition(() => + { + // Actually expecting an exception from WaitAny(), but there may be a delay before + // the mutex is actually released and abandoned. If there is no exception, the + // WaitAny() must have succeeded due to the event being signaled. + int r = WaitHandle.WaitAny(waitHandles, ThreadTestHelpers.UnexpectedTimeoutMilliseconds); + Assert.Equal(notAbandonedWaitIndex, r); + return false; + }); + }); } else { ame = AssertExtensions.Throws( () => WaitHandle.WaitAny(waitHandles, ThreadTestHelpers.UnexpectedTimeoutMilliseconds)); - Assert.Equal(waitCount != 1 && notAbandonedWaitIndex == 0 ? 1 : 0, ame.MutexIndex); - Assert.Equal(m, ame.Mutex); } - if (m2 != null) + // Due to a potential delay in abandoning mutexes, either mutex may have been seen to be + // abandoned first + Assert.True(ame.Mutex == m || (m2 != null && ame.Mutex == m2)); + int mIndex = waitCount != 1 && notAbandonedWaitIndex == 0 ? 1 : 0; + int m2Index = waitCount != 1 && notAbandonedWaitIndex == 2 ? 1 : 2; + if (ame.Mutex == m) + { + Assert.Equal(mIndex, ame.MutexIndex); + } + else + { + Assert.True(m2Index < notAbandonedWaitIndex); + Assert.Equal(m2Index, ame.MutexIndex); + } + + // Verify that the other mutex also gets abandoned + if (ame.MutexIndex == mIndex) + { + if (m2 != null) + { + AssertExtensions.Throws( + () => m2.WaitOne(ThreadTestHelpers.UnexpectedTimeoutMilliseconds)); + } + } + else { AssertExtensions.Throws( - () => m2.WaitOne(ThreadTestHelpers.UnexpectedTimeoutMilliseconds)); + () => m.WaitOne(ThreadTestHelpers.UnexpectedTimeoutMilliseconds)); } + break; case WaitHandleWaitType.WaitAll: -- 2.7.4