// ========================= private void TakeWakesUpTest(DiskQueue <int> queue) { Barrier bar = new Barrier(2); AtomicNullableBool takeResult = new AtomicNullableBool(); AtomicNullableBool takeResult2 = new AtomicNullableBool(); Task task = Task.Run(() => { bar.SignalAndWait(); int item = 0; takeResult.Value = queue.TryTake(out item, 60000); Assert.AreEqual(100, item); item = 0; takeResult2.Value = queue.TryTake(out item, 60000); Assert.AreEqual(200, item); }); bar.SignalAndWait(); Thread.Sleep(10); Assert.IsFalse(takeResult.HasValue); queue.Add(100); TimingAssert.AreEqual(10000, true, () => takeResult.Value); Thread.Sleep(10); Assert.IsFalse(takeResult2.HasValue); queue.Add(200); TimingAssert.AreEqual(10000, true, () => takeResult2.Value); task.Wait(); }
public void TestSingleDecreaseWork() { PartialThreadBlocker inst = new PartialThreadBlocker(4); Barrier startBar = new Barrier(1 + 1); int exitedCount = 0; Task.Run(() => { startBar.SignalAndWait(); inst.Wait(); Interlocked.Increment(ref exitedCount); }); startBar.SignalAndWait(); TimingAssert.AreEqual(5000, 4, () => inst.ExpectedWaiterCount); TimingAssert.AreEqual(5000, 1, () => inst.RealWaiterCount, "RealWaiterCount != 1"); for (int i = 1; i <= 4; i++) { inst.SubstractExpectedWaiterCount(1); TimingAssert.AreEqual(5000, 4 - i, () => inst.ExpectedWaiterCount); TimingAssert.AreEqual(5000, i == 4 ? 0 : 1, () => inst.RealWaiterCount); } TimingAssert.AreEqual(5000, 1, () => Volatile.Read(ref exitedCount), "exitedCount != 1"); }
public void TestCancellationWork() { PartialThreadBlocker inst = new PartialThreadBlocker(4); CancellationTokenSource tokSrc = new CancellationTokenSource(); int exitedCount = 0; Task.Run(() => { try { inst.Wait(tokSrc.Token); } catch (OperationCanceledException) { } Interlocked.Increment(ref exitedCount); }); TimingAssert.AreEqual(5000, 4, () => inst.ExpectedWaiterCount); TimingAssert.AreEqual(5000, 1, () => inst.RealWaiterCount); tokSrc.Cancel(); TimingAssert.AreEqual(5000, 4, () => inst.ExpectedWaiterCount); TimingAssert.AreEqual(5000, 0, () => inst.RealWaiterCount); TimingAssert.AreEqual(5000, 1, () => Volatile.Read(ref exitedCount)); }
public void TestNotificationWithPredicate() { using (var testInst = new MonitorObject()) { int result = 0; int state = 0; var task = Task.Run(() => { using (var waiter = testInst.Enter(60000)) { if (waiter.Wait((s) => Volatile.Read(ref state) > 0, new object())) { Interlocked.Exchange(ref result, 1); } else { Interlocked.Exchange(ref result, 2); } } }); TimingAssert.AreEqual(10000, 1, () => testInst.WaiterCount); Assert.AreEqual(0, Volatile.Read(ref result)); testInst.Pulse(); Thread.Sleep(100); Assert.AreEqual(0, Volatile.Read(ref result)); Interlocked.Increment(ref state); testInst.Pulse(); TimingAssert.AreEqual(10000, 1, () => Volatile.Read(ref result)); task.Wait(); } }
public void PeekWakesUpTest() { var queue = new BlockingQueue <int>(); Barrier bar = new Barrier(3); AtomicNullableBool peekResult = new AtomicNullableBool(); AtomicNullableBool peekResult2 = new AtomicNullableBool(); Task task = Task.Run(() => { bar.SignalAndWait(); int item = 0; peekResult.Value = queue.TryPeek(out item, 60000); Assert.AreEqual(100, item); }); Task task2 = Task.Run(() => { bar.SignalAndWait(); int item = 0; peekResult2.Value = queue.TryPeek(out item, 60000); Assert.AreEqual(100, item); }); bar.SignalAndWait(); Thread.Sleep(20); Assert.IsFalse(peekResult.HasValue); Assert.IsFalse(peekResult2.HasValue); queue.Add(100); TimingAssert.AreEqual(10000, true, () => peekResult.Value); TimingAssert.AreEqual(10000, true, () => peekResult2.Value); Task.WaitAll(task, task2); }
public void TestNotificationReceived() { using (var testInst = new MonitorObject()) { int result = 0; var task = Task.Run(() => { using (var waiter = testInst.Enter(60000)) { if (waiter.Wait()) { Interlocked.Exchange(ref result, 1); } else { Interlocked.Exchange(ref result, 2); } } }); TimingAssert.AreEqual(10000, 1, () => testInst.WaiterCount); Assert.AreEqual(0, Volatile.Read(ref result)); testInst.Pulse(); TimingAssert.AreEqual(10000, 1, () => Volatile.Read(ref result)); task.Wait(); } }
public void TestSingleThreadWakeUpOnSignal() { using (var testInst = new MonitorObject()) { int exitCount = 0; int state = 0; List <Task> tasks = new List <Task>(); for (int i = 0; i < 6; i++) { var task = Task.Run(() => { using (var waiter = testInst.Enter()) { waiter.Wait(_ => { return(Volatile.Read(ref state) > 0); }, (object)null); Interlocked.Increment(ref exitCount); } }); tasks.Add(task); } TimingAssert.AreEqual(10000, 6, () => testInst.WaiterCount); Interlocked.Increment(ref state); for (int i = 0; i < 6; i++) { testInst.Pulse(); TimingAssert.AreEqual(10000, 5 - i, () => testInst.WaiterCount); Thread.Sleep(50); TimingAssert.AreEqual(10000, i + 1, () => Volatile.Read(ref exitCount)); } Task.WaitAll(tasks.ToArray()); } }
public void TestIsTimeoutedWorks() { using (var testInst = new MonitorObject()) { int result = 0; int cycleCount = 0; var task = Task.Run(() => { using (var waiter = testInst.Enter(200)) { while (!waiter.IsTimeouted) { waiter.Wait(10); Interlocked.Increment(ref cycleCount); } Interlocked.Exchange(ref result, 1); } }); TimingAssert.AreEqual(10000, 1, () => testInst.WaiterCount); TimingAssert.AreEqual(10000, 1, () => Volatile.Read(ref result)); Assert.IsTrue(Volatile.Read(ref cycleCount) > 0); task.Wait(); } }
public void TestCancellationWorksInWaitNoParam() { using (var testInst = new MonitorObject()) { int result = 0; CancellationTokenSource tokenSrc = new CancellationTokenSource(); var task = Task.Run(() => { try { using (var waiter = testInst.Enter(60000, tokenSrc.Token)) { while (!waiter.Wait()) { } Interlocked.Exchange(ref result, 1); } } catch (OperationCanceledException) { Interlocked.Exchange(ref result, 3); } }); testInst.Pulse(); Thread.Sleep(100); Assert.AreEqual(0, Volatile.Read(ref result)); tokenSrc.Cancel(); TimingAssert.AreEqual(10000, 3, () => Volatile.Read(ref result)); } }
public void TestSendOrPostCallbackSyncThreadPoolWorkItem() { int value = 0; System.Threading.SendOrPostCallback act = (s) => { Interlocked.Increment(ref value); }; var item = new SendOrPostCallbackSyncThreadPoolWorkItem(act, null); bool waitFinished = false; int startedFlag = 0; Task.Run(() => { Interlocked.Exchange(ref startedFlag, 1); item.Wait(); Volatile.Write(ref waitFinished, true); }); TimingAssert.IsTrue(5000, () => Volatile.Read(ref startedFlag) == 1); Thread.Sleep(100); Assert.AreEqual(0, value); Assert.AreEqual(false, waitFinished); item.Run(false, false); TimingAssert.AreEqual(5000, 1, () => Volatile.Read(ref value)); TimingAssert.AreEqual(5000, true, () => Volatile.Read(ref waitFinished)); }
public void TestCustomTimeoutWorks() { using (var testInst = new MonitorObject()) { int result = 0; var task = Task.Run(() => { using (var waiter = testInst.Enter(60000)) { if (waiter.Wait(100)) { Interlocked.Exchange(ref result, 1); } else { Interlocked.Exchange(ref result, 2); } } }); TimingAssert.AreEqual(10000, 1, () => testInst.WaiterCount); TimingAssert.AreEqual(10000, 2, () => Volatile.Read(ref result)); task.Wait(); } }
public void TestWaitingPassed() { using (var inst = new MutuallyExclusivePrimitive()) { Barrier bar = new Barrier(2); int result = 0; var task = Task.Run(() => { bar.SignalAndWait(); using (var guard = inst.EnterBackground(60000, default(CancellationToken))) { if (guard.IsAcquired) { Interlocked.Exchange(ref result, 1); } else { Interlocked.Exchange(ref result, 2); } } }); bar.SignalAndWait(); Thread.Sleep(20); inst.AllowBackgroundGate(); TimingAssert.AreEqual(10000, 1, () => Volatile.Read(ref result)); task.Wait(); } }
public void SegmentBackgroundCompactionTest() { var segmentFactory = new MemorySegmentFactory <int>(10); using (var queue = new DiskQueue <int>("dummy", segmentFactory, -1, true, 100)) { Assert.AreEqual(1, queue.SegmentCount); for (int i = 0; i < segmentFactory.Capacity; i++) { queue.Add(i); } Assert.AreEqual(1, queue.SegmentCount); queue.Add(-1); Assert.AreEqual(2, queue.SegmentCount); Assert.AreEqual(2, segmentFactory.AllocatedSegments.Count); for (int i = 0; i < segmentFactory.Capacity; i++) { queue.Take(); } Assert.IsTrue(segmentFactory.AllocatedSegments[0].IsCompleted); TimingAssert.AreEqual(10000, 1, () => { Interlocked.MemoryBarrier(); return(queue.SegmentCount); }); Assert.AreEqual(2, segmentFactory.AllocatedSegments.Count); } }
// ========================= private void TimeoutWorksTest(DiskQueue <int> queue) { Barrier bar = new Barrier(2); AtomicNullableBool takeResult = new AtomicNullableBool(); AtomicNullableBool peekResult = new AtomicNullableBool(); AtomicNullableBool addResult = new AtomicNullableBool(); Task task = Task.Run(() => { bar.SignalAndWait(); int item = 0; takeResult.Value = queue.TryTake(out item, 100); peekResult.Value = queue.TryPeek(out item, 100); while (queue.TryAdd(-1)) { ; } addResult.Value = queue.TryAdd(100, 100); }); bar.SignalAndWait(); TimingAssert.AreEqual(10000, false, () => takeResult.Value, "take"); TimingAssert.AreEqual(10000, false, () => peekResult.Value, "peek"); TimingAssert.AreEqual(10000, false, () => addResult.Value, "Add"); task.Wait(); }
public void TestSimpleRunAndStop() { int threadStart = 0; using (DelegateThreadSetManager testInst = new DelegateThreadSetManager(Environment.ProcessorCount, "name", (id, state, token) => { Interlocked.Increment(ref threadStart); })) { Assert.IsTrue(testInst.State == ThreadSetManagerState.Created, "State != Created"); Assert.AreEqual(Environment.ProcessorCount, testInst.ThreadCount); Assert.IsFalse(testInst.IsWork); testInst.Start(); SpinWait.SpinUntil(() => Volatile.Read(ref threadStart) >= testInst.ThreadCount, 10000); TestContext.WriteLine("All thread started"); bool byCondition = SpinWait.SpinUntil(() => testInst.ActiveThreadCount == 0, 5000); TestContext.WriteLine(byCondition ? "ActiveThreadCount == 0" : "ActiveThreadCount != 0 (timeout)"); TimingAssert.AreEqual(15000, Environment.ProcessorCount, () => Volatile.Read(ref threadStart)); TimingAssert.IsTrue(5000, () => testInst.State == ThreadSetManagerState.AllThreadsExited, "State != AllThreadsExited"); testInst.Stop(); Assert.IsTrue(testInst.State == ThreadSetManagerState.Stopped, "State != Stopped"); } }
public void TestBlockRequiredCount() { PartialThreadBlocker inst = new PartialThreadBlocker(4); Barrier startBar = new Barrier(8 + 1); int exitedCount = 0; for (int i = 0; i < 8; i++) { Task.Run(() => { startBar.SignalAndWait(); inst.Wait(); Interlocked.Increment(ref exitedCount); }); } startBar.SignalAndWait(); Assert.IsTrue(SpinWait.SpinUntil(() => Volatile.Read(ref exitedCount) >= 4 && inst.RealWaiterCount >= 4, 4000)); TimingAssert.AreEqual(5000, 8 - 4, () => Volatile.Read(ref exitedCount), "exitedCount != 8"); TimingAssert.AreEqual(5000, 4, () => inst.ExpectedWaiterCount, "ExpectedWaiterCount != 4"); TimingAssert.AreEqual(5000, 4, () => inst.RealWaiterCount, "RealWaiterCount != 4"); inst.SetExpectedWaiterCount(0); TimingAssert.AreEqual(5000, 8, () => Volatile.Read(ref exitedCount), "exitedCount != 8"); TimingAssert.AreEqual(5000, 0, () => inst.ExpectedWaiterCount, "ExpectedWaiterCount != 0"); TimingAssert.AreEqual(5000, 0, () => inst.RealWaiterCount, "RealWaiterCount != 0"); }
// ========================= private void AddWakesUpTest(DiskQueue <int> queue, int segmentCapacity) { while (queue.TryAdd(100)) { ; // Fill queue } queue.AddForced(200); Barrier bar = new Barrier(2); AtomicNullableBool addResult = new AtomicNullableBool(); Task task = Task.Run(() => { bar.SignalAndWait(); addResult.Value = queue.TryAdd(-100, 60000); }); bar.SignalAndWait(); Thread.Sleep(10); Assert.IsFalse(addResult.HasValue); queue.Take(); Thread.Sleep(10); Assert.IsFalse(addResult.HasValue); for (int i = 0; i < segmentCapacity; i++) { int tmp = 0; Assert.IsTrue(queue.TryTake(out tmp)); } TimingAssert.AreEqual(10000, true, () => addResult.Value); task.Wait(); }
public void TestPredicateCalledOnTimeout() { object syncObj = new object(); using (var testInst = new ConditionVariableAlt(syncObj)) { int result = 0; int state = 0; int called = 0; var task = Task.Run(() => { using (var waiter = testInst.Enter(100)) { if (waiter.Wait((s) => { Interlocked.Increment(ref called); return(Volatile.Read(ref state) > 0); }, new object())) { Interlocked.Exchange(ref result, 1); } else { Interlocked.Exchange(ref result, 2); } } }); TimingAssert.AreEqual(10000, 1, () => testInst.WaiterCount); TimingAssert.AreEqual(10000, 2, () => Volatile.Read(ref result)); TimingAssert.AreEqual(10000, 2, () => Volatile.Read(ref called)); } }
public void TestNotificationReceived() { object syncObj = new object(); using (var testInst = new ConditionVariableAlt(syncObj)) { int result = 0; var task = Task.Run(() => { using (var waiter = testInst.Enter(60000)) { if (waiter.Wait()) { Interlocked.Exchange(ref result, 1); } else { Interlocked.Exchange(ref result, 2); } } }); TimingAssert.AreEqual(10000, 1, () => testInst.WaiterCount); Assert.AreEqual(0, Volatile.Read(ref result)); lock (syncObj) { testInst.Pulse(); } TimingAssert.AreEqual(10000, 1, () => Volatile.Read(ref result)); } }
public void TestInterruptOnDispose() { object syncObj = new object(); using (var testInst = new ConditionVariableAlt(syncObj)) { int exitCount = 0; var task = Task.Run(() => { try { using (var waiter = testInst.Enter()) { waiter.Wait(_ => false, (object)null); } } catch (OperationInterruptedException) { } Interlocked.Increment(ref exitCount); }); TimingAssert.AreEqual(10000, 1, () => testInst.WaiterCount); testInst.Dispose(); TimingAssert.AreEqual(10000, 1, () => Volatile.Read(ref exitCount)); task.Wait(); } }
public void TestAllThreadWakeUpOnSignalAll() { object syncObj = new object(); using (var testInst = new ConditionVariableAlt(syncObj)) { int exitCount = 0; int state = 0; List <Task> tasks = new List <Task>(); for (int i = 0; i < 6; i++) { var task = Task.Run(() => { using (var waiter = testInst.Enter()) { waiter.Wait(_ => { return(Volatile.Read(ref state) > 0); }, (object)null); Interlocked.Increment(ref exitCount); } }); tasks.Add(task); } TimingAssert.AreEqual(10000, 6, () => testInst.WaiterCount); Interlocked.Increment(ref state); lock (syncObj) { testInst.PulseAll(); } TimingAssert.AreEqual(10000, 0, () => testInst.WaiterCount); TimingAssert.AreEqual(10000, 6, () => Volatile.Read(ref exitCount)); } }
public void TestLongPredicateEstimatesOnceWithSmallTimeout() { object syncObj = new object(); using (var testInst = new ConditionVariableAlt(syncObj)) { int result = 0; int estimCount = 0; var task = Task.Run(() => { using (var waiter = testInst.Enter(200)) { if (waiter.Wait(_ => { Interlocked.Increment(ref estimCount); Thread.Sleep(500); return(false); }, (object)null)) { Interlocked.Exchange(ref result, 1); } else { Interlocked.Exchange(ref result, 2); } } }); lock (syncObj) { testInst.Pulse(); } TimingAssert.AreEqual(10000, 2, () => Volatile.Read(ref result)); Assert.AreEqual(1, Volatile.Read(ref estimCount)); } }
public void TestTimeoutWorks() { object syncObj = new object(); using (var testInst = new ConditionVariableAlt(syncObj)) { int result = 0; var task = Task.Run(() => { using (var waiter = testInst.Enter(500)) { if (waiter.Wait(_ => false, (object)null)) { Interlocked.Exchange(ref result, 1); } else { Interlocked.Exchange(ref result, 2); } } }); lock (syncObj) { testInst.Pulse(); } TimingAssert.AreEqual(10000, 2, () => Volatile.Read(ref result)); } }
private void RunComplexTest() { using (EntryCountingEvent inst = new EntryCountingEvent()) { Barrier bar = new Barrier(7); int threadFinished = 0; int entryCount = 0; bool isTestDispose = false; List <Task> taskList = new List <Task>(); for (int i = 0; i < 6; i++) { int a = i; var task = Task.Run(() => { Random rnd = new Random(a); bar.SignalAndWait(); for (int j = 0; j < 1000; j++) { using (var eee = inst.TryEnter()) { if (!eee.IsAcquired) { break; } Interlocked.Increment(ref entryCount); if (isTestDispose) { throw new Exception(); } Thread.Sleep(rnd.Next(10, 100)); if (isTestDispose) { throw new Exception(); } } } Interlocked.Increment(ref threadFinished); }); taskList.Add(task); } bar.SignalAndWait(); TimingAssert.IsTrue(5000, () => Volatile.Read(ref entryCount) > 12); inst.TerminateAndWait(); isTestDispose = true; inst.Dispose(); TimingAssert.AreEqual(5000, 6, () => Volatile.Read(ref threadFinished)); Task.WhenAll(taskList); } }
public void TestPredicateCalledInsideLock() { object syncObj = new object(); using (var testInst = new ConditionVariable(syncObj)) { int result = 0; int estimCount = 0; int inMonitorCount = 0; int stopEstim = 0; var task = Task.Run(() => { using (var waiter = testInst.Enter(60000)) { if (waiter.Wait(_ => { if (Monitor.IsEntered(syncObj)) { Interlocked.Increment(ref inMonitorCount); } Interlocked.Increment(ref estimCount); return(Volatile.Read(ref stopEstim) != 0); }, (object)null)) { Interlocked.Exchange(ref result, 1); } else { Interlocked.Exchange(ref result, 2); } } }); TimingAssert.AreEqual(10000, 1, () => testInst.WaiterCount); for (int i = 0; i < 20; i++) { lock (syncObj) { testInst.Pulse(); } Thread.Sleep(10); } Interlocked.Increment(ref stopEstim); lock (syncObj) { testInst.Pulse(); } TimingAssert.AreEqual(10000, 1, () => Volatile.Read(ref result)); Assert.IsTrue(Volatile.Read(ref estimCount) > 1); Assert.AreEqual(Volatile.Read(ref inMonitorCount), Volatile.Read(ref estimCount)); } }
public void TestWaitingWithPredicate() { using (var ev = new SignalEvent()) { int waiterEntered = 0; int waitResult = 0; int waitCondition = 0; int condEvaluated = 0; object waiterRef = null; var task = Task.Run(() => { using (var waiter = ev.Factory.CreateWaiter()) { Interlocked.Exchange(ref waiterRef, waiter); lock (waiter) { Interlocked.Increment(ref waiterEntered); if (waiter.Wait(s => { Interlocked.Increment(ref condEvaluated); return(Volatile.Read(ref waitCondition) > 0); }, (object)null, 60000)) { Interlocked.Exchange(ref waitResult, 1); } else { Interlocked.Exchange(ref waitResult, 2); } } } }); TimingAssert.AreEqual(10000, 1, () => Volatile.Read(ref waiterEntered)); lock (waiterRef) { ev.Signal(); } TimingAssert.AreEqual(10000, 2, () => Volatile.Read(ref condEvaluated)); lock (waiterRef) { Assert.AreEqual(0, Volatile.Read(ref waitResult)); } lock (waiterRef) { Interlocked.Increment(ref waitCondition); ev.Signal(); } TimingAssert.AreEqual(10000, 3, () => Volatile.Read(ref condEvaluated)); TimingAssert.AreEqual(10000, 1, () => Volatile.Read(ref waitResult)); task.Wait(); } }
public void TestTryRunWork() { using (DynamicThreadPool testInst = new DynamicThreadPool(0, Environment.ProcessorCount, 100, "name")) { int wasExecuted = 0; bool wasRunned = testInst.TryRun(() => { Interlocked.Exchange(ref wasExecuted, 1); }); Assert.IsTrue(wasRunned); TimingAssert.AreEqual(5000, 1, () => Volatile.Read(ref wasExecuted)); } }
public void TestExceptionFromPredicatePassed() { using (var testInst = new MonitorObject()) { int result = 0; int state = 0; var task = Task.Run(() => { using (var waiter = testInst.Enter(60000)) { if (waiter.Wait((s) => { if (Volatile.Read(ref state) > 0) { throw new ApplicationException(); } return(false); }, new object())) { Interlocked.Exchange(ref result, 1); } else { Interlocked.Exchange(ref result, 2); } } }); TimingAssert.AreEqual(10000, 1, () => testInst.WaiterCount); Assert.AreEqual(0, Volatile.Read(ref result)); testInst.Pulse(); Thread.Sleep(1); Assert.AreEqual(0, Volatile.Read(ref result)); Interlocked.Increment(ref state); testInst.Pulse(); TimingAssert.AreEqual(10000, 0, () => testInst.WaiterCount); Assert.AreEqual(0, result); try { task.Wait(); } catch (AggregateException aE) { if (aE.InnerExceptions.Count != 1 || !(aE.InnerExceptions[0] is ApplicationException)) { throw; } } } }
// ========================= private void AddWakesUpTest(LevelingQueue <int> queue) { while (queue.TryAdd(100)) { ; // Fill queue } if (queue.IsBackgroundTransferingEnabled) { SpinWait sw = new SpinWait(); while (queue.IsBackgroundInWork && sw.Count < 100) { sw.SpinOnce(); } for (int i = 0; i < 100; i++) { queue.TryAdd(100); SpinWaitHelper.SpinWait(12); } } Barrier bar = new Barrier(2); int addResult = 0; Task task = Task.Run(() => { bar.SignalAndWait(); AtomicSet(ref addResult, queue.TryAdd(-100, 60000)); }); bar.SignalAndWait(); Thread.Sleep(10); Assert.AreEqual(0, Volatile.Read(ref addResult)); queue.Take(); if (queue.AddingMode == LevelingQueueAddingMode.PreserveOrder && !queue.IsBackgroundTransferingEnabled) { int item; while (queue.TryTake(out item)) { ; } } TimingAssert.AreEqual(10000, 1, () => Volatile.Read(ref addResult)); task.Wait(); }
public void TestIncreaseWaiters() { PartialThreadBlocker inst = new PartialThreadBlocker(4); Barrier startBar = new Barrier(8 + 1); int exitedCount = 0; int somethingWork = 0; CancellationTokenSource tokenSrc = new CancellationTokenSource(); for (int i = 0; i < 8; i++) { Task.Run(() => { startBar.SignalAndWait(); while (!tokenSrc.IsCancellationRequested) { inst.Wait(); Interlocked.Increment(ref somethingWork); Thread.Sleep(10); } Interlocked.Increment(ref exitedCount); }); } startBar.SignalAndWait(); TimingAssert.AreEqual(5000, 4, () => inst.ExpectedWaiterCount); TimingAssert.AreEqual(5000, 4, () => inst.RealWaiterCount, "Real waiter count != 4 (can be caused by slow processing)"); TimingAssert.IsTrue(5000, () => Volatile.Read(ref somethingWork) > 0); inst.SetExpectedWaiterCount(8); Assert.AreEqual(8, inst.ExpectedWaiterCount); TimingAssert.AreEqual(5000, 8, () => inst.RealWaiterCount); Interlocked.Exchange(ref somethingWork, 0); TimingAssert.IsTrue(5000, () => Volatile.Read(ref somethingWork) == 0); tokenSrc.Cancel(); inst.SetExpectedWaiterCount(0); TimingAssert.AreEqual(5000, 8, () => Volatile.Read(ref exitedCount)); TimingAssert.AreEqual(5000, 0, () => inst.ExpectedWaiterCount); TimingAssert.AreEqual(500, 0, () => inst.RealWaiterCount); }