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