}
}
+ public enum WaitHandleWaitType
+ {
+ WaitOne,
+ WaitAny,
+ WaitAll
+ }
+
private static IEnumerable<string> GetNamePrefixes()
{
yield return string.Empty;
public static IEnumerable<object[]> AbandonExisting_MemberData()
{
var nameGuidStr = Guid.NewGuid().ToString("N");
- for (int waitType = 0; waitType < 2; ++waitType) // 0 == WaitOne, 1 == WaitAny
+ foreach (WaitHandleWaitType waitType in Enum.GetValues(typeof(WaitHandleWaitType)))
{
- yield return new object[] { null, waitType };
- foreach (var namePrefix in GetNamePrefixes())
+ foreach (int waitCount in new int[] { 1, 3 })
{
- yield return new object[] { namePrefix + nameGuidStr, waitType };
+ if (waitType == WaitHandleWaitType.WaitOne && waitCount != 1)
+ {
+ continue;
+ }
+
+ for (int notAbandonedWaitIndex = 0; notAbandonedWaitIndex < waitCount; ++notAbandonedWaitIndex)
+ {
+ foreach (bool abandonDuringWait in new bool[] { false, true })
+ {
+ var args =
+ new object[]
+ {
+ null, // name
+ waitType,
+ waitCount,
+ notAbandonedWaitIndex,
+ false, // isNotAbandonedWaitObjectSignaled
+ abandonDuringWait
+ };
+
+ bool includeArgsForSignaledNotAbandonedWaitObject =
+ waitCount != 1 && (waitType == WaitHandleWaitType.WaitAll || !abandonDuringWait);
+
+ yield return (object[])args.Clone();
+ if (includeArgsForSignaledNotAbandonedWaitObject)
+ {
+ var newArgs = (object[])args.Clone();
+ newArgs[4] = true; // isNotAbandonedWaitObjectSignaled
+ yield return newArgs;
+ }
+
+ if (waitCount == 1 || PlatformDetection.IsWindows)
+ {
+ foreach (var namePrefix in GetNamePrefixes())
+ {
+ var newArgs = (object[])args.Clone();
+ newArgs[0] = namePrefix + nameGuidStr;
+ yield return newArgs;
+ if (includeArgsForSignaledNotAbandonedWaitObject)
+ {
+ newArgs = (object[])newArgs.Clone();
+ newArgs[4] = true; // isNotAbandonedWaitObjectSignaled
+ yield return newArgs;
+ }
+ }
+ }
+ }
+ }
}
}
}
[Theory]
[MemberData(nameof(AbandonExisting_MemberData))]
- public void AbandonExisting(string name, int waitType)
+ public void AbandonExisting(
+ string name,
+ WaitHandleWaitType waitType,
+ int waitCount,
+ int notAbandonedWaitIndex,
+ bool isNotAbandonedWaitObjectSignaled,
+ bool abandonDuringWait)
{
ThreadTestHelpers.RunTestInBackgroundThread(() =>
{
using (var m = new Mutex(false, name))
+ using (Mutex m2 = waitCount == 1 ? null : new Mutex(false, name == null ? null : name + "_2"))
+ using (ManualResetEvent e = waitCount == 1 ? null : new ManualResetEvent(isNotAbandonedWaitObjectSignaled))
+ using (ManualResetEvent threadReadyForAbandon = abandonDuringWait ? new ManualResetEvent(false) : null)
+ using (ManualResetEvent abandonSoon = abandonDuringWait ? new ManualResetEvent(false) : null)
{
- Task t = Task.Factory.StartNew(() =>
+ WaitHandle[] waitHandles = null;
+ if (waitType != WaitHandleWaitType.WaitOne)
{
- m.CheckedWait();
- // don't release the mutex; abandon it on this thread
- }, CancellationToken.None, TaskCreationOptions.LongRunning, TaskScheduler.Default);
- t.CheckedWait();
+ waitHandles = new WaitHandle[waitCount];
+ if (waitCount == 1)
+ {
+ waitHandles[0] = m;
+ }
+ else
+ {
+ waitHandles[notAbandonedWaitIndex] = e;
+ waitHandles[notAbandonedWaitIndex == 0 ? 1 : 0] = m;
+ waitHandles[notAbandonedWaitIndex == 2 ? 1 : 2] = m2;
+ }
+ }
+ Thread t = ThreadTestHelpers.CreateGuardedThread(out Action waitForThread, () =>
+ {
+ Assert.True(m.WaitOne(0));
+ if (m2 != null)
+ {
+ Assert.True(m2.WaitOne(0));
+ }
+
+ if (abandonDuringWait)
+ {
+ threadReadyForAbandon.Set();
+ abandonSoon.CheckedWait();
+ Thread.Sleep(ThreadTestHelpers.ExpectedTimeoutMilliseconds);
+ }
+
+ // don't release the mutexes; abandon them on this thread
+ });
+ t.IsBackground = true;
+ t.Start();
+
+ if (abandonDuringWait)
+ {
+ threadReadyForAbandon.CheckedWait();
+ abandonSoon.Set();
+ }
+ else
+ {
+ waitForThread();
+ }
+
+ AbandonedMutexException ame;
switch (waitType)
{
- case 0: // WaitOne
- Assert.Throws<AbandonedMutexException>(() => m.CheckedWait());
+ case WaitHandleWaitType.WaitOne:
+ ame =
+ AssertExtensions.Throws<AbandonedMutexException, bool>(
+ () => m.WaitOne(ThreadTestHelpers.UnexpectedTimeoutMilliseconds));
+ Assert.Equal(-1, ame.MutexIndex);
+ Assert.Null(ame.Mutex);
+ break;
+
+ case WaitHandleWaitType.WaitAny:
+ if (waitCount != 1 && isNotAbandonedWaitObjectSignaled && notAbandonedWaitIndex == 0)
+ {
+ Assert.Equal(0, WaitHandle.WaitAny(waitHandles, 0));
+ AssertExtensions.Throws<AbandonedMutexException, bool>(
+ () => m.WaitOne(ThreadTestHelpers.UnexpectedTimeoutMilliseconds));
+ }
+ 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)
+ {
+ AssertExtensions.Throws<AbandonedMutexException, bool>(
+ () => m2.WaitOne(ThreadTestHelpers.UnexpectedTimeoutMilliseconds));
+ }
break;
- case 1: // WaitAny
- AbandonedMutexException ame =
- Assert.Throws<AbandonedMutexException>(
- () => WaitHandle.WaitAny(new[] { m }, ThreadTestHelpers.UnexpectedTimeoutMilliseconds));
- Assert.Equal(0, ame.MutexIndex);
- Assert.Equal(m, ame.Mutex);
+ case WaitHandleWaitType.WaitAll:
+ if (waitCount != 1 && !isNotAbandonedWaitObjectSignaled)
+ {
+ Assert.False(WaitHandle.WaitAll(waitHandles, ThreadTestHelpers.ExpectedTimeoutMilliseconds * 2));
+ Assert.True(e.Set());
+ }
+
+ ame =
+ AssertExtensions.Throws<AbandonedMutexException, bool>(
+ () => WaitHandle.WaitAll(waitHandles, ThreadTestHelpers.UnexpectedTimeoutMilliseconds));
+ Assert.Equal(-1, ame.MutexIndex);
+ Assert.Null(ame.Mutex);
break;
}
+
+ if (abandonDuringWait)
+ {
+ waitForThread();
+ }
+
+ m.ReleaseMutex();
+ m2?.ReleaseMutex();
}
});
}