using API.Schema; using log4net; namespace API.Workers; public abstract class BaseWorker : Identifiable { /// /// Workers this Worker depends on being completed before running. /// public BaseWorker[] DependsOn { get; init; } /// /// Dependencies and dependencies of dependencies. See also . /// public IEnumerable AllDependencies => DependsOn.Select(d => d.AllDependencies).SelectMany(x => x); /// /// and Self. /// public IEnumerable DependenciesAndSelf => AllDependencies.Append(this); /// /// where is less than Completed. /// public IEnumerable MissingDependencies => DependsOn.Where(d => d.State < WorkerExecutionState.Completed); public bool AllDependenciesFulfilled => DependsOn.All(d => d.State >= WorkerExecutionState.Completed); internal WorkerExecutionState State { get; private set; } private static readonly CancellationTokenSource CancellationTokenSource = new(TimeSpan.FromMinutes(10)); protected ILog Log { get; init; } /// /// Stops worker, and marks as .Cancelled /// public void Cancel() { this.State = WorkerExecutionState.Cancelled; CancellationTokenSource.Cancel(); } /// /// Stops worker, and marks as .Failed /// protected void Fail() { this.State = WorkerExecutionState.Failed; CancellationTokenSource.Cancel(); } public BaseWorker(IEnumerable? dependsOn = null) { this.DependsOn = dependsOn?.ToArray() ?? []; this.Log = LogManager.GetLogger(GetType()); } /// /// Sets States during worker-run. /// States: /// /// .Waiting when waiting for /// .Running when running /// .Completed after finished /// /// /// /// /// If has , missing dependencies. /// If are .Running, itself after waiting for dependencies. /// If has run, additional . /// /// public Task DoWork() { this.State = WorkerExecutionState.Waiting; BaseWorker[] missingDependenciesThatNeedStarting = MissingDependencies.Where(d => d.State < WorkerExecutionState.Waiting).ToArray(); if(missingDependenciesThatNeedStarting.Any()) return new Task(() => missingDependenciesThatNeedStarting); if (MissingDependencies.Any()) return new Task(WaitForDependencies); Task task = new (DoWorkInternal, CancellationTokenSource.Token); task.GetAwaiter().OnCompleted(() => this.State = WorkerExecutionState.Completed); task.Start(); this.State = WorkerExecutionState.Running; return task; } protected abstract BaseWorker[] DoWorkInternal(); private BaseWorker[] WaitForDependencies() { while (CancellationTokenSource.IsCancellationRequested == false && MissingDependencies.Any()) { Thread.Sleep(TrangaSettings.workCycleTimeout); } return [this]; } } public enum WorkerExecutionState { Failed = 0, Cancelled = 32, Created = 64, Waiting = 96, Running = 128, Completed = 192 }