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); } } }