File: ReadWriterLocker.cs
Web Access
Project: src\src\Razor\src\Shared\Microsoft.AspNetCore.Razor.Utilities.Shared\Microsoft.AspNetCore.Razor.Utilities.Shared.csproj (Microsoft.AspNetCore.Razor.Utilities.Shared)
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
 
using System;
using System.Diagnostics;
using System.Threading;
 
namespace Microsoft.AspNetCore.Razor;
 
internal class ReadWriterLocker
{
    // Specify recursion is supported, since an item with an upgradeable lock can still
    // get another read lock on the same thread
    private readonly ReaderWriterLockSlim _lock = new(LockRecursionPolicy.SupportsRecursion);
 
    public bool IsReadLockHeld => _lock.IsReadLockHeld;
    public bool IsUpgradeableReadLockHeld => _lock.IsUpgradeableReadLockHeld;
    public bool IsWriteLockHeld => _lock.IsWriteLockHeld;
 
    public ReadOnlyLock EnterReadLock() => new ReadOnlyLock(_lock);
    public WriteOnlyLock EnterWriteLock() => new WriteOnlyLock(_lock);
    public UpgradeableReadLock EnterUpgradeableReadLock() => new UpgradeableReadLock(_lock);
 
    private static readonly TimeSpan s_maxTimeout = TimeSpan.FromMilliseconds(int.MaxValue);
    private static readonly TimeSpan s_timeout =
#if DEBUG
        s_maxTimeout;
#else
        TimeSpan.FromSeconds(30);
#endif
 
    private static TimeSpan GetTimeout()
    {
        if (Debugger.IsAttached)
        {
            return s_maxTimeout;
        }
 
        return s_timeout;
    }
 
    public ref struct ReadOnlyLock
    {
        private readonly ReaderWriterLockSlim _rwLock;
        private bool _disposed;
 
        public ReadOnlyLock(ReaderWriterLockSlim rwLock)
        {
            _rwLock = rwLock;
            if (!_rwLock.TryEnterReadLock(GetTimeout()))
            {
                throw new InvalidOperationException("Failed getting a read lock");
            }
        }
 
        public void Dispose()
        {
            if (_disposed)
            {
                return;
            }
 
            _disposed = true;
            _rwLock.ExitReadLock();
        }
    }
 
    public ref struct WriteOnlyLock
    {
        private readonly ReaderWriterLockSlim _rwLock;
        private bool _disposed;
 
        public WriteOnlyLock(ReaderWriterLockSlim rwLock)
        {
            _rwLock = rwLock;
            if (!_rwLock.TryEnterWriteLock(GetTimeout()))
            {
                throw new InvalidOperationException("Failed getting a write lock");
            }
        }
 
        public void Dispose()
        {
            if (_disposed)
            {
                return;
            }
 
            _disposed = true;
            _rwLock.ExitWriteLock();
        }
    }
 
    public ref struct UpgradeableReadLock
    {
        private readonly ReaderWriterLockSlim _rwLock;
        private bool _disposed;
 
        public UpgradeableReadLock(ReaderWriterLockSlim rwLock)
        {
            _rwLock = rwLock;
            if (!_rwLock.TryEnterUpgradeableReadLock(GetTimeout()))
            {
                throw new InvalidOperationException("Failed getting an upgradeable read lock");
            }
        }
 
        public void Dispose()
        {
            if (_disposed)
            {
                return;
            }
 
            _disposed = true;
            _rwLock.ExitUpgradeableReadLock();
        }
 
        public WriteOnlyLock EnterWriteLock()
        {
            return new WriteOnlyLock(_rwLock);
        }
    }
}