refactor!: Use Func<Task> for scheduled tasks

This commit is contained in:
Mira 2023-08-06 19:13:35 +02:00
parent 647d7c4a1c
commit bc8eb1ac0d
Signed by untrusted user who does not match committer: Xorog
GPG key ID: 983798ED9C3E7C36
5 changed files with 98 additions and 66 deletions

35
Entities/ScheduledTask.cs Normal file
View file

@ -0,0 +1,35 @@
namespace Xorog.UniversalExtensions.Entities;
public class ScheduledTask
{
internal ScheduledTask()
{
this.Uid = Guid.NewGuid().ToString();
}
/// <summary>
/// The unique identifier of this task.
/// </summary>
public string Uid { get; internal set; }
/// <summary>
/// The custom data asscociated with this task.
/// </summary>
public object? CustomData { get; internal set; }
/// <summary>
/// The time this task will run.
/// </summary>
public DateTime? RunTime { get; internal set; }
/// <summary>
/// The <see cref="CancellationTokenSource"/> to prematurely dequeue this task.
/// </summary>
internal CancellationTokenSource? TokenSource { get; set; } = new();
/// <summary>
/// Delete this task.
/// </summary>
public void Delete() =>
ScheduledTaskExtensions.DeleteScheduledTask(Uid);
}

View file

@ -0,0 +1,22 @@
using Xorog.UniversalExtensions.Entities;
namespace Xorog.UniversalExtensions.EventArgs;
public class ScheduledTaskStartedEventArgs : System.EventArgs
{
internal ScheduledTaskStartedEventArgs(ScheduledTask details, Task task)
{
this.Details = details;
this.Task = task;
}
/// <summary>
/// The details of this scheduled task.
/// </summary>
public ScheduledTask Details { get; internal set; }
/// <summary>
/// The task that was executed.
/// </summary>
public Task Task { get; internal set; }
}

View file

