Initial commit
This commit is contained in:
112
srcs/RobotNet10/Commons/RobotNet10.Common/WatchTimer.cs
Normal file
112
srcs/RobotNet10/Commons/RobotNet10.Common/WatchTimer.cs
Normal file
@@ -0,0 +1,112 @@
|
||||
using Microsoft.Extensions.Logging;
|
||||
using System.Diagnostics;
|
||||
|
||||
namespace RobotNet10.Common;
|
||||
|
||||
public class WatchTimer<T>(int Interval, Action Callback, ILogger<T>? Logger) : IDisposable where T : class
|
||||
{
|
||||
private Timer? Timer;
|
||||
public bool Disposed;
|
||||
|
||||
private long NextDueTime;
|
||||
private readonly Lock Lock = new();
|
||||
|
||||
private 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("WatchTimer Warning: Elapsed time {peak}ms exceeds interval {Interval}ms.", now - scheduledTime + Interval, Interval);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (shouldRun)
|
||||
{
|
||||
try { 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("WatchTimer 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(WatchTimer<>));
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
~WatchTimer()
|
||||
{
|
||||
Dispose(false);
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user