1
0
Fork 0
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

168 lines
5.6 KiB

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<BGTaskStartEvent>? OnExecuteStart;
public event EventHandler<BGTaskEndEvent>? 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<TResult>
{
public Guid Guid { get; } = Guid.NewGuid();
private int _intervalID = 0;
private readonly CancellationTokenSource _cts = new();
private readonly IBGTaskLoopable<TResult> _bgTaskDelegate;
private PeriodicTimer _pTimer;
private TimeSpan _currentIntrvl;
private Task? _bgTask;
public event EventHandler<BGTaskStartEvent>? OnExecuteStart;
public event EventHandler<BGTaskEndEvent<TResult>>? OnExecuteEnd;
public BGTaskLoop(IBGTaskLoopable<TResult> 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
/// <summary> Updates the current timer loop to return on a new interval. This will take effect after any current iteration is in process</summary>
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();
}
}
}