using System; using System.Threading; namespace Laservall.Solidworks.Server { internal sealed class HeartbeatMonitor : IDisposable { private readonly TimeSpan _timeout; private readonly Action _onTimeout; private Timer _timer; private long _lastPingTicks; private bool _running; private bool _disposed; public HeartbeatMonitor(TimeSpan timeout, Action onTimeout) { _timeout = timeout; _onTimeout = onTimeout; } public void Start() { _lastPingTicks = DateTime.UtcNow.Ticks; _running = true; _timer = new Timer(CheckTimeout, null, _timeout, _timeout); } public void Stop() { _running = false; _timer?.Change(Timeout.Infinite, Timeout.Infinite); } public void Ping() { Interlocked.Exchange(ref _lastPingTicks, DateTime.UtcNow.Ticks); } private void CheckTimeout(object state) { if (!_running) return; long lastPing = Interlocked.Read(ref _lastPingTicks); var elapsed = TimeSpan.FromTicks(DateTime.UtcNow.Ticks - lastPing); if (elapsed > _timeout) { _running = false; _onTimeout?.Invoke(); } } public void Dispose() { if (_disposed) return; _disposed = true; _timer?.Dispose(); } } }