public void Dispose() { lock (SyncObject) { if (_disposed) { return; } _disposed = true; CreditWaiter waiter = _waitersTail; if (waiter != null) { do { CreditWaiter next = waiter.Next; waiter.Next = null; waiter.Dispose(); waiter = next; }while (waiter != _waitersTail); _waitersTail = null; } } }
public ValueTask <int> RequestCreditAsync(int amount, CancellationToken cancellationToken) { lock (SyncObject) { if (_disposed) { throw new ObjectDisposedException($"{nameof(CreditManager)}:{_owner.GetType().Name}:{_name}"); } // If we can satisfy the request with credit already available, do so synchronously. if (_current > 0) { Debug.Assert(_waitersTail is null, "Shouldn't have waiters when credit is available"); int granted = Math.Min(amount, _current); if (NetEventSource.IsEnabled) { _owner.Trace($"{_name}. requested={amount}, current={_current}, granted={granted}"); } _current -= granted; return(new ValueTask <int>(granted)); } if (NetEventSource.IsEnabled) { _owner.Trace($"{_name}. requested={amount}, no credit available."); } // Otherwise, create a new waiter. CreditWaiter waiter = cancellationToken.CanBeCanceled ? new CancelableCreditWaiter(SyncObject, cancellationToken) : new CreditWaiter(); waiter.Amount = amount; // Add the waiter at the tail of the queue. if (_waitersTail is null) { _waitersTail = waiter.Next = waiter; } else { waiter.Next = _waitersTail.Next; _waitersTail.Next = waiter; _waitersTail = waiter; } // And return a ValueTask<int> for it. return(waiter.AsValueTask()); } }
public void AdjustCredit(int amount) { // Note credit can be adjusted *downward* as well. // This can cause the current credit to become negative. lock (SyncObject) { if (NetEventSource.IsEnabled) { _owner.Trace($"{_name}. {nameof(amount)}={amount}, current={_current}"); } if (_disposed) { return; } Debug.Assert(_current <= 0 || _waitersTail is null, "Shouldn't have waiters when credit is available"); _current = checked (_current + amount); while (_current > 0 && _waitersTail != null) { // Get the waiter from the head of the queue. CreditWaiter waiter = _waitersTail.Next; int granted = Math.Min(waiter.Amount, _current); // Remove the waiter from the list. if (waiter.Next == waiter) { Debug.Assert(_waitersTail == waiter); _waitersTail = null; } else { _waitersTail.Next = waiter.Next; } waiter.Next = null; // Ensure that we grant credit only if the task has not been canceled. if (waiter.TrySetResult(granted)) { _current -= granted; } waiter.Dispose(); } } }
public ValueTask <int> RequestCreditAsync(int amount, CancellationToken cancellationToken) { lock (SyncObject) { // If we can satisfy the request with credit already available, do so synchronously. int granted = TryRequestCreditNoLock(amount); if (granted > 0) { return(new ValueTask <int>(granted)); } if (NetEventSource.Log.IsEnabled()) { _owner.Trace($"{_name}. requested={amount}, no credit available."); } // Otherwise, create a new waiter. CreditWaiter waiter = cancellationToken.CanBeCanceled ? new CancelableCreditWaiter(SyncObject, cancellationToken) : new CreditWaiter(); waiter.Amount = amount; // Add the waiter at the tail of the queue. if (_waitersTail is null) { _waitersTail = waiter.Next = waiter; } else { waiter.Next = _waitersTail.Next; _waitersTail.Next = waiter; _waitersTail = waiter; } // And return a ValueTask<int> for it. return(waiter.AsValueTask()); } }