Initial commit
This commit is contained in:
114
srcs/RobotNet10/Commons/RobotNet10.Common/WatchTimerAsync.cs
Normal file
114
srcs/RobotNet10/Commons/RobotNet10.Common/WatchTimerAsync.cs
Normal file
@@ -0,0 +1,114 @@
|
||||
using Microsoft.Extensions.Logging;
|
||||
using System.Diagnostics;
|
||||
|
||||
namespace RobotNet10.Common;
|
||||
|
||||
public class WatchTimerAsync<T>(int interval, Func<Task> Callback, ILogger<T>? Logger) : IDisposable where T : class
|
||||
{
|
||||
private Timer? Timer;
|
||||
public bool Disposed;
|
||||
|
||||
private long NextDueTime;
|
||||
private readonly Lock Lock = new();
|
||||
|
||||
public int Interval => interval;
|
||||
|
||||
private async void Handler(object? state)
|
||||
{
|
||||
try
|
||||
{
|
||||
bool shouldRun = false;
|
||||
lock (Lock)
|
||||
{
|
||||
if (Disposed) return;
|
||||
long now = GetCurrentTimeMs();
|
||||
if (now >= NextDueTime)
|
||||
{
|
||||
shouldRun = true;
|
||||
long scheduledTime = NextDueTime;
|
||||
NextDueTime += interval;
|
||||
|
||||
if (now - scheduledTime > interval / 2)
|
||||
{
|
||||
NextDueTime = now + interval;
|
||||
if (Logger is not null && Logger.IsEnabled(LogLevel.Warning)) Logger.LogWarning("WatchTimerAsync Warning: Elapsed time {peak}ms exceeds interval {interval}ms.", now - scheduledTime + interval, interval);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (shouldRun)
|
||||
{
|
||||
try { await Callback.Invoke(); }
|
||||
catch (Exception ex) { if (Logger is not null && Logger.IsEnabled(LogLevel.Error)) Logger.LogError("Callback error: {ex}", ex.Message); }
|
||||
}
|
||||
|
||||
lock (Lock)
|
||||
{
|
||||
if (Disposed) return;
|
||||
long now = GetCurrentTimeMs();
|
||||
long delay = NextDueTime - now;
|
||||
if (delay < 0) delay = 0;
|
||||
Timer?.Change(delay, Timeout.Infinite);
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
if (Logger is not null && Logger.IsEnabled(LogLevel.Error)) Logger.LogError("WatchTimerAsync Error: {ex}", ex.Message);
|
||||
Timer?.Change(interval, Timeout.Infinite);
|
||||
}
|
||||
}
|
||||
|
||||
public void Start()
|
||||
{
|
||||
if (!Disposed)
|
||||
{
|
||||
lock (Lock)
|
||||
{
|
||||
NextDueTime = GetCurrentTimeMs() + interval;
|
||||
Timer = new Timer(Handler, null, Timeout.Infinite, Timeout.Infinite);
|
||||
Timer.Change(interval, Timeout.Infinite);
|
||||
}
|
||||
}
|
||||
else throw new ObjectDisposedException(nameof(WatchTimerAsync<>));
|
||||
}
|
||||
|
||||
public void Stop()
|
||||
{
|
||||
if (Disposed) return;
|
||||
|
||||
if (Timer != null)
|
||||
{
|
||||
lock (Lock)
|
||||
{
|
||||
Timer.Change(Timeout.Infinite, Timeout.Infinite);
|
||||
Timer.Dispose();
|
||||
Timer = null;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private static long GetCurrentTimeMs()
|
||||
{
|
||||
return Stopwatch.GetTimestamp() * 1000 / Stopwatch.Frequency;
|
||||
}
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
Dispose(true);
|
||||
GC.SuppressFinalize(this);
|
||||
}
|
||||
|
||||
protected virtual void Dispose(bool disposing)
|
||||
{
|
||||
if (Disposed) return;
|
||||
|
||||
if (disposing) Stop();
|
||||
|
||||
Disposed = true;
|
||||
}
|
||||
|
||||
~WatchTimerAsync()
|
||||
{
|
||||
Dispose(false);
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user