using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Threading.Tasks; namespace Scheduler.Backgrounding { public class BGTaskLoop { public Guid Guid { get; } = Guid.NewGuid(); private int _intervalID = 0; private readonly CancellationTokenSource _cts = new(); private readonly IBGTaskLoopable _bgTaskDelegate; private readonly PeriodicTimer _pTimer; private Task? _bgTask; public event EventHandler? OnExecuteStart; public event EventHandler? OnExecuteEnd; public BGTaskLoop(IBGTaskLoopable task, TimeSpan interval) { _bgTaskDelegate = task; _pTimer = new PeriodicTimer(interval); } public void Start() { _bgTask = RunBGTaskOnInterval(); } private async Task RunBGTaskOnInterval() { try { while (await _pTimer.WaitForNextTickAsync(_cts.Token)) //TODO: Maybe inform the scheduling core when //task time > interval //or automatically update interval? //"BackgroundTaskScheduler that will call "update interval" etc. { int id = ++_intervalID; OnExecuteStart?.Invoke(this, new(Guid, id, DateTime.Now)); await _bgTaskDelegate.OnBGTaskLoopAsync(); OnExecuteEnd?.Invoke(this, new(Guid, id, DateTime.Now)); } } catch { } } public async Task StopAsync() { if (_bgTask == null) return; _cts.Cancel(); await _bgTask; //ensure completion _pTimer.Dispose(); _cts.Dispose(); } } public class BGTaskLoop { public Guid Guid { get; } = Guid.NewGuid(); private int _intervalID = 0; private readonly CancellationTokenSource _cts = new(); private readonly IBGTaskLoopable _bgTaskDelegate; private PeriodicTimer _pTimer; private TimeSpan _currentIntrvl; private Task? _bgTask; public event EventHandler? OnExecuteStart; public event EventHandler>? OnExecuteEnd; public BGTaskLoop(IBGTaskLoopable task, TimeSpan interval) { _bgTaskDelegate = task; _currentIntrvl = interval; _pTimer = new PeriodicTimer(interval); } public void Start() { _bgTask = RunBGTaskOnIntervalAsync(); } internal void Start(TimeSpan startAfter) { _bgTask = RunBGTaskOnIntervalAsync(startAfter); } private async Task RunBGTaskOnIntervalAsync(TimeSpan? startAfter = null) { if (startAfter.HasValue) await Task.Delay((int)startAfter.Value.TotalMilliseconds); try { while (await _pTimer.WaitForNextTickAsync(_cts.Token)) //TODO: Maybe inform the scheduling core when //task time > interval //or automatically update interval? //"BackgroundTaskScheduler that will call "update interval" etc. { bool update = _updateRequested; if (update) { _pTimer.Dispose(); //Dispose directly after call, //.WaitForNextTickAsync(..) will now return false } int id = ++_intervalID; OnExecuteStart?.Invoke(this, new(Guid, id, DateTime.Now)); TResult result = await _bgTaskDelegate.OnBGTaskLoopAsync(); OnExecuteEnd?.Invoke(this, new(Guid, id, DateTime.Now, result)); if (update) { UpdateRunBGTaskOnInterval(); return; } } } catch (Exception ex) { var asd = ex; //TODO: Invoke OnError? } } private bool _updateRequested = false; //TODO: make it instantly act upon timer, //eg. if timer would trigger 2 sec after .UpdateInterval(..) //and newInterval is lower than 2 sec //make the call earlier somehow :-) //opt: extract while business logic to method and call it earlier than while loop wouldve done /// Updates the current timer loop to return on a new interval. This will take effect after any current iteration is in process public void UpdateInterval(TimeSpan newInterval) { _currentIntrvl = newInterval; _updateRequested = true; } private void UpdateRunBGTaskOnInterval() { _updateRequested = false; _pTimer = new(_currentIntrvl); _bgTask = RunBGTaskOnIntervalAsync(); } public async Task StopAsync() { if (_bgTask == null) return; _cts.Cancel(); await _bgTask; //ensure completion _pTimer.Dispose(); _cts.Dispose(); } } }