using System; using System.Collections.Generic; using System.Linq; using System.Threading; using System.Threading.Tasks; using ComposableAsync; namespace RateLimiter { /// /// TimeLimiter implementation /// public class TimeLimiter : IDispatcher { private readonly IAwaitableConstraint _AwaitableConstraint; internal TimeLimiter(IAwaitableConstraint awaitableConstraint) { _AwaitableConstraint = awaitableConstraint; } /// /// Perform the given task respecting the time constraint /// returning the result of given function /// /// /// public Task Enqueue(Func perform) { return Enqueue(perform, CancellationToken.None); } /// /// Perform the given task respecting the time constraint /// returning the result of given function /// /// /// /// public Task Enqueue(Func> perform) { return Enqueue(perform, CancellationToken.None); } /// /// Perform the given task respecting the time constraint /// /// /// /// public async Task Enqueue(Func perform, CancellationToken cancellationToken) { cancellationToken.ThrowIfCancellationRequested(); using (await _AwaitableConstraint.WaitForReadiness(cancellationToken)) { await perform(); } } /// /// Perform the given task respecting the time constraint /// returning the result of given function /// /// /// /// /// public async Task Enqueue(Func> perform, CancellationToken cancellationToken) { cancellationToken.ThrowIfCancellationRequested(); using (await _AwaitableConstraint.WaitForReadiness(cancellationToken)) { return await perform(); } } public IDispatcher Clone() => new TimeLimiter(_AwaitableConstraint.Clone()); private static Func Transform(Action act) { return () => { act(); return Task.FromResult(0); }; } /// /// Perform the given task respecting the time constraint /// returning the result of given function /// /// /// /// private static Func> Transform(Func compute) { return () => Task.FromResult(compute()); } /// /// Perform the given task respecting the time constraint /// /// /// public Task Enqueue(Action perform) { var transformed = Transform(perform); return Enqueue(transformed); } /// /// Perform the given task respecting the time constraint /// /// public void Dispatch(Action action) { Enqueue(action); } /// /// Perform the given task respecting the time constraint /// returning the result of given function /// /// /// /// public Task Enqueue(Func perform) { var transformed = Transform(perform); return Enqueue(transformed); } /// /// Perform the given task respecting the time constraint /// returning the result of given function /// /// /// /// /// public Task Enqueue(Func perform, CancellationToken cancellationToken) { var transformed = Transform(perform); return Enqueue(transformed, cancellationToken); } /// /// Perform the given task respecting the time constraint /// /// /// /// public Task Enqueue(Action perform, CancellationToken cancellationToken) { var transformed = Transform(perform); return Enqueue(transformed, cancellationToken); } /// /// Returns a TimeLimiter based on a maximum number of times /// during a given period /// /// /// /// public static TimeLimiter GetFromMaxCountByInterval(int maxCount, TimeSpan timeSpan) { return new TimeLimiter(new CountByIntervalAwaitableConstraint(maxCount, timeSpan)); } /// /// Create that will save state using action passed through parameter. /// /// Maximum actions allowed per time interval. /// Time interval limits are applied for. /// Action is used to save state. /// instance with . public static TimeLimiter GetPersistentTimeLimiter(int maxCount, TimeSpan timeSpan, Action saveStateAction) { return GetPersistentTimeLimiter(maxCount, timeSpan, saveStateAction, null); } /// /// Create with initial timestamps that will save state using action passed through parameter. /// /// Maximum actions allowed per time interval. /// Time interval limits are applied for. /// Action is used to save state. /// Initial timestamps. /// instance with . public static TimeLimiter GetPersistentTimeLimiter(int maxCount, TimeSpan timeSpan, Action saveStateAction, IEnumerable initialTimeStamps) { return new TimeLimiter(new PersistentCountByIntervalAwaitableConstraint(maxCount, timeSpan, saveStateAction, initialTimeStamps)); } /// /// Compose various IAwaitableConstraint in a TimeLimiter /// /// /// public static TimeLimiter Compose(params IAwaitableConstraint[] constraints) { var composed = constraints.Aggregate(default(IAwaitableConstraint), (accumulated, current) => (accumulated == null) ? current : accumulated.Compose(current)); return new TimeLimiter(composed); } } }