Relax a mutex test condition (dotnet/corefx#39786)
authorKoundinya Veluri <kouvel@users.noreply.github.com>
Thu, 25 Jul 2019 22:21:00 +0000 (15:21 -0700)
committerStephen Toub <stoub@microsoft.com>
Thu, 25 Jul 2019 22:21:00 +0000 (18:21 -0400)
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

index e787d16..a4262d2 100644 (file)
@@ -309,21 +309,64 @@ namespace System.Threading.Tests
                                 Assert.Equal(0, WaitHandle.WaitAny(waitHandles, 0));
                                 AssertExtensions.Throws<AbandonedMutexException, bool>(
                                     () => m.WaitOne(ThreadTestHelpers.UnexpectedTimeoutMilliseconds));
+                                AssertExtensions.Throws<AbandonedMutexException, bool>(
+                                    () => m2.WaitOne(ThreadTestHelpers.UnexpectedTimeoutMilliseconds));
+                                break;
+                            }
+
+                            if (waitCount != 1 && isNotAbandonedWaitObjectSignaled && notAbandonedWaitIndex != 0)
+                            {
+                                ame =
+                                    Assert.Throws<AbandonedMutexException>(() =>
+                                    {
+                                        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<AbandonedMutexException, int>(
                                         () => 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<AbandonedMutexException, bool>(
+                                        () => m2.WaitOne(ThreadTestHelpers.UnexpectedTimeoutMilliseconds));
+                                }
+                            }
+                            else
                             {
                                 AssertExtensions.Throws<AbandonedMutexException, bool>(
-                                    () => m2.WaitOne(ThreadTestHelpers.UnexpectedTimeoutMilliseconds));
+                                    () => m.WaitOne(ThreadTestHelpers.UnexpectedTimeoutMilliseconds));
                             }
+
                             break;
 
                         case WaitHandleWaitType.WaitAll: