public void TestInvaildExitReadLockUsage()
        {
            var l             = new AsyncReaderWriterLock();
            var readReleaser  = l.ReadLock();
            var readReleaser2 = l.ReadLock();

            readReleaser.Dispose();
            readReleaser2.Dispose();
            Assert.Throws <InvalidOperationException>(() => readReleaser.Dispose());
            Assert.Throws <InvalidOperationException>(() => readReleaser2.Dispose());
        }
        public void TestBlocking()
        {
            var l = new AsyncReaderWriterLock();

            for (var i = 0; i < 2; i++)
            {
                var readReleaser = l.ReadLock();
                Assert.That(l.CurrentReaders, Is.EqualTo(1));

                var readReleaserTask = Task.Run(() => l.ReadLock());
                AssertEqualValue(() => l.CurrentReaders, 2, readReleaserTask);
                AssertTaskCompleted(readReleaserTask);

                var writeReleaserTask = Task.Run(() => l.WriteLock());
                AssertEqualValue(() => l.AcquiredWriteLock, true, writeReleaserTask);
                AssertEqualValue(() => l.Writing, false, writeReleaserTask);
                Assert.That(writeReleaserTask.IsCompleted, Is.False);

                readReleaser.Dispose();
                Assert.That(l.CurrentReaders, Is.EqualTo(1));
                Assert.That(writeReleaserTask.IsCompleted, Is.False);

                readReleaserTask.Result.Dispose();
                Assert.That(l.CurrentReaders, Is.EqualTo(0));
                AssertEqualValue(() => l.Writing, true, writeReleaserTask);
                AssertTaskCompleted(writeReleaserTask);

                readReleaserTask = Task.Run(() => l.ReadLock());
                AssertEqualValue(() => l.ReadersWaiting, 1, readReleaserTask);
                Assert.That(readReleaserTask.IsCompleted, Is.False);

                var writeReleaserTask2 = Task.Run(() => l.WriteLock());
                AssertEqualValue(() => l.WritersWaiting, 1, writeReleaserTask2);
                Assert.That(writeReleaserTask2.IsCompleted, Is.False);

                writeReleaserTask.Result.Dispose();
                AssertEqualValue(() => l.WritersWaiting, 0, writeReleaserTask2);
                AssertEqualValue(() => l.Writing, true, writeReleaserTask2);
                Assert.That(readReleaserTask.IsCompleted, Is.False);
                AssertTaskCompleted(writeReleaserTask2);

                writeReleaserTask2.Result.Dispose();
                AssertEqualValue(() => l.Writing, false, writeReleaserTask2);
                AssertEqualValue(() => l.ReadersWaiting, 0, readReleaserTask);
                AssertEqualValue(() => l.CurrentReaders, 1, readReleaserTask);
                AssertTaskCompleted(readReleaserTask);

                readReleaserTask.Result.Dispose();
                Assert.That(l.ReadersWaiting, Is.EqualTo(0));
                Assert.That(l.WritersWaiting, Is.EqualTo(0));
                Assert.That(l.CurrentReaders, Is.EqualTo(0));
                Assert.That(l.Writing, Is.False);
            }
        }
        public virtual bool IsUpToDate(ISet <string> spaces, long timestamp /* H2.1 has Long here */)
        {
            if (spaces.Count == 0)
            {
                return(true);
            }

            using (_asyncReaderWriterLock.ReadLock())
            {
                var lastUpdates = _updateTimestamps.GetMany(spaces.ToArray <object>());
                return(lastUpdates.All(lastUpdate => !IsOutdated(lastUpdate as long?, timestamp)));
            }
        }
        public void TestMixingSyncAndAsync()
        {
            var l            = new AsyncReaderWriterLock();
            var readReleaser = l.ReadLock();

            Assert.That(l.CurrentReaders, Is.EqualTo(1));

            var readReleaserTask = l.ReadLockAsync();

            AssertEqualValue(() => l.CurrentReaders, 2, readReleaserTask);
            AssertTaskCompleted(readReleaserTask);

            readReleaser.Dispose();
            Assert.That(l.CurrentReaders, Is.EqualTo(1));

            readReleaserTask.Result.Dispose();
            Assert.That(l.CurrentReaders, Is.EqualTo(0));

            var writeReleaser = l.WriteLock();

            Assert.That(l.AcquiredWriteLock, Is.True);

            var writeReleaserTask = l.WriteLockAsync();

            AssertEqualValue(() => l.WritersWaiting, 1, writeReleaserTask);
            Assert.That(writeReleaserTask.IsCompleted, Is.False);

            readReleaserTask = Task.Run(() => l.ReadLock());
            AssertEqualValue(() => l.ReadersWaiting, 1, readReleaserTask);
            Assert.That(readReleaserTask.IsCompleted, Is.False);

            var readReleaserTask2 = l.ReadLockAsync();

            AssertEqualValue(() => l.ReadersWaiting, 2, readReleaserTask2);
            Assert.That(readReleaserTask2.IsCompleted, Is.False);

            writeReleaser.Dispose();
            AssertEqualValue(() => l.WritersWaiting, 0, writeReleaserTask);
            AssertEqualValue(() => l.Writing, true, writeReleaserTask);
            AssertTaskCompleted(writeReleaserTask);
            Assert.That(readReleaserTask.IsCompleted, Is.False);
            Assert.That(readReleaserTask2.IsCompleted, Is.False);

            writeReleaserTask.Result.Dispose();
            AssertEqualValue(() => l.CurrentReaders, 2, readReleaserTask);
            AssertEqualValue(() => l.ReadersWaiting, 0, readReleaserTask2);
            AssertTaskCompleted(readReleaserTask);
            AssertTaskCompleted(readReleaserTask2);
        }
        public void TestOperationAfterDispose()
        {
            var l = new AsyncReaderWriterLock();

            l.Dispose();

            Assert.Throws <ObjectDisposedException>(() => l.ReadLock());
            Assert.Throws <ObjectDisposedException>(() => l.WriteLock());
        }
        private static void Lock(
            AsyncReaderWriterLock readWriteLock,
            Random random,
            LockStatistics lockStatistics,
            System.Action checkLockAction,
            Func <bool> canContinue)
        {
            while (canContinue())
            {
                var isRead   = random.Next(100) < 80;
                var releaser = isRead ? readWriteLock.ReadLock() : readWriteLock.WriteLock();
                lock (readWriteLock)
                {
                    if (isRead)
                    {
                        lockStatistics.ReadLockCount++;
                    }
                    else
                    {
                        lockStatistics.WriteLockCount++;
                    }

                    checkLockAction();
                }

                Thread.Sleep(10);

                lock (readWriteLock)
                {
                    releaser.Dispose();
                    if (isRead)
                    {
                        lockStatistics.ReadLockCount--;
                    }
                    else
                    {
                        lockStatistics.WriteLockCount--;
                    }

                    checkLockAction();
                }
            }
        }
        public async Task TestConcurrentReadWriteAsync()
        {
            var l = new AsyncReaderWriterLock();

            for (var i = 0; i < 2; i++)
            {
                var writeReleaser = await(l.WriteLockAsync());
                Assert.That(l.Writing, Is.True);

                var secondWriteSemaphore = new SemaphoreSlim(0);
                var secondWriteReleaser  = default(AsyncReaderWriterLock.Releaser);
                var secondWriteThread    = new Thread(
                    () =>
                {
                    secondWriteSemaphore.Wait();
                    secondWriteReleaser = l.WriteLock();
                });
                secondWriteThread.Priority = ThreadPriority.Highest;
                secondWriteThread.Start();
                await(AssertEqualValueAsync(() => secondWriteThread.ThreadState == ThreadState.WaitSleepJoin, true));

                var secondReadThreads   = new Thread[20];
                var secondReadReleasers = new AsyncReaderWriterLock.Releaser[secondReadThreads.Length];
                var secondReadSemaphore = new SemaphoreSlim(0);
                for (var j = 0; j < secondReadReleasers.Length; j++)
                {
                    var index  = j;
                    var thread = new Thread(
                        () =>
                    {
                        secondReadSemaphore.Wait();
                        secondReadReleasers[index] = l.ReadLock();
                    });
                    thread.Priority      = ThreadPriority.Highest;
                    secondReadThreads[j] = thread;
                    thread.Start();
                }

                await(AssertEqualValueAsync(() => secondReadThreads.All(o => o.ThreadState == ThreadState.WaitSleepJoin), true));

                var firstReadReleaserTasks = new Task[30];
                var firstReadStopSemaphore = new SemaphoreSlim(0);
                for (var j = 0; j < firstReadReleaserTasks.Length; j++)
                {
                    firstReadReleaserTasks[j] = Task.Run(async() =>
                    {
                        var releaser = await(l.ReadLockAsync());
                        await(firstReadStopSemaphore.WaitAsync());
                        releaser.Dispose();
                    });
                }

                await(AssertEqualValueAsync(() => l.ReadersWaiting, firstReadReleaserTasks.Length, waitDelay: 60000));

                writeReleaser.Dispose();
                secondWriteSemaphore.Release();
                secondReadSemaphore.Release(secondReadThreads.Length);
                await(Task.Delay(1000));
                firstReadStopSemaphore.Release(firstReadReleaserTasks.Length);

                await(AssertEqualValueAsync(() => firstReadReleaserTasks.All(o => o.IsCompleted), true));
                Assert.That(l.ReadersWaiting, Is.EqualTo(secondReadThreads.Length));
                Assert.That(l.CurrentReaders, Is.EqualTo(0));
                await(AssertEqualValueAsync(() => secondWriteThread.IsAlive, false));
                await(AssertEqualValueAsync(() => secondReadThreads.All(o => o.IsAlive), true));

                secondWriteReleaser.Dispose();
                await(AssertEqualValueAsync(() => secondReadThreads.All(o => !o.IsAlive), true));

                Assert.That(l.ReadersWaiting, Is.EqualTo(0));
                Assert.That(l.CurrentReaders, Is.EqualTo(secondReadThreads.Length));

                foreach (var secondReadReleaser in secondReadReleasers)
                {
                    secondReadReleaser.Dispose();
                }

                Assert.That(l.ReadersWaiting, Is.EqualTo(0));
                Assert.That(l.CurrentReaders, Is.EqualTo(0));
            }
        }