/// <inheritdoc /> public override int Next() { int intReturn; _objLock.SafeWait(); try { intReturn = _objRandom.Next(); } finally { _objLock.Release(); } return(intReturn); }
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); }