Example #1
0
        /// <inheritdoc />
        public override int Next()
        {
            int intReturn;

            _objLock.SafeWait();
            try
            {
                intReturn = _objRandom.Next();
            }
            finally
            {
                _objLock.Release();
            }
            return(intReturn);
        }
Example #2
0
            private async ValueTask DisposeCoreAsync()
            {
                try
                {
                    if (_objCurrentSemaphore.CurrentCount == 0)
                    {
                        await _objNextSemaphore.WaitAsync();

                        try
                        {
                            _objCurrentSemaphore.Release();
                        }
                        finally
                        {
                            _objNextSemaphore.Release();
                        }
                    }
                }
                finally
                {
                    Utils.SemaphorePool.Return(ref _objNextSemaphore);
                }
            }
        /// <summary>
        /// Try to synchronously obtain a lock for reading.
        /// </summary>
        public void EnterReadLock(CancellationToken token = default)
        {
            if (_intDisposedStatus != 0)
            {
                throw new ObjectDisposedException(nameof(AsyncFriendlyReaderWriterLock));
            }

            token.ThrowIfCancellationRequested();
            // Only do the complicated steps if any write lock is currently being held, otherwise skip it and just process the read lock
            if (_objTopLevelWriterSemaphore.CurrentCount == 0)
            {
                // Temporarily acquiring a write lock just to mess with the read locks is a bottleneck, so don't do any such setting unless we need it
                DebuggableSemaphoreSlim objCurrentSemaphore = _objCurrentWriterSemaphore.Value?.Item2 ?? _objTopLevelWriterSemaphore;
                objCurrentSemaphore.SafeWait(token);
                try
                {
                    if (Interlocked.Increment(ref _intCountActiveReaders) == 1)
                    {
                        try
                        {
                            _objReaderSemaphore.SafeWait(token);
                        }
                        catch
                        {
                            Interlocked.Decrement(ref _intCountActiveReaders);
                            throw;
                        }
                    }
                }
                finally
                {
                    objCurrentSemaphore.Release();
                }
            }
            else if (Interlocked.Increment(ref _intCountActiveReaders) == 1)
            {
                try
                {
                    _objReaderSemaphore.SafeWait(token);
                }
                catch
                {
                    Interlocked.Decrement(ref _intCountActiveReaders);
                    throw;
                }
            }
        }
        /// <summary>
        /// Heavier read lock entrant, used if a write lock is already being held somewhere
        /// </summary>
        private async Task TakeReadLockCoreAsync(DebuggableSemaphoreSlim objCurrentSemaphore, CancellationToken token = default)
        {
            await objCurrentSemaphore.WaitAsync(token);

            try
            {
                if (Interlocked.Increment(ref _intCountActiveReaders) == 1)
                {
                    try
                    {
                        await _objReaderSemaphore.WaitAsync(token);
                    }
                    catch
                    {
                        Interlocked.Decrement(ref _intCountActiveReaders);
                        throw;
                    }
                }
            }
            finally
            {
                objCurrentSemaphore.Release();
            }
        }
        /// <summary>
        /// Try to synchronously obtain a lock for writing.
        /// The returned SafeSemaphoreWriterRelease must be stored for when the write lock is to be released.
        /// </summary>
        public IDisposable EnterWriteLock(CancellationToken token = default)
        {
            if (_intDisposedStatus != 0)
            {
                throw new ObjectDisposedException(nameof(AsyncFriendlyReaderWriterLock));
            }

            token.ThrowIfCancellationRequested();
            (DebuggableSemaphoreSlim objLastSemaphore, DebuggableSemaphoreSlim objCurrentSemaphore)
                = _objCurrentWriterSemaphore.Value
                  ?? new Tuple <DebuggableSemaphoreSlim, DebuggableSemaphoreSlim>(null, _objTopLevelWriterSemaphore);
            DebuggableSemaphoreSlim objNextSemaphore = Utils.SemaphorePool.Get();

            // Extremely hacky solution to buggy semaphore (re)cycling in AsyncLocal
            // TODO: Fix this properly. The problem is that after an AsyncLocal shallow-copy in a different context, the semaphores can get returned in the copy without altering the original AsyncLocal
            while (objNextSemaphore == objCurrentSemaphore || objNextSemaphore == objLastSemaphore)
            {
                objNextSemaphore = Utils.SemaphorePool.Get();
            }
            _objCurrentWriterSemaphore.Value = new Tuple <DebuggableSemaphoreSlim, DebuggableSemaphoreSlim>(objCurrentSemaphore, objNextSemaphore);
            SafeWriterSemaphoreRelease objRelease = new SafeWriterSemaphoreRelease(objLastSemaphore, objCurrentSemaphore, objNextSemaphore, this);

            try
            {
                objCurrentSemaphore.SafeWait(token);
            }
            catch
            {
                _objCurrentWriterSemaphore.Value = new Tuple <DebuggableSemaphoreSlim, DebuggableSemaphoreSlim>(objLastSemaphore, objCurrentSemaphore);
                Utils.SemaphorePool.Return(ref objNextSemaphore);
                throw;
            }
            try
            {
                if (Interlocked.Increment(ref _intCountActiveReaders) == 1)
                {
                    try
                    {
                        // Wait for the reader lock only if there have been no other write locks before us
                        if (_objTopLevelWriterSemaphore.CurrentCount != 0 || objCurrentSemaphore == _objTopLevelWriterSemaphore)
                        {
                            _objReaderSemaphore.SafeWait(token);
                        }
                    }
                    catch
                    {
                        Interlocked.Decrement(ref _intCountActiveReaders);
                        throw;
                    }
                }
            }
            catch
            {
                _objCurrentWriterSemaphore.Value = new Tuple <DebuggableSemaphoreSlim, DebuggableSemaphoreSlim>(objLastSemaphore, objCurrentSemaphore);
                try
                {
                    // ReSharper disable once MethodSupportsCancellation
                    objNextSemaphore.SafeWait();
                    try
                    {
                        objCurrentSemaphore.Release();
                    }
                    finally
                    {
                        objNextSemaphore.Release();
                    }
                }
                finally
                {
                    Utils.SemaphorePool.Return(ref objNextSemaphore);
                }
                throw;
            }
            return(objRelease);
        }