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);
}
}
}