| File: System\Threading\RateLimiting\ChainedRateLimiterShared.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.Collections.Generic; using System.Runtime.ExceptionServices; using System.Threading.Tasks; namespace System.Threading.RateLimiting { /// <summary> /// Shared static methods used by <see cref="ChainedRateLimiter"/>, /// <see cref="ChainedReplenishingRateLimiter"/>, and <see cref="ChainedPartitionedRateLimiter{TResource}"/>. /// </summary> internal static class ChainedRateLimiterShared { internal static RateLimiterStatistics GetStatisticsCore(RateLimiter[] limiters) { long lowestAvailablePermits = long.MaxValue; long currentQueuedCount = 0; long totalFailedLeases = 0; long innerMostSuccessfulLeases = 0; foreach (RateLimiter limiter in limiters) { if (limiter.GetStatistics() 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, }; } internal static TimeSpan? GetIdleDurationCore(RateLimiter[] limiters) { TimeSpan? lowestIdleDuration = null; foreach (RateLimiter limiter in limiters) { TimeSpan? idleDuration = limiter.IdleDuration; if (idleDuration is null) { // The chain should not be considered idle if any of its children is not idle. return null; } if (lowestIdleDuration is null || idleDuration < lowestIdleDuration) { lowestIdleDuration = idleDuration; } } return lowestIdleDuration; } internal static RateLimitLease AttemptAcquireChained(RateLimiter[] limiters, int permitCount) { RateLimitLease[]? leases = null; for (int i = 0; i < limiters.Length; i++) { RateLimitLease? lease = null; Exception? exception = null; try { lease = limiters[i].AttemptAcquire(permitCount); } catch (Exception ex) { exception = ex; } RateLimitLease? notAcquiredLease = CommonAcquireLogic(exception, lease, ref leases, i, limiters.Length); if (notAcquiredLease is not null) { return notAcquiredLease; } } return new CombinedRateLimitLease(leases!); } internal static async ValueTask<RateLimitLease> AcquireAsyncChained(RateLimiter[] limiters, int permitCount, CancellationToken cancellationToken) { RateLimitLease[]? leases = null; for (int i = 0; i < limiters.Length; i++) { RateLimitLease? lease = null; Exception? exception = null; try { lease = await limiters[i].AcquireAsync(permitCount, cancellationToken).ConfigureAwait(false); } catch (Exception ex) { exception = ex; } RateLimitLease? notAcquiredLease = CommonAcquireLogic(exception, lease, ref leases, i, limiters.Length); if (notAcquiredLease is not null) { return notAcquiredLease; } } return new CombinedRateLimitLease(leases!); } internal static RateLimitLease? CommonAcquireLogic(Exception? ex, RateLimitLease? lease, ref RateLimitLease[]? leases, int index, int length) { if (ex is not null) { AggregateException? innerEx = CommonDispose(leases, index); if (innerEx is not null) { Exception[] exceptions = new Exception[innerEx.InnerExceptions.Count + 1]; innerEx.InnerExceptions.CopyTo(exceptions, 0); exceptions[exceptions.Length - 1] = ex; throw new AggregateException(exceptions); } ExceptionDispatchInfo.Capture(ex).Throw(); } if (!lease!.IsAcquired) { AggregateException? innerEx = CommonDispose(leases, index); return innerEx is not null ? throw innerEx : lease; } leases ??= new RateLimitLease[length]; leases[index] = lease; return null; } private static AggregateException? CommonDispose(RateLimitLease[]? leases, int i) { List<Exception>? exceptions = null; while (i > 0) { i--; try { leases![i].Dispose(); } catch (Exception ex) { exceptions ??= []; exceptions.Add(ex); } } if (exceptions is not null) { return new AggregateException(exceptions); } return null; } } }