public async Task WriteTakesPriorityOverRead1()
        {
            var myLock = new AsyncReaderWriterLockSlim();

            // Check that no new read lock can be entered when another thread/task wants
            // to enter the write lock while at least one read lock is active.
            myLock.EnterReadLock();

            using (var cts = new CancellationTokenSource())
            {
                var enterWriteLockTask = myLock.EnterWriteLockAsync(cts.Token);

                Assert.IsFalse(myLock.TryEnterReadLock(0));

                cts.Cancel();
                try
                {
                    await enterWriteLockTask;
                    Assert.Fail();
                }
                catch (OperationCanceledException)
                {
                }
            }

            // Verify that now a new read lock can be entered.
            Assert.IsTrue(await myLock.TryEnterReadLockAsync(0));
        }
        public void BasicTest()
        {
            var l   = new AsyncReaderWriterLockSlim();
            var vt1 = l.AcquireReaderLockAsync();
            var vt2 = l.AcquireReaderLockAsync();
            var vt3 = l.AcquireWriterLockAsync();

            Assert.True(vt1.IsCompletedSuccessfully);
            Assert.True(vt2.IsCompletedSuccessfully);
            Assert.False(vt3.IsCompleted);

            vt1.GetAwaiter().GetResult();
            vt2.GetAwaiter().GetResult();
            l.ReleaseReaderLock();
            l.ReleaseReaderLock();


            Assert.True(vt3.IsCompletedSuccessfully);

            vt1 = l.AcquireReaderLockAsync();
            vt2 = l.AcquireReaderLockAsync();
            Assert.False(vt1.IsCompleted);
            Assert.False(vt2.IsCompleted);

            vt3.GetAwaiter().GetResult();
            l.ReleaseWriterLock();

            Assert.True(vt1.IsCompletedSuccessfully);
            Assert.True(vt2.IsCompletedSuccessfully);
        }
        public void ThrowsOnIncorrectWriteLockRelease()
        {
            var myLock = new AsyncReaderWriterLockSlim();

            myLock.EnterWriteLock();

            myLock.ExitWriteLock();
            myLock.ExitWriteLock(); // should throw
        }
        public void OverflowReadersTest()
        {
            var l = new AsyncReaderWriterLockSlim();

            for (int i = 0; i < int.MaxValue - 1; i++)
            {
                var vt = l.AcquireReaderLockAsync();
                Assert.True(vt.IsCompletedSuccessfully);
            }

            Assert.Throws <InvalidOperationException>(() => l.AcquireReaderLockAsync());
        }
        public async Task MixedSyncAndAsync()
        {
            var myLock = new AsyncReaderWriterLockSlim();

            myLock.EnterReadLock();
            await myLock.EnterReadLockAsync();

            myLock.ExitReadLock();
            myLock.ExitReadLock();

            myLock.EnterWriteLock();
            Assert.IsFalse(await myLock.TryEnterWriteLockAsync(10));
            myLock.ExitWriteLock();
        }
        public void CanEnterLocksSync()
        {
            var myLock = new AsyncReaderWriterLockSlim();

            myLock.EnterReadLock();
            Assert.IsTrue(myLock.TryEnterReadLock(0));
            Assert.IsFalse(myLock.TryEnterWriteLock(0));
            myLock.ExitReadLock();
            myLock.ExitReadLock();

            myLock.EnterWriteLock();
            Assert.IsFalse(myLock.TryEnterReadLock(0));
            Assert.IsFalse(myLock.TryEnterWriteLock(0));
            myLock.ExitWriteLock();
        }
        public void ThrowsOperationCanceledException()
        {
            var myLock = new AsyncReaderWriterLockSlim();

            try
            {
                // Should throw without entering the lock.
                myLock.EnterReadLock(new CancellationToken(true));
            }
            catch (OperationCanceledException)
            {
                // Check that the read lock actually was not entered.
                Assert.IsTrue(myLock.TryEnterWriteLock(0));

                throw;
            }
        }
        public async Task CanEnterLocksAync()
        {
            var myLock = new AsyncReaderWriterLockSlim();

            await myLock.EnterReadLockAsync();

            Assert.IsTrue(await myLock.TryEnterReadLockAsync(0));
            Assert.IsFalse(await myLock.TryEnterWriteLockAsync(0));
            myLock.ExitReadLock();
            myLock.ExitReadLock();

            await myLock.EnterWriteLockAsync();

            Assert.IsFalse(await myLock.TryEnterReadLockAsync(0));
            Assert.IsFalse(await myLock.TryEnterWriteLockAsync(0));
            myLock.ExitWriteLock();
        }
        public void MultipleThreadsAndTasks()
        {
            var myLock           = new AsyncReaderWriterLockSlim();
            int readLocksEntered = 0;

            var threads = new Thread[5];
            var tasks   = new Task[5];

            myLock.EnterWriteLock();
            for (int i = 0; i < threads.Length; i++)
            {
                (threads[i] = new Thread(() =>
                {
                    myLock.EnterReadLock();
                    Interlocked.Increment(ref readLocksEntered);
                })).Start();
            }
            for (int i = 0; i < tasks.Length; i++)
            {
                tasks[i] = Task.Run(async() =>
                {
                    await myLock.EnterReadLockAsync();
                    Interlocked.Increment(ref readLocksEntered);
                });
            }

            // Wait a bit, then release the read lock, so that the other thread can enter
            // the write lock.
            Thread.Sleep(200);
            Assert.AreEqual(0, Volatile.Read(ref readLocksEntered));
            myLock.ExitWriteLock();

            // Wait for the tasks and threads.
            foreach (var thread in threads)
            {
                thread.Join();
            }
            foreach (var task in tasks)
            {
                task.GetAwaiter().GetResult();
            }

            Assert.AreEqual(threads.Length + tasks.Length, Volatile.Read(ref readLocksEntered));
        }
        public void DowngradeLockAllowsReadLock()
        {
            var myLock = new AsyncReaderWriterLockSlim();

            myLock.EnterWriteLock();

            Assert.IsFalse(myLock.TryEnterWriteLock(0));
            Assert.IsFalse(myLock.TryEnterReadLock(0));

            // After downgrading the lock and after the try to get the write lock is canceled,
            // it should be possible to enter another read lock.
            myLock.DowngradeWriteLockToReadLock();

            Assert.IsFalse(myLock.TryEnterWriteLock(0));
            Assert.IsTrue(myLock.TryEnterReadLock(0));

            myLock.ExitReadLock();
            myLock.ExitReadLock();
        }
        public async Task ReleasingOneOfTwoReadLocksDoesNotReleaseWaitingWriteAndReadLocks()
        {
            var myLock = new AsyncReaderWriterLockSlim();

            await myLock.EnterReadLockAsync();

            await myLock.EnterReadLockAsync();

            var writeLockTask = myLock.TryEnterWriteLockAsync(500);
            var readLockTask  = myLock.TryEnterReadLockAsync(300);

            // When releasing one of the two read locks, the waiting write lock should not
            // be released, and also the waiting read lock should not be released because of
            // the waiting write lock.
            myLock.ExitReadLock();

            Assert.IsFalse(await readLockTask);
            Assert.IsFalse(await writeLockTask);
        }
        public void ThrowsOperationCanceledExceptionAfterWait()
        {
            var myLock = new AsyncReaderWriterLockSlim();

            myLock.EnterReadLock();

            try
            {
                using (var cts = new CancellationTokenSource(100))
                {
                    myLock.EnterWriteLock(cts.Token);
                }
            }
            catch (OperationCanceledException)
            {
                // Check that we can now enter another read lock.
                Assert.IsTrue(myLock.TryEnterReadLock(0));

                throw;
            }
        }
        public void WaitingReaderIsReleasedAfterWaitingWriterCanceled()
        {
            var myLock = new AsyncReaderWriterLockSlim();

            // Thread A enters a read lock.
            myLock.EnterReadLock();

            // Thread B wants to enter a write lock within 2 seconds. Because Thread A holds
            // a read lock, Thread B will not get the write lock.
            bool threadBResult = true;
            var  threadB       = new Thread(() =>
            {
                bool result = myLock.TryEnterWriteLock(600);
                Volatile.Write(ref threadBResult, result);
            });

            threadB.Start();

            // Wait a bit before starting the next thread, to ensure Thread B is already
            // in the TryEnter...() call.
            Thread.Sleep(200);

            // Thread C wants to enter a read lock. It should get the lock after
            // 2 seconds because Thread B cancels its try to get the write lock after
            // that time.
            var threadC = new Thread(() =>
            {
                myLock.EnterReadLock();
            });

            threadC.Start();

            threadB.Join();
            threadC.Join();

            Assert.IsFalse(Volatile.Read(ref threadBResult));

            myLock.EnterReadLock();
        }
        public void MultipleThreads()
        {
            var myLock = new AsyncReaderWriterLockSlim();

            myLock.EnterReadLock();
            bool enteredWriteLock = false;
            var  t1 = new Thread(() =>
            {
                myLock.EnterWriteLock();
                Volatile.Write(ref enteredWriteLock, true);
                myLock.ExitWriteLock();
            });

            t1.Start();

            // Wait a bit, then release the read lock, so that the other thread can enter
            // the write lock.
            Thread.Sleep(200);
            Assert.IsFalse(Volatile.Read(ref enteredWriteLock));
            myLock.ExitReadLock();

            t1.Join();
            Assert.IsTrue(Volatile.Read(ref enteredWriteLock));
        }
        public async Task WriteTakesPriorityOverRead2()
        {
            var myLock = new AsyncReaderWriterLockSlim();

            // Check that when at least one thread/task wants to enter the read lock and at least
            // one thread/task wants to enter the write lock while another write lock is active,
            // writers always get precedence over readers when the existing write lock is released.
            for (int i = 0; i < 1000; i++)
            {
                myLock.EnterWriteLock();

                var readLockTask  = myLock.EnterReadLockAsync();
                var writeLockTask = myLock.EnterWriteLockAsync();

                // Release the current write lock. Now the writeLockTask should complete.
                myLock.ExitWriteLock();
                await writeLockTask;

                // After releasing the second write lock, the readLockTask should complete.
                myLock.ExitWriteLock();
                await readLockTask;
                myLock.ExitReadLock();
            }
        }
        public void LoadTest()
        {
            var myLock = new AsyncReaderWriterLockSlim();

            object lockCountSyncRoot = new object();
            int    readLockCount = 0, writeLockCount = 0;

            bool incorrectLockCount = false;

            void checkLockCount()
            {
                Debug.WriteLine($"ReadLocks = {readLockCount}, WriteLocks = {writeLockCount}");

                bool countIsCorrect = readLockCount == 0 && writeLockCount == 0 ||
                                      readLockCount > 0 && writeLockCount == 0 ||
                                      readLockCount == 0 && writeLockCount == 1;

                if (!countIsCorrect)
                {
                    Volatile.Write(ref incorrectLockCount, true);
                }
            }

            bool cancel = false;

            var threads = new Thread[20];
            var tasks   = new Task[20];

            var masterRandom = new Random();

            for (int i = 0; i < threads.Length; i++)
            {
                var random = new Random(masterRandom.Next());
                (threads[i] = new Thread(() =>
                {
                    bool isRead = random.Next(100) < 70;
                    if (isRead)
                    {
                        myLock.EnterReadLock();
                    }
                    else
                    {
                        myLock.EnterWriteLock();
                    }

                    lock (lockCountSyncRoot)
                    {
                        if (isRead)
                        {
                            readLockCount++;
                        }
                        else
                        {
                            writeLockCount++;
                        }
                    }

                    // Simulate work.
                    Thread.Sleep(10);

                    lock (lockCountSyncRoot)
                    {
                        if (isRead)
                        {
                            myLock.ExitReadLock();
                            readLockCount--;
                        }
                        else
                        {
                            myLock.ExitWriteLock();
                            writeLockCount--;
                        }
                    }
                })).Start();
            }
            for (int i = 0; i < tasks.Length; i++)
            {
                var random = new Random(masterRandom.Next());
                tasks[i] = Task.Run(async() =>
                {
                    while (!Volatile.Read(ref cancel))
                    {
                        bool isRead = random.Next(10) < 7;
                        if (isRead)
                        {
                            await myLock.EnterReadLockAsync();
                        }
                        else
                        {
                            await myLock.EnterWriteLockAsync();
                        }

                        lock (lockCountSyncRoot)
                        {
                            if (isRead)
                            {
                                readLockCount++;
                            }
                            else
                            {
                                writeLockCount++;
                            }

                            checkLockCount();
                        }

                        // Simulate work.
                        await Task.Delay(10);

                        lock (lockCountSyncRoot)
                        {
                            if (isRead)
                            {
                                myLock.ExitReadLock();
                                readLockCount--;
                            }
                            else
                            {
                                myLock.ExitWriteLock();
                                writeLockCount--;
                            }

                            checkLockCount();
                        }
                    }
                });
            }

            // Run for 5 seconds, then stop the tasks and threads.
            Thread.Sleep(5000);

            Volatile.Write(ref cancel, true);
            foreach (var thread in threads)
            {
                thread.Join();
            }
            foreach (var task in tasks)
            {
                task.GetAwaiter().GetResult();
            }

            Assert.IsFalse(incorrectLockCount);
        }