- Remove unused using directives across C# and Razor files - Remove unused IServiceProvider from SettingsRepository - Simplify null/empty string checks in StatsRepository - Add null-safe navigation for Albums/Tags in stats queries - Initialize Asset.Hash default to prevent null refs - Deduplicate AssetIds in AssetPicker - Add OnStartedWaiting/OnFinishedWaiting/OnProgressChanged to Job - Add global SearchDropdown component with keyboard nav - Fix XML doc param mismatches
168 lines
6.4 KiB
C#
168 lines
6.4 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; }
|
|
/// <summary>
|
|
/// Gets or sets the DI scope created for this job's dependencies.
|
|
/// Disposed automatically when the job completes.
|
|
/// </summary>
|
|
public IServiceScope? Scope { get; set; }
|
|
Task? task;
|
|
CancellationTokenSource? cts;
|
|
CancellationToken? _parentToken;
|
|
|
|
/// <summary>
|
|
/// Initialises a new job instance.
|
|
/// </summary>
|
|
protected Job() => JobStatus = new JobStatus(this);
|
|
|
|
/// <summary>
|
|
/// Links this job's cancellation token to a parent token. When the parent token is cancelled,
|
|
/// this job's token is also cancelled automatically — even before <see cref="Start"/> is called.
|
|
/// </summary>
|
|
/// <param name="parentToken">The parent job's cancellation token.</param>
|
|
public void LinkParentToken(CancellationToken parentToken) {
|
|
_parentToken = parentToken;
|
|
}
|
|
|
|
/// <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 = _parentToken.HasValue
|
|
? CancellationTokenSource.CreateLinkedTokenSource(_parentToken.Value)
|
|
: new CancellationTokenSource();
|
|
task = Task.Run(() => TaskJob(cts.Token));
|
|
|
|
task.ContinueWith(t => {
|
|
if (t.IsCanceled && JobStatus.Status != EJobStatus.Canceled) {
|
|
JobStatus.Cancel();
|
|
}
|
|
|
|
if (t.IsFaulted && JobStatus.Status != EJobStatus.Canceled) {
|
|
JobStatus.Fail($"Unhandled exception: {t.Exception?.InnerException?.Message}");
|
|
}
|
|
|
|
switch (JobStatus.Status) {
|
|
case EJobStatus.Canceled:
|
|
Canceled?.Invoke(this, JobStatus);
|
|
break;
|
|
case EJobStatus.Failed:
|
|
Failed?.Invoke(this, JobStatus);
|
|
break;
|
|
case EJobStatus.Running:
|
|
case EJobStatus.Waiting:
|
|
case EJobStatus.Completed:
|
|
case EJobStatus.CompletedWithErrors:
|
|
Completed?.Invoke(this, JobStatus);
|
|
break;
|
|
case EJobStatus.Queued:
|
|
default:
|
|
Completed?.Invoke(this, JobStatus);
|
|
break;
|
|
}
|
|
cts?.Dispose();
|
|
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. For running jobs, signals the cancellation token. For queued jobs,
|
|
/// marks the status as Canceled and fires completion events directly.
|
|
/// </summary>
|
|
public virtual void Cancel() {
|
|
if (cts is not null) {
|
|
cts.Cancel();
|
|
} else {
|
|
JobStatus.Cancel();
|
|
Canceled?.Invoke(this, JobStatus);
|
|
Done?.Invoke(this, JobStatus);
|
|
}
|
|
}
|
|
|
|
/// <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;
|
|
|
|
/// <summary>
|
|
/// Raises the StartedWaiting event.
|
|
/// </summary>
|
|
/// <param name="e">The job status.</param>
|
|
protected virtual void OnStartedWaiting(JobStatus e) { StartedWaiting?.Invoke(this, e); }
|
|
|
|
/// <summary>
|
|
/// Raises the FinishedWaiting event.
|
|
/// </summary>
|
|
/// <param name="e">The job status.</param>
|
|
protected virtual void OnFinishedWaiting(JobStatus e) { FinishedWaiting?.Invoke(this, e); }
|
|
|
|
/// <summary>
|
|
/// Raises the ProgressChanged event.
|
|
/// </summary>
|
|
/// <param name="e">The job status.</param>
|
|
protected virtual void OnProgressChanged(JobStatus e) { ProgressChanged?.Invoke(this, e); }
|
|
}
|