Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
76 changes: 60 additions & 16 deletions src/DtronixCommon/Threading/DelayedAction.cs
Original file line number Diff line number Diff line change
@@ -1,4 +1,7 @@
namespace DtronixCommon.Threading;
using System.Diagnostics;
using System.Runtime.CompilerServices;

namespace DtronixCommon.Threading;

/// <summary>
/// Class to aid in the culling of events within a specified amount of time with a maximum delay.
Expand All @@ -8,11 +11,15 @@ public class DelayedAction : IDisposable
private readonly int _cullingInterval;
private readonly int _maxCullingDelay;

protected Action Action;
private long _lastInvokedTick = 0;
private Action _action;
private readonly Timer _timer;
private DateTime? _startTime;
private long? _startTick;
private readonly SemaphoreSlim _semaphore = new(1, 1);

/// <summary>
/// True if an invocation is queued for execution.
/// </summary>
public bool InvokeQueued { get; private set; }

/// <summary>
Expand All @@ -25,15 +32,52 @@ public DelayedAction(int cullingInterval, int maxCullingDelay, Action action)
{
_cullingInterval = cullingInterval;
_maxCullingDelay = maxCullingDelay;
Action = action;
_action = action ?? throw new ArgumentNullException(nameof(action));
_timer = new Timer(TimerCallback, null, Timeout.Infinite, Timeout.Infinite);
}

protected virtual void TimerCallback(object _)
/// <summary>
/// Method called when the timer time elapses.
/// </summary>
protected virtual void TimerCallback(object? _)
{
// If the ticks are equal, the invocation was only called once and is ready to be fired now.
if (_lastInvokedTick == _startTick)
{
ExecuteCallback();
return;
}

var currentTicks = Environment.TickCount64;
var elapsedTime = currentTicks - _startTick!.Value;

// See if we have exceeded the max culling delay.
if (elapsedTime >= _maxCullingDelay)
{
ExecuteCallback();
return;
}

// Check to see if we are exceeded the culling interval.
var lastInvokeTickDelta = currentTicks - _lastInvokedTick;
if (lastInvokeTickDelta >= _cullingInterval)
{
ExecuteCallback();
}
}

private void ExecuteCallback()
{
// Stop the timer.
_timer.Change(-1, -1);
InvokeQueued = false;
_startTime = null;
Action?.Invoke();
_startTick = null;
OnCallback();
}

protected virtual void OnCallback()
{
_action.Invoke();
}

/// <summary>
Expand Down Expand Up @@ -77,21 +121,21 @@ public void Invoke()
private void InvokeInternal()
{
InvokeQueued = true;
_lastInvokedTick = Environment.TickCount64;
// Check if we have exceeded the maximum delay time.
if (_maxCullingDelay == 0
|| (DateTime.UtcNow - (_startTime ??= DateTime.UtcNow)).TotalMilliseconds >= _maxCullingDelay)
if (_startTick == null)
{
// Stop the timer.
_timer.Change(0, Timeout.Infinite);
return;
_startTick = _lastInvokedTick;
_timer.Change(_cullingInterval, _cullingInterval);
}

_timer.Change(_cullingInterval, Timeout.Infinite);

}


public void Dispose()
{
_timer?.Dispose();
_semaphore?.Dispose();
_timer.Dispose();
_semaphore.Dispose();
}
}
10 changes: 8 additions & 2 deletions src/DtronixCommon/Threading/DelayedActionGeneric.cs
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
public class DelayedAction<TArgs> : DelayedAction
where TArgs : struct
{
private readonly Action<TArgs> _action;
private TArgs _arguments;

/// <summary>
Expand All @@ -16,9 +17,14 @@ public class DelayedAction<TArgs> : DelayedAction
/// <param name="maxCullingDelay">Maximum delay that calls will be culled.</param>
/// <param name="action">Action to be invoked with the arguments passed.</param>
public DelayedAction(int cullingInterval, int maxCullingDelay, Action<TArgs> action)
: base(cullingInterval, maxCullingDelay, null)
: base(cullingInterval, maxCullingDelay, () => { })
{
Action = () => action(_arguments);
_action = action ?? throw new ArgumentNullException(nameof(action));
}

protected override void OnCallback()
{
_action.Invoke(_arguments);
}

/// <summary>
Expand Down