Files
MilkyShots/Lactose/Jobs/Job.cs

115 lines
4.5 KiB
C#

using Butter.Types;
namespace Lactose.Jobs;
/// <summary>
/// Abstract class representing a job that can be executed asynchronously.
/// Provides events for job lifecycle (Started, ProgressChanged, Completed, Canceled, Failed).
/// Inherit from this class and implement the TaskJob method to define the job's functionality.
/// </summary>
public abstract class Job {
/// <summary>
/// Gets the unique identifier of the job.
/// </summary>
public virtual Guid Id { get; } = Guid.NewGuid();
/// <summary>
/// Gets or sets the parent job ID, if this is a sub-job.
/// </summary>
public virtual Guid? ParentJobId { get; set; }
/// <summary>
/// Gets the name of the job.
/// </summary>
public virtual string Name { get; } = "Unnamed Job";
/// <summary>
/// Gets the status tracker for this job.
/// </summary>
public virtual JobStatus JobStatus { get; }
Task? task;
CancellationTokenSource? cts;
/// <summary>
/// Initialises a new job instance.
/// </summary>
protected Job() => JobStatus = new JobStatus(this);
/// <summary>
/// Sets up the task and the cancellation token, then starts the task (triggering the Started event)
/// Also sets up a continuation to handle completion, failure, or cancellation of the task.
/// </summary>
public virtual void Start() {
JobStatus.Start();
cts = new CancellationTokenSource();
task = Task.Run(() => TaskJob(cts.Token));
task.ContinueWith(t => {
switch (JobStatus.Status) {
case EJobStatus.Canceled:
Canceled?.Invoke(this, JobStatus);
break;
case EJobStatus.Failed:
Failed?.Invoke(this, JobStatus);
break;
case EJobStatus.Running: // If still running or waiting, mark as completed
case EJobStatus.Waiting: //somebody forgot to update the status and the method returned.
case EJobStatus.Completed:
case EJobStatus.CompletedWithErrors:
Completed?.Invoke(this, JobStatus);
break;
case EJobStatus.Queued: // This should never happen
default:
Completed?.Invoke(this, JobStatus);
break;
}
Done?.Invoke(this, JobStatus);
}
);
Started?.Invoke(this, EventArgs.Empty);
}
/// <summary>
/// Override this method to implement the job's functionality. The provided CancellationToken can be used to check for cancellation requests.
/// Invoke ProgressChanged event to report progress updates.
/// </summary>
/// <param name="token">Cancellation toke to retrieve when a task cancellation has been requested</param>
protected abstract Task TaskJob(CancellationToken token);
/// <summary>
/// Cancels the job if it's running. Canceled event will be called by the callbacks on the task.
/// </summary>
public virtual void Cancel() => cts?.Cancel();
/// <summary>
/// Raised when the job starts running.
/// </summary>
public virtual event EventHandler? Started;
/// <summary>
/// Raised when the job makes progress. The JobStatus parameter contains the current progress (0 to 1).
/// </summary>
public virtual event EventHandler<JobStatus>? ProgressChanged;
/// <summary>
/// Raised when the job transitions from running to waiting (e.g., waiting for resources or sub jobs).
/// </summary>
public virtual event EventHandler<JobStatus>? StartedWaiting;
/// <summary>
/// Raised when the job transitions from waiting to running.
/// </summary>
public virtual event EventHandler<JobStatus>? FinishedWaiting;
/// <summary>
/// Raised when the job completes successfully.
/// </summary>
public virtual event EventHandler<JobStatus>? Completed;
/// <summary>
/// Raised when the job is canceled by the user or system.
/// </summary>
public virtual event EventHandler<JobStatus>? Canceled;
/// <summary>
/// Raised when the job fails due to an unhandled exception.
/// </summary>
public virtual event EventHandler<JobStatus>? Failed;
/// <summary>
/// Raised when the job is done, regardless of the final status (Completed, Canceled, or Failed).
/// </summary>
public virtual event EventHandler<JobStatus>? Done;
}