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