public static void AtomicDowngradeTest() { var trwl = new TestReaderWriterLock(); trwl.AcquireReaderLock(); TestLockCookie tlc = trwl.UpgradeToWriterLock(); Action waitForWaitingWriter; Thread waitingWriter = ThreadTestHelpers.CreateGuardedThread(out waitForWaitingWriter, () => { trwl.AcquireWriterLock(); trwl.ReleaseWriterLock(); }); waitingWriter.Start(); ThreadTestHelpers.WaitForCondition(() => (waitingWriter.ThreadState & ThreadState.WaitSleepJoin) != 0); // Downgrade to a read lock successfully while there is a waiting writer trwl.DowngradeFromWriterLock(tlc); // Releasing the read lock releases the waiting writer trwl.ReleaseReaderLock(); waitForWaitingWriter(); trwl.Dispose(); }
public static void AtomicRecursiveReaderTest() { var trwl = new TestReaderWriterLock(); trwl.AcquireReaderLock(); Action waitForWaitingWriter; Thread waitingWriter = ThreadTestHelpers.CreateGuardedThread(out waitForWaitingWriter, () => { trwl.AcquireWriterLock(); trwl.ReleaseWriterLock(); }); waitingWriter.Start(); ThreadTestHelpers.WaitForCondition(() => (waitingWriter.ThreadState & ThreadState.WaitSleepJoin) != 0); // Acquire a recursive read lock successfully while there is a waiting writer trwl.AcquireReaderLock(); // Releasing both read locks releases the waiting writer trwl.ReleaseLock(); waitForWaitingWriter(); trwl.Dispose(); }
public static void WaitingWritersTest() { var trwl = new TestReaderWriterLock(); trwl.AcquireReaderLock(); Action acquireReleaseWriterLock = () => { trwl.AcquireWriterLock(); trwl.ReleaseWriterLock(); }; Action waitForWaitingWriter1, waitForWaitingWriter2; Thread waitingWriter1 = ThreadTestHelpers.CreateGuardedThread(out waitForWaitingWriter1, acquireReleaseWriterLock); Thread waitingWriter2 = ThreadTestHelpers.CreateGuardedThread(out waitForWaitingWriter2, acquireReleaseWriterLock); waitingWriter1.IsBackground = true; waitingWriter2.IsBackground = true; waitingWriter1.Start(); waitingWriter2.Start(); ThreadTestHelpers.WaitForCondition(() => (waitingWriter1.ThreadState & ThreadState.WaitSleepJoin) != 0); ThreadTestHelpers.WaitForCondition(() => (waitingWriter2.ThreadState & ThreadState.WaitSleepJoin) != 0); // Releasing the read lock releases a waiting writer, that writer releases its write lock, in turn releasing the // other writer trwl.ReleaseReaderLock(); waitForWaitingWriter1(); waitForWaitingWriter2(); trwl.Dispose(); }
public static void ReadersWaitingOnWaitingUpgraderTest() { var trwl = new TestReaderWriterLock(); trwl.AcquireReaderLock(); var waitingUpgraderReady = new AutoResetEvent(false); var continueWaitingUpgrader = new AutoResetEvent(false); Action waitForWaitingUpgrader; Thread waitingUpgrader = ThreadTestHelpers.CreateGuardedThread(out waitForWaitingUpgrader, () => { trwl.AcquireReaderLock(); trwl.UpgradeToWriterLock(); waitingUpgraderReady.Set(); continueWaitingUpgrader.CheckedWait(); trwl.ReleaseWriterLock(); trwl.VerifyIsReaderLockHeld(false); trwl.VerifyIsWriterLockHeld(false); }); waitingUpgrader.IsBackground = true; waitingUpgrader.Start(); ThreadTestHelpers.WaitForCondition(() => (waitingUpgrader.ThreadState & ThreadState.WaitSleepJoin) != 0); Action acquireReleaseReaderLock = () => { trwl.AcquireReaderLock(); trwl.ReleaseReaderLock(); }; Action waitForWaitingReader1, waitForWaitingReader2; Thread waitingReader1 = ThreadTestHelpers.CreateGuardedThread(out waitForWaitingReader1, acquireReleaseReaderLock); Thread waitingReader2 = ThreadTestHelpers.CreateGuardedThread(out waitForWaitingReader2, acquireReleaseReaderLock); waitingReader1.IsBackground = true; waitingReader2.IsBackground = true; waitingReader1.Start(); waitingReader2.Start(); ThreadTestHelpers.WaitForCondition(() => (waitingReader1.ThreadState & ThreadState.WaitSleepJoin) != 0); ThreadTestHelpers.WaitForCondition(() => (waitingReader2.ThreadState & ThreadState.WaitSleepJoin) != 0); // Releasing the read lock releases the waiting upgrader trwl.ReleaseReaderLock(); waitingUpgraderReady.CheckedWait(); // Releasing the now-writer's write lock releases all waiting readers continueWaitingUpgrader.Set(); waitForWaitingUpgrader(); waitForWaitingReader1(); waitForWaitingReader2(); trwl.Dispose(); }
public static void TimersCreatedConcurrentlyOnDifferentThreadsAllFire() { int processorCount = Environment.ProcessorCount; int timerTickCount = 0; TimerCallback timerCallback = data => Interlocked.Increment(ref timerTickCount); var threadStarted = new AutoResetEvent(false); var createTimers = new ManualResetEvent(false); var timers = new Timer[processorCount]; Action <object> createTimerThreadStart = data => { int i = (int)data; var sw = new Stopwatch(); threadStarted.Set(); createTimers.WaitOne(); // Use the CPU a bit around creating the timer to try to have some of these threads run concurrently sw.Restart(); do { Thread.SpinWait(1000); } while (sw.ElapsedMilliseconds < 10); timers[i] = new Timer(timerCallback, null, 1, Timeout.Infinite); // Use the CPU a bit around creating the timer to try to have some of these threads run concurrently sw.Restart(); do { Thread.SpinWait(1000); } while (sw.ElapsedMilliseconds < 10); }; var waitsForThread = new Action[timers.Length]; for (int i = 0; i < timers.Length; ++i) { var t = ThreadTestHelpers.CreateGuardedThread(out waitsForThread[i], createTimerThreadStart); t.IsBackground = true; t.Start(i); threadStarted.CheckedWait(); } createTimers.Set(); ThreadTestHelpers.WaitForCondition(() => timerTickCount == timers.Length); foreach (var waitForThread in waitsForThread) { waitForThread(); } }
public static void WaitingUpgradersTest() { var trwl = new TestReaderWriterLock(); trwl.AcquireReaderLock(); var waitingUpgrader1AcquiredReadLock = new ManualResetEvent(false); Action waitForWaitingUpgrader1, waitForWaitingUpgrader2, waitForWaitingUpgrader3; Thread waitingUpgrader1 = ThreadTestHelpers.CreateGuardedThread(out waitForWaitingUpgrader1, () => { trwl.AcquireReaderLock(); waitingUpgrader1AcquiredReadLock.Set(); TestLockCookie tlc = trwl.UpgradeToWriterLock(); trwl.DowngradeFromWriterLock(tlc); trwl.ReleaseReaderLock(); }); Action upgradeDowngradeLock = () => { TestLockCookie tlc = trwl.UpgradeToWriterLock(); trwl.DowngradeFromWriterLock(tlc); }; Thread waitingUpgrader2 = ThreadTestHelpers.CreateGuardedThread(out waitForWaitingUpgrader2, upgradeDowngradeLock); Thread waitingUpgrader3 = ThreadTestHelpers.CreateGuardedThread(out waitForWaitingUpgrader3, upgradeDowngradeLock); waitingUpgrader1.IsBackground = true; waitingUpgrader2.IsBackground = true; waitingUpgrader1.Start(); waitingUpgrader1AcquiredReadLock.CheckedWait(); waitingUpgrader2.Start(); waitingUpgrader3.Start(); ThreadTestHelpers.WaitForCondition(() => (waitingUpgrader1.ThreadState & ThreadState.WaitSleepJoin) != 0); ThreadTestHelpers.WaitForCondition(() => (waitingUpgrader2.ThreadState & ThreadState.WaitSleepJoin) != 0); ThreadTestHelpers.WaitForCondition(() => (waitingUpgrader3.ThreadState & ThreadState.WaitSleepJoin) != 0); // Releasing the read lock releases a waiting upgrader, that writer downgrades its write lock, in turn releasing the // other upgrader, and so on trwl.ReleaseReaderLock(); waitForWaitingUpgrader1(); waitForWaitingUpgrader2(); waitForWaitingUpgrader3(); trwl.Dispose(); }
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) { WaitHandle[] waitHandles = null; if (waitType != WaitHandleWaitType.WaitOne) { 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 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)); 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)); } // 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>( () => m.WaitOne(ThreadTestHelpers.UnexpectedTimeoutMilliseconds)); } break; 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(); } }); }