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 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 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 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 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 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 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 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 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 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 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 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 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(); }