Files
annotations/Azaion.Common/Extensions/ThrottleExtensions.cs
T
Alex Bezdieniezhnykh d42409de7d fix throttle ext
fix configs
fix build scripts
2025-04-17 19:40:09 +03:00

72 lines
2.4 KiB
C#

using System.Collections.Concurrent;
namespace Azaion.Common.Extensions;
public static class ThrottleExt
{
private class ThrottleState(Func<Task> action)
{
public Func<Task> Action { get; set; } = action ?? throw new ArgumentNullException(nameof(action));
public bool IsCoolingDown = false;
public bool CallScheduledDuringCooldown = false;
public Task CooldownTask = Task.CompletedTask;
public readonly object StateLock = new();
}
private static readonly ConcurrentDictionary<Guid, ThrottleState> ThrottlerStates = new();
public static void Throttle(Func<Task> action, Guid actionId, TimeSpan interval, bool scheduleCallAfterCooldown = false)
{
ArgumentNullException.ThrowIfNull(action);
if (actionId == Guid.Empty)
throw new ArgumentException("Throttle identifier cannot be empty.", nameof(actionId));
if (interval <= TimeSpan.Zero)
throw new ArgumentOutOfRangeException(nameof(interval), "Interval must be positive.");
var state = ThrottlerStates.GetOrAdd(actionId, new ThrottleState(action));
state.Action = action;
lock (state.StateLock)
{
if (!state.IsCoolingDown)
{
state.IsCoolingDown = true;
state.CooldownTask = ExecuteAndManageCooldownStaticAsync(actionId, interval, state);
}
else
{
if (scheduleCallAfterCooldown)
state.CallScheduledDuringCooldown = true;
}
}
}
private static async Task ExecuteAndManageCooldownStaticAsync(Guid throttleId, TimeSpan interval, ThrottleState state)
{
try
{
await state.Action();
}
catch (Exception ex)
{
Console.WriteLine($"[Throttled Action Error - ID: {throttleId}] {ex.GetType().Name}: {ex.Message}");
}
finally
{
await Task.Delay(interval);
lock (state.StateLock)
{
if (state.CallScheduledDuringCooldown)
{
state.CallScheduledDuringCooldown = false;
state.CooldownTask = ExecuteAndManageCooldownStaticAsync(throttleId, interval, state);
}
else
{
state.IsCoolingDown = false;
}
}
}
}
}