Created
June 6, 2022 10:48
-
-
Save mstijak/7e10a1e73c763d3ca71ffd6e7f89ce01 to your computer and use it in GitHub Desktop.
WakeUpService class is very helpful for organizing background tasks.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
using System; | |
using System.Collections.Generic; | |
using System.Linq; | |
using System.Text; | |
using System.Threading; | |
using System.Threading.Tasks; | |
namespace StatusApp.Util; | |
public sealed class WakeUpService : IDisposable | |
{ | |
public static WakeUpService Shared { get; } = new WakeUpService(); | |
private class WakeUpCall | |
{ | |
public object Target { get; init; } = null!; | |
public Func<Task> Callback { get; init; } = null!; | |
public DateTimeOffset When { get; init; } | |
} | |
private readonly SortedDictionary<DateTimeOffset, List<WakeUpCall>> schedule; | |
private readonly Dictionary<object, WakeUpCall> targets; | |
private readonly object lockObject = new(); | |
private Timer? timer; | |
public TimeSpan TimerInterval { get; set; } | |
public WakeUpService() | |
{ | |
schedule = new SortedDictionary<DateTimeOffset, List<WakeUpCall>>(); | |
targets = new Dictionary<object, WakeUpCall>(); | |
TimerInterval = TimeSpan.FromMilliseconds(1000); | |
} | |
public void Schedule(object target, DateTimeOffset when, Func<Task> callback) | |
{ | |
lock (lockObject) | |
{ | |
Cancel(target); | |
var call = new WakeUpCall { Target = target, Callback = callback, When = when }; | |
targets.Add(target, call); | |
if (!schedule.TryGetValue(when, out var calls)) | |
{ | |
calls = new List<WakeUpCall>(); | |
schedule[when] = calls; | |
} | |
calls.Add(call); | |
if (timer == null) | |
timer = new Timer(OnCheck, null, 0, (int)TimerInterval.TotalMilliseconds); | |
} | |
} | |
public Action Schedule(DateTimeOffset when, Func<Task> callback) | |
{ | |
var service = new object(); | |
Schedule(service, when, callback); | |
return () => Cancel(service); | |
} | |
public void Schedule(object target, TimeSpan dueTime, Func<Task> callback) | |
{ | |
Schedule(target, DateTimeOffset.Now.Add(dueTime), callback); | |
} | |
public void Schedule(TimeSpan dueTime, Func<Task> callback) | |
{ | |
Schedule(DateTimeOffset.Now.Add(dueTime), callback); | |
} | |
public void OnCheck(object? state) | |
{ | |
lock (lockObject) | |
{ | |
while (schedule.Count > 0) | |
{ | |
var entry = schedule.First(); | |
if (DateTimeOffset.Now < entry.Key) | |
return; | |
schedule.Remove(entry.Key); | |
foreach (var call in entry.Value) | |
{ | |
targets.Remove(call.Target); | |
Task.Run(call.Callback); | |
} | |
} | |
DisposeTimer(); | |
} | |
} | |
public void Cancel(object service) | |
{ | |
lock (lockObject) | |
{ | |
if (!targets.Remove(service, out var wakeUpCall)) | |
return; | |
if (!schedule.TryGetValue(wakeUpCall.When, out var calls)) | |
return; | |
calls.RemoveAll(x => x.Target == service); | |
if (calls.Count == 0) | |
schedule.Remove(wakeUpCall.When); | |
} | |
} | |
public void Dispose() | |
{ | |
DisposeTimer(); | |
} | |
private void DisposeTimer() | |
{ | |
if (timer != null) | |
{ | |
timer.Dispose(); | |
timer = null; | |
} | |
} | |
} |
Author
mstijak
commented
Jun 6, 2022
•
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment