| File: System\Threading\RateLimiting\ChainedPartitionedRateLimiter.cs | Web Access |
| Project: src\runtime\src\libraries\System.Threading.RateLimiting\src\System.Threading.RateLimiting.csproj (System.Threading.RateLimiting) |
// 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.Collections.Generic; using System.Linq; using System.Text; using System.Threading.Tasks; namespace System.Threading.RateLimiting { /// <summary> /// Acquires leases from rate limiters in the order given. If a lease fails to be acquired (throwing or IsAcquired == false) /// then the already acquired leases are disposed in reverse order and the failing lease is returned or the exception is thrown to user code. /// </summary> internal sealed class ChainedPartitionedRateLimiter<TResource> : PartitionedRateLimiter<TResource> { private readonly PartitionedRateLimiter<TResource>[] _limiters; private bool _disposed; public ChainedPartitionedRateLimiter(PartitionedRateLimiter<TResource>[] limiters) { _limiters = (PartitionedRateLimiter<TResource>[])limiters.Clone(); } public override RateLimiterStatistics? GetStatistics(TResource resource) { ThrowIfDisposed(); long lowestAvailablePermits = long.MaxValue; long currentQueuedCount = 0; long totalFailedLeases = 0; long innerMostSuccessfulLeases = 0; foreach (PartitionedRateLimiter<TResource> limiter in _limiters) { if (limiter.GetStatistics(resource) is { } statistics) { if (statistics.CurrentAvailablePermits < lowestAvailablePermits) { lowestAvailablePermits = statistics.CurrentAvailablePermits; } currentQueuedCount += statistics.CurrentQueuedCount; totalFailedLeases += statistics.TotalFailedLeases; innerMostSuccessfulLeases = statistics.TotalSuccessfulLeases; } } return new RateLimiterStatistics() { CurrentAvailablePermits = lowestAvailablePermits, CurrentQueuedCount = currentQueuedCount, TotalFailedLeases = totalFailedLeases, TotalSuccessfulLeases = innerMostSuccessfulLeases, }; } protected override RateLimitLease AttemptAcquireCore(TResource resource, int permitCount) { ThrowIfDisposed(); RateLimitLease[]? leases = null; for (int i = 0; i < _limiters.Length; i++) { RateLimitLease? lease = null; Exception? exception = null; try { lease = _limiters[i].AttemptAcquire(resource, permitCount); } catch (Exception ex) { exception = ex; } RateLimitLease? notAcquiredLease = ChainedRateLimiterShared.CommonAcquireLogic(exception, lease, ref leases, i, _limiters.Length); if (notAcquiredLease is not null) { return notAcquiredLease; } } return new CombinedRateLimitLease(leases!); } protected override async ValueTask<RateLimitLease> AcquireAsyncCore(TResource resource, int permitCount, CancellationToken cancellationToken) { ThrowIfDisposed(); RateLimitLease[]? leases = null; for (int i = 0; i < _limiters.Length; i++) { RateLimitLease? lease = null; Exception? exception = null; try { lease = await _limiters[i].AcquireAsync(resource, permitCount, cancellationToken).ConfigureAwait(false); } catch (Exception ex) { exception = ex; } RateLimitLease? notAcquiredLease = ChainedRateLimiterShared.CommonAcquireLogic(exception, lease, ref leases, i, _limiters.Length); if (notAcquiredLease is not null) { return notAcquiredLease; } } return new CombinedRateLimitLease(leases!); } protected override void Dispose(bool disposing) { _disposed = true; } private void ThrowIfDisposed() { if (_disposed) { throw new ObjectDisposedException(nameof(ChainedPartitionedRateLimiter<TResource>)); } } } }