mirror of
https://github.com/chylex/Minecraft-Phantom-Panel.git
synced 2025-05-25 07:34:05 +02:00
86 lines
3.4 KiB
C#
86 lines
3.4 KiB
C#
using Phantom.Agent.Services.Instances.Procedures;
|
|
using Phantom.Common.Data.Minecraft;
|
|
using Phantom.Utils.Tasks;
|
|
using Phantom.Utils.Threading;
|
|
|
|
namespace Phantom.Agent.Services.Instances;
|
|
|
|
sealed class InstanceProcedureManager : IAsyncDisposable {
|
|
private readonly record struct CurrentProcedure(IInstanceProcedure Procedure, CancellationTokenSource CancellationTokenSource);
|
|
|
|
private readonly ThreadSafeStructRef<CurrentProcedure> currentProcedure = new ();
|
|
private readonly ThreadSafeLinkedList<IInstanceProcedure> procedureQueue = new ();
|
|
private readonly AutoResetEvent procedureQueueReady = new (false);
|
|
private readonly ManualResetEventSlim procedureQueueFinished = new (false);
|
|
|
|
private readonly Instance instance;
|
|
private readonly IInstanceContext context;
|
|
private readonly CancellationTokenSource shutdownCancellationTokenSource = new ();
|
|
|
|
public InstanceProcedureManager(Instance instance, IInstanceContext context, TaskManager taskManager) {
|
|
this.instance = instance;
|
|
this.context = context;
|
|
taskManager.Run("Procedure manager for instance " + context.ShortName, Run);
|
|
}
|
|
|
|
public async Task Enqueue(IInstanceProcedure procedure, bool immediate = false) {
|
|
await procedureQueue.Add(procedure, toFront: immediate, shutdownCancellationTokenSource.Token);
|
|
procedureQueueReady.Set();
|
|
}
|
|
|
|
public async Task<IInstanceProcedure?> GetCurrentProcedure(CancellationToken cancellationToken) {
|
|
return (await currentProcedure.Get(cancellationToken))?.Procedure;
|
|
}
|
|
|
|
private async Task Run() {
|
|
try {
|
|
var shutdownCancellationToken = shutdownCancellationTokenSource.Token;
|
|
while (true) {
|
|
await procedureQueueReady.WaitOneAsync(shutdownCancellationToken);
|
|
while (await procedureQueue.TryTakeFromFront(shutdownCancellationToken) is {} nextProcedure) {
|
|
using var procedureCancellationTokenSource = new CancellationTokenSource();
|
|
await currentProcedure.Set(new CurrentProcedure(nextProcedure, procedureCancellationTokenSource), shutdownCancellationToken);
|
|
await RunProcedure(nextProcedure, procedureCancellationTokenSource.Token);
|
|
await currentProcedure.Set(null, shutdownCancellationToken);
|
|
}
|
|
}
|
|
} catch (OperationCanceledException) {
|
|
// Ignore.
|
|
}
|
|
|
|
await RunProcedure(new StopInstanceProcedure(MinecraftStopStrategy.Instant), CancellationToken.None);
|
|
procedureQueueFinished.Set();
|
|
}
|
|
|
|
private async Task RunProcedure(IInstanceProcedure procedure, CancellationToken cancellationToken) {
|
|
var procedureName = procedure.GetType().Name;
|
|
|
|
context.Logger.Debug("Started procedure: {Procedure}", procedureName);
|
|
try {
|
|
var newState = await procedure.Run(context, cancellationToken);
|
|
context.Logger.Debug("Finished procedure: {Procedure}", procedureName);
|
|
|
|
if (newState != null) {
|
|
instance.TransitionState(newState);
|
|
}
|
|
} catch (OperationCanceledException) {
|
|
context.Logger.Debug("Cancelled procedure: {Procedure}", procedureName);
|
|
} catch (Exception e) {
|
|
context.Logger.Error(e, "Caught exception while running procedure: {Procedure}", procedureName);
|
|
}
|
|
}
|
|
|
|
public async ValueTask DisposeAsync() {
|
|
shutdownCancellationTokenSource.Cancel();
|
|
|
|
(await currentProcedure.Get(CancellationToken.None))?.CancellationTokenSource.Cancel();
|
|
await procedureQueueFinished.WaitHandle.WaitOneAsync();
|
|
|
|
currentProcedure.Dispose();
|
|
procedureQueue.Dispose();
|
|
procedureQueueReady.Dispose();
|
|
procedureQueueFinished.Dispose();
|
|
shutdownCancellationTokenSource.Dispose();
|
|
}
|
|
}
|