@ -1,52 +1,61 @@
namespace Xorog.UniversalExtensions;
using Xorog.UniversalExtensions.Entities;
using Xorog.UniversalExtensions.EventArgs;
namespace Xorog.UniversalExtensions;
public static class ScheduledTaskExtensions
{
/// <summary>
/// Fired when a log message has been sent.
/// </summary>
public static event EventHandler<ScheduledTaskStartedEventArgs>? TaskStarted;
/// <summary>
/// Create a scheduled task
/// </summary>
/// <param name="task">The task to run</param>
/// <param name="taskFunc">The task to run</param>
/// <param name="runTime">The time to run the task</param>
/// <param name="customData">Any custom data you wish to provide.</param>
/// <returns>An unique identifier of the task</returns>
public static string CreateScheduledTask(this Task task, DateTime runTime, object? customData = null)
public static string CreateScheduledTask(this Func<Task> taskFunc, DateTime runTime, object? customData = null)
{
if (task.Status != TaskStatus.Created)
throw new InvalidOperationException("The task is already being executed or has been scheduled for execution.")
.AttachData("Task", task)
.AttachData("RunTime", runTime)
.AttachData("CustomData", customData);
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 (RegisteredScheduledTasks)
lock (InternalScheduler.RegisteredScheduledTasks)
{
RegisteredScheduledTasks.Remove(UID);
InternalScheduler.RegisteredScheduledTasks.Remove(UID);
}
_logger?.LogDebug("Running scheduled task with UID '{UID}'", UID, runTime.GetTimespanUntil().GetHumanReadable());
if (x.IsCompletedSuccessfully)
task.Start();
{
var task = Task.Run(taskFunc);
_ = Task.Run(() =>
{
ScheduledTaskExtensions.TaskStarted?.Invoke(null, new ScheduledTaskStartedEventArgs(scheduledTask, task));
});
}
});
lock (RegisteredScheduledTasks)
lock (InternalScheduler.RegisteredScheduledTasks)
{
_logger?.LogDebug("Creating scheduled task with UID '{UID}' running in {RunTime}", UID, runTime.GetTimespanUntil().GetHumanReadable());
RegisteredScheduledTasks.Add(UID, new ScheduledTask
{
Uid = UID,
RunTime = runTime,
TokenSource = CancellationToken,
CustomData = customData,
});
InternalScheduler.RegisteredScheduledTasks.Add(UID, scheduledTask);
}
return UID;
}
@ -60,18 +69,18 @@ public static class ScheduledTaskExtensions
/// <exception cref="KeyNotFoundException">Throws if the task hasn't been found or if an internal error occurred</exception>
public static void DeleteScheduledTask(string UID)
{
if (!RegisteredScheduledTasks.ContainsKey(UID))
if (!InternalScheduler.RegisteredScheduledTasks.ContainsKey(UID))
throw new KeyNotFoundException($"No scheduled task has been found with UID '{UID}'");
if (RegisteredScheduledTasks[UID].TokenSource is null)
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 (RegisteredScheduledTasks)
lock (InternalScheduler.RegisteredScheduledTasks)
{
RegisteredScheduledTasks[UID].TokenSource?.Cancel();
RegisteredScheduledTasks.Remove(UID);
InternalScheduler.RegisteredScheduledTasks[UID].TokenSource?.Cancel();
InternalScheduler.RegisteredScheduledTasks.Remove(UID);
}
return;
}
@ -83,7 +92,7 @@ public static class ScheduledTaskExtensions
/// </summary>
/// <returns>A list of all registered tasks</returns>
public static IReadOnlyList<ScheduledTask>? GetScheduledTasks()
=> RegisteredScheduledTasks.Select(x => x.Value).ToList().AsReadOnly();
=> InternalScheduler.RegisteredScheduledTasks.Select(x => x.Value).ToList().AsReadOnly();
/// <summary>
/// Gets a specific task
@ -92,7 +101,7 @@ public static class ScheduledTaskExtensions
/// <returns>The task</returns>
/// <exception cref="Exception">Throws if the task has not been found</exception>
public static ScheduledTask GetScheduledTask(string UID)
=> RegisteredScheduledTasks[UID];
=> InternalScheduler.RegisteredScheduledTasks[UID];
internal static async Task LongDelay(TimeSpan delay, CancellationTokenSource token)
{

View file

@ -9,6 +9,4 @@ global using System.Drawing;
global using Microsoft.Extensions.Logging;
global using Xorog.UniversalExtensions.Enums;
global using static Xorog.UniversalExtensions.Internal;
global using static Xorog.UniversalExtensions.InternalSheduler;
global using static Xorog.UniversalExtensions.Log;

View file

@ -1,4 +1,6 @@
namespace Xorog.UniversalExtensions;
using Xorog.UniversalExtensions.Entities;
namespace Xorog.UniversalExtensions;
internal static class Internal
{
@ -79,43 +81,9 @@ internal static class Internal
}
}
public class InternalSheduler
internal class InternalScheduler
{
public static Dictionary<string, ScheduledTask> RegisteredScheduledTasks { get; internal set; } = new Dictionary<string, ScheduledTask>();
public class ScheduledTask
{
public ScheduledTask()
{
this.Uid = Guid.NewGuid().ToString();
}
/// <summary>
/// The unique identifier of this task.
/// </summary>
public string Uid { get; internal set; }
/// <summary>
/// The custom data asscociated with this task.
/// </summary>
public object? CustomData { get; internal set; }
/// <summary>
/// The time this task will run.
/// </summary>
public DateTime? RunTime { get; internal set; }
/// <summary>
/// The <see cref="CancellationTokenSource"/> to prematurely dequeue this task.
/// </summary>
internal CancellationTokenSource? TokenSource { get; set; } = new();
/// <summary>
/// Delete this task.
/// </summary>
public void Delete() =>
ScheduledTaskExtensions.DeleteScheduledTask(Uid);
}
}
public class HumanReadableTimeFormatConfig