public static void InvalidLockCookieTest() { // Invalid lock cookie created by using one up with Upgrade/Downgrade var trwl = new TestReaderWriterLock(); TestLockCookie tlc = trwl.UpgradeToWriterLock(); trwl.DowngradeFromWriterLock(tlc); trwl.AcquireWriterLock(); trwl.DowngradeFromWriterLock(tlc, InvalidLockCookieExceptionHResult); trwl.ReleaseWriterLock(); trwl.RestoreLock(tlc, InvalidLockCookieExceptionHResult); // Invalid lock cookie created by using one up with Release/Restore tlc = trwl.ReleaseLock(); trwl.RestoreLock(tlc); trwl.AcquireWriterLock(); trwl.DowngradeFromWriterLock(tlc, InvalidLockCookieExceptionHResult); trwl.ReleaseWriterLock(); trwl.RestoreLock(tlc, InvalidLockCookieExceptionHResult); // Lock cookie owned by another thread ThreadTestHelpers.RunTestInBackgroundThread(() => { TestLockCookie tlc2 = trwl.UpgradeToWriterLock(); tlc = tlc2.Clone(); trwl.DowngradeFromWriterLock(tlc2); }); trwl.AcquireWriterLock(); trwl.DowngradeFromWriterLock(tlc, InvalidLockCookieExceptionHResult); trwl.ReleaseWriterLock(); trwl.RestoreLock(tlc, InvalidLockCookieExceptionHResult); trwl.Dispose(); }
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 ShouldNotBeOwnerForRestoreLockTest() { var trwl = new TestReaderWriterLock(); trwl.AcquireReaderLock(); TestLockCookie restoreReadLockTlc = trwl.ReleaseLock(); trwl.AcquireWriterLock(); TestLockCookie restoreWriteLockTlc = trwl.ReleaseLock(); Action verifyCannotRestore = () => { Assert.Throws <SynchronizationLockException>(() => trwl.RestoreLock(restoreReadLockTlc)); Assert.Throws <SynchronizationLockException>(() => trwl.RestoreLock(restoreWriteLockTlc)); }; trwl.AcquireReaderLock(); verifyCannotRestore(); trwl.ReleaseReaderLock(); trwl.AcquireWriterLock(); verifyCannotRestore(); trwl.ReleaseWriterLock(); 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 DowngradeQuirks_ChangedInDotNetCore() { var trwl = new TestReaderWriterLock(); TestLockCookie tlc; // Downgrade quirk when downgrading from a state where multiple recursive write locks are held, when the lock cookie // was obtained from a state where: // - No lock was held // - When any number of recursive write locks are held // The expectation in both cases is that a downgrade respects the lock cookie and restores the write lock recursion // level to the point indicated by the lock cookie. { // Lock cookie obtained when no lock is held tlc = trwl.UpgradeToWriterLock(); trwl.AcquireWriterLock(); trwl.DowngradeFromWriterLock(tlc); trwl.VerifyIsReaderLockHeld(false); trwl.VerifyIsWriterLockHeld(false); // Lock cookie obtained when write locks are held trwl.AcquireWriterLock(); tlc = trwl.UpgradeToWriterLock(); trwl.AcquireWriterLock(); trwl.DowngradeFromWriterLock(tlc); trwl.VerifyIsReaderLockHeld(false); trwl.VerifyIsWriterLockHeld(true); trwl.ReleaseWriterLock(); trwl.VerifyIsWriterLockHeld(false); } // Cannot downgrade to a recursive write lock level greater than or equal to the current trwl.AcquireWriterLock(); trwl.AcquireWriterLock(); tlc = trwl.UpgradeToWriterLock(); trwl.ReleaseWriterLock(); trwl.DowngradeFromWriterLock(tlc, InvalidLockCookieExceptionHResult); trwl.ReleaseWriterLock(); trwl.DowngradeFromWriterLock(tlc, InvalidLockCookieExceptionHResult); trwl.ReleaseWriterLock(); trwl.VerifyIsReaderLockHeld(false); trwl.VerifyIsWriterLockHeld(false); 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 NotOwnerTest() { var trwl = new TestReaderWriterLock(); trwl.ReleaseReaderLock(NotOwnerExceptionHResult); trwl.ReleaseWriterLock(NotOwnerExceptionHResult); trwl.DowngradeFromWriterLock(new TestLockCookie(), NotOwnerExceptionHResult); { trwl.AcquireReaderLock(); trwl.ReleaseWriterLock(NotOwnerExceptionHResult); TestLockCookie tlc = trwl.UpgradeToWriterLock(); TestLockCookie tlc2 = tlc.Clone(); trwl.DowngradeFromWriterLock(tlc); // tlc is invalid, tlc2 is valid trwl.DowngradeFromWriterLock(tlc2, NotOwnerExceptionHResult); trwl.ReleaseReaderLock(); } trwl.Dispose(); }
public static void BasicLockTest() { var trwl = new TestReaderWriterLock(); TestLockCookie tlc; var threadReady = new AutoResetEvent(false); var continueThread = new AutoResetEvent(false); Action checkForThreadErrors, waitForThread; Thread t = ThreadTestHelpers.CreateGuardedThread(out checkForThreadErrors, out waitForThread, () => { TestLockCookie tlc2; Action switchToMainThread = () => { threadReady.Set(); continueThread.CheckedWait(); }; switchToMainThread(); // Multiple readers from multiple threads { trwl.AcquireReaderLock(); trwl.AcquireReaderLock(); switchToMainThread(); trwl.ReleaseReaderLock(); switchToMainThread(); trwl.ReleaseReaderLock(); switchToMainThread(); trwl.AcquireReaderLock(); trwl.ReleaseReaderLock(); switchToMainThread(); } // What can be done when a read lock is held { trwl.AcquireReaderLock(); switchToMainThread(); // Any thread can take a read lock trwl.AcquireReaderLock(); trwl.ReleaseReaderLock(); switchToMainThread(); // No thread can take a write lock trwl.AcquireWriterLock(TimeoutExceptionHResult); trwl.AcquireReaderLock(); trwl.UpgradeToWriterLock(TimeoutExceptionHResult); trwl.ReleaseReaderLock(); switchToMainThread(); trwl.ReleaseReaderLock(); switchToMainThread(); // Owning thread releases read lock when upgrading trwl.AcquireWriterLock(); trwl.ReleaseWriterLock(); switchToMainThread(); // Owning thread cannot upgrade if there are other readers or writers trwl.AcquireReaderLock(); switchToMainThread(); trwl.ReleaseReaderLock(); trwl.AcquireWriterLock(); switchToMainThread(); trwl.ReleaseWriterLock(); switchToMainThread(); } // What can be done when a write lock is held { // Write lock acquired through AcquireWriteLock is exclusive trwl.AcquireWriterLock(); switchToMainThread(); trwl.ReleaseWriterLock(); switchToMainThread(); // Write lock acquired through upgrading is also exclusive trwl.AcquireReaderLock(); tlc2 = trwl.UpgradeToWriterLock(); switchToMainThread(); trwl.DowngradeFromWriterLock(tlc2); trwl.ReleaseReaderLock(); switchToMainThread(); // Write lock acquired through restore is also exclusive trwl.AcquireWriterLock(); tlc = trwl.ReleaseLock(); trwl.RestoreLock(tlc); switchToMainThread(); trwl.ReleaseWriterLock(); switchToMainThread(); } }); t.IsBackground = true; t.Start(); Action beginSwitchToBackgroundThread = () => continueThread.Set(); Action endSwitchToBackgroundThread = () => { try { threadReady.CheckedWait(); } finally { checkForThreadErrors(); } }; Action switchToBackgroundThread = () => { beginSwitchToBackgroundThread(); endSwitchToBackgroundThread(); }; endSwitchToBackgroundThread(); // Multiple readers from muliple threads { trwl.AcquireReaderLock(); trwl.AcquireReaderLock(); switchToBackgroundThread(); // AcquireReaderLock * 2 trwl.ReleaseReaderLock(); switchToBackgroundThread(); // ReleaseReaderLock // Release/restore the read lock while a read lock is held by another thread tlc = trwl.ReleaseLock(); trwl.RestoreLock(tlc); switchToBackgroundThread(); // ReleaseReaderLock // Downgrade to read lock allows another thread to acquire read lock tlc = trwl.UpgradeToWriterLock(); trwl.DowngradeFromWriterLock(tlc); switchToBackgroundThread(); // AcquireReaderLock, ReleaseReaderLock trwl.ReleaseReaderLock(); } // What can be done when a read lock is held { switchToBackgroundThread(); // AcquireReaderLock { // Any thread can take a read lock trwl.AcquireReaderLock(); trwl.ReleaseReaderLock(); switchToBackgroundThread(); // same as above // No thread can take a write lock trwl.AcquireWriterLock(TimeoutExceptionHResult); trwl.AcquireReaderLock(); trwl.UpgradeToWriterLock(TimeoutExceptionHResult); switchToBackgroundThread(); // same as above trwl.ReleaseReaderLock(); // Other threads cannot upgrade to a write lock, but the owning thread can trwl.AcquireReaderLock(); trwl.UpgradeToWriterLock(TimeoutExceptionHResult); trwl.ReleaseReaderLock(); } switchToBackgroundThread(); // ReleaseReaderLock // Owning thread releases read lock when upgrading trwl.AcquireReaderLock(); beginSwitchToBackgroundThread(); // AcquireWriterLock: background thread gets blocked trwl.UpgradeToWriterLock(); // unblocks background thread: ReleaseWriterLock trwl.ReleaseWriterLock(); endSwitchToBackgroundThread(); // Owning thread cannot upgrade if there are other readers or writers trwl.AcquireReaderLock(); switchToBackgroundThread(); // AcquireReaderLock trwl.UpgradeToWriterLock(TimeoutExceptionHResult); trwl.ReleaseReaderLock(); switchToBackgroundThread(); // ReleaseReaderLock, AcquireWriterLock trwl.UpgradeToWriterLock(TimeoutExceptionHResult); switchToBackgroundThread(); // ReleaseWriterLock } // What can be done when a write lock is held { trwl.AcquireWriterLock(); TestLockCookie restoreToWriteLockTlc = trwl.ReleaseLock(); Action verifyCannotAcquireLock = () => { trwl.AcquireReaderLock(TimeoutExceptionHResult); trwl.AcquireWriterLock(TimeoutExceptionHResult); trwl.UpgradeToWriterLock(TimeoutExceptionHResult); }; Action verifyCanAcquireLock = () => { trwl.AcquireReaderLock(); tlc = trwl.UpgradeToWriterLock(); trwl.DowngradeFromWriterLock(tlc); trwl.ReleaseReaderLock(); trwl.AcquireWriterLock(); trwl.ReleaseWriterLock(); trwl.RestoreLock(restoreToWriteLockTlc.Clone()); trwl.ReleaseWriterLock(); }; // Write lock acquired through AcquireWriteLock is exclusive switchToBackgroundThread(); // AcquireWriterLock verifyCannotAcquireLock(); switchToBackgroundThread(); // ReleaseWriterLock verifyCanAcquireLock(); // Write lock acquired through upgrading is also exclusive switchToBackgroundThread(); // AcquireReaderLock, UpgradeToWriterLock verifyCannotAcquireLock(); switchToBackgroundThread(); // DowngradeFromWriterLock, ReleaseReaderLock verifyCanAcquireLock(); // Write lock acquired through restore is also exclusive switchToBackgroundThread(); // AcquireWriterLock, ReleaseLock, RestoreLock verifyCannotAcquireLock(); switchToBackgroundThread(); // ReleaseWriterLock verifyCanAcquireLock(); } beginSwitchToBackgroundThread(); waitForThread(); trwl.Dispose(); }
public static void SingleThreadLockOwnerMiscellaneousTest() { var trwl = new TestReaderWriterLock(); TestLockCookie tlc, tlc2; // Read lock owner can upgrade to a write lock trwl.AcquireReaderLock(); tlc = trwl.UpgradeToWriterLock(); trwl.DowngradeFromWriterLock(tlc); trwl.ReleaseReaderLock(); // Can acquire and release a recursive write lock in multiple ways trwl.AcquireWriterLock(); trwl.AcquireWriterLock(); trwl.ReleaseWriterLock(); trwl.AcquireReaderLock(); trwl.ReleaseReaderLock(); trwl.UpgradeToWriterLock(); trwl.ReleaseWriterLock(); trwl.ReleaseWriterLock(); // Typical upgrade with single read lock trwl.AcquireReaderLock(); tlc = trwl.UpgradeToWriterLock(); trwl.DowngradeFromWriterLock(tlc); trwl.ReleaseReaderLock(); // Write lock can be taken with UpgradeToWriterLock when no read lock is held, and with that lock cookie, // DowngradeFromWriterLock does not acquire a read lock tlc = trwl.UpgradeToWriterLock(); tlc2 = trwl.UpgradeToWriterLock(); trwl.DowngradeFromWriterLock(tlc2); trwl.DowngradeFromWriterLock(tlc); // Upgrading from recursive read lock downgrades back to recursive read lock trwl.AcquireReaderLock(); trwl.AcquireReaderLock(); tlc = trwl.UpgradeToWriterLock(); trwl.DowngradeFromWriterLock(tlc); trwl.ReleaseReaderLock(); trwl.ReleaseReaderLock(); // Can downgrade from any write lock level, and to any read lock level with lock cookie from ReleaseLock trwl.AcquireReaderLock(); trwl.AcquireReaderLock(); tlc = trwl.ReleaseLock(); trwl.AcquireWriterLock(); trwl.AcquireWriterLock(); trwl.DowngradeFromWriterLock(tlc); trwl.ReleaseReaderLock(); trwl.ReleaseReaderLock(); // Typical release/restore { trwl.AcquireReaderLock(); trwl.AcquireReaderLock(); tlc = trwl.ReleaseLock(); trwl.RestoreLock(tlc); trwl.ReleaseReaderLock(); trwl.ReleaseReaderLock(); trwl.AcquireWriterLock(); trwl.AcquireWriterLock(); tlc = trwl.ReleaseLock(); trwl.RestoreLock(tlc); trwl.ReleaseWriterLock(); trwl.ReleaseWriterLock(); } // Can restore to any read lock level with lock cookie from UpgradeToWriterLock trwl.AcquireReaderLock(); trwl.AcquireReaderLock(); tlc = trwl.UpgradeToWriterLock(); trwl.ReleaseWriterLock(); trwl.RestoreLock(tlc); trwl.ReleaseReaderLock(); trwl.ReleaseReaderLock(); trwl.Dispose(); }
public static void ShouldNotBeOwnerForRestoreLockTest() { var trwl = new TestReaderWriterLock(); trwl.AcquireReaderLock(); TestLockCookie restoreReadLockTlc = trwl.ReleaseLock(); trwl.AcquireWriterLock(); TestLockCookie restoreWriteLockTlc = trwl.ReleaseLock(); Action verifyCannotRestore = () => { Assert.Throws<SynchronizationLockException>(() => trwl.RestoreLock(restoreReadLockTlc)); Assert.Throws<SynchronizationLockException>(() => trwl.RestoreLock(restoreWriteLockTlc)); }; trwl.AcquireReaderLock(); verifyCannotRestore(); trwl.ReleaseReaderLock(); trwl.AcquireWriterLock(); verifyCannotRestore(); trwl.ReleaseWriterLock(); trwl.Dispose(); }
public static void ReadersWaitingOnWaitingWriterTest() { var trwl = new TestReaderWriterLock(); trwl.AcquireReaderLock(); var waitingWriterReady = new AutoResetEvent(false); var continueWaitingWriter = new AutoResetEvent(false); Action waitForWaitingWriter; Thread waitingWriter = ThreadTestHelpers.CreateGuardedThread(out waitForWaitingWriter, () => { trwl.AcquireWriterLock(); waitingWriterReady.Set(); continueWaitingWriter.CheckedWait(); trwl.ReleaseWriterLock(); }); waitingWriter.IsBackground = true; waitingWriter.Start(); ThreadTestHelpers.WaitForCondition(() => (waitingWriter.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 writer trwl.ReleaseReaderLock(); waitingWriterReady.CheckedWait(); // Releasing the now-writer's write lock releases all waiting readers continueWaitingWriter.Set(); waitForWaitingWriter(); waitForWaitingReader1(); waitForWaitingReader2(); trwl.Dispose(); }