using Xorog.UniversalExtensions.Entities;
using Xorog.UniversalExtensions.EventArgs;
namespace Xorog.UniversalExtensions;
public static class ScheduledTaskExtensions
{
///
/// Fired when a log message has been sent.
///
public static event EventHandler? TaskStarted;
///
/// Create a scheduled task
///
/// The task to run
/// The time to run the task
/// Any custom data you wish to provide.
/// An unique identifier of the task
public static string CreateScheduledTask(this Func taskFunc, DateTime runTime, object? customData = null)
{
string UID = Guid.NewGuid().ToString();
CancellationTokenSource CancellationToken = new CancellationTokenSource();
if (Math.Ceiling(runTime.GetTimespanUntil().TotalMilliseconds) < 0)
runTime = DateTime.UtcNow.AddSeconds(1);
var scheduledTask = new ScheduledTask
{
Uid = UID,
RunTime = runTime,
TokenSource = CancellationToken,
CustomData = customData,
};
_ = LongDelay(runTime.GetTimespanUntil(), CancellationToken).ContinueWith(x =>
{
lock (InternalScheduler.RegisteredScheduledTasks)
{
InternalScheduler.RegisteredScheduledTasks.Remove(UID);
}
_logger?.LogDebug("Running scheduled task with UID '{UID}'", UID, runTime.GetTimespanUntil().GetHumanReadable());
if (x.IsCompletedSuccessfully)
{
var task = Task.Run(taskFunc);
_ = Task.Run(() =>
{
ScheduledTaskExtensions.TaskStarted?.Invoke(null, new ScheduledTaskStartedEventArgs(scheduledTask, task));
});
}
});
lock (InternalScheduler.RegisteredScheduledTasks)
{
_logger?.LogDebug("Creating scheduled task with UID '{UID}' running in {RunTime}", UID, runTime.GetTimespanUntil().GetHumanReadable());
InternalScheduler.RegisteredScheduledTasks.Add(UID, scheduledTask);
}
return UID;
}
///
/// Deletes a scheduled task
///
/// The task's unique identifier
/// Throws if the task hasn't been found or if an internal error occurred
public static void DeleteScheduledTask(string UID)
{
if (!InternalScheduler.RegisteredScheduledTasks.ContainsKey(UID))
throw new KeyNotFoundException($"No scheduled task has been found with UID '{UID}'");
if (InternalScheduler.RegisteredScheduledTasks[UID].TokenSource is null)
throw new Exception($"Internal: There is no token source registered the specified task.");
_logger?.LogDebug("Deleting scheduled task with UID '{UID}'", UID);
lock (InternalScheduler.RegisteredScheduledTasks)
{
InternalScheduler.RegisteredScheduledTasks[UID].TokenSource?.Cancel();
InternalScheduler.RegisteredScheduledTasks.Remove(UID);
}
return;
}
///
/// Gets a list of all registered tasks
///
/// A list of all registered tasks
public static IReadOnlyList? GetScheduledTasks()
=> InternalScheduler.RegisteredScheduledTasks.Select(x => x.Value).ToList().AsReadOnly();
///
/// Gets a specific task
///
/// The unique identifier of what task to get
/// The task
/// Throws if the task has not been found
public static ScheduledTask GetScheduledTask(string UID)
=> InternalScheduler.RegisteredScheduledTasks[UID];
internal static async Task LongDelay(TimeSpan delay, CancellationTokenSource token)
{
var st = new Stopwatch();
st.Start();
while (true && !token.IsCancellationRequested)
{
var remaining = (delay - st.Elapsed).TotalMilliseconds;
if (remaining <= 0)
break;
if (remaining > Int16.MaxValue)
remaining = Int16.MaxValue;
await Task.Delay(TimeSpan.FromMilliseconds(remaining), token.Token);
}
}
}