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