fix(launcher): harden updater crash recovery
This commit is contained in:
parent
2d2a5b5046
commit
1955ca8ab5
27 changed files with 2714 additions and 544 deletions
|
|
@ -55,6 +55,42 @@ public sealed class LauncherOrchestratorTests : IDisposable
|
|||
using UpdateSessionBarrier.ExclusiveLease update = barrier.AcquireExclusive();
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task DisposeKeepsUpdateLeaseUntilLiveChildIsObservedTerminal()
|
||||
{
|
||||
var supervisors = new BlockingStopSupervisorFactory();
|
||||
var barrier = new UpdateSessionBarrier(_paths.DataDirectory);
|
||||
LauncherOrchestrator orchestrator = CreateOrchestrator(
|
||||
supervisorFactory: supervisors,
|
||||
updateSessionBarrier: barrier);
|
||||
try
|
||||
{
|
||||
_ = await orchestrator.LaunchAsync(
|
||||
"Local ACE",
|
||||
"testaccount",
|
||||
"+Acdream",
|
||||
LaunchMode.Headless);
|
||||
BlockingStopSupervisor supervisor = Assert.Single(supervisors.Created);
|
||||
|
||||
Task disposal = Task.Run(orchestrator.Dispose);
|
||||
Assert.True(supervisor.StopEntered.Wait(TimeSpan.FromSeconds(5)));
|
||||
|
||||
Assert.False(disposal.IsCompleted);
|
||||
Assert.Throws<LauncherUpdateException>(barrier.AcquireExclusive);
|
||||
|
||||
supervisor.AllowTerminal.Set();
|
||||
await disposal.WaitAsync(TimeSpan.FromSeconds(5));
|
||||
using UpdateSessionBarrier.ExclusiveLease update = barrier.AcquireExclusive();
|
||||
Assert.Equal(LauncherSessionState.Exited, supervisor.State);
|
||||
Assert.True(supervisor.Disposed);
|
||||
}
|
||||
finally
|
||||
{
|
||||
supervisors.AllowEveryStop();
|
||||
orchestrator.Dispose();
|
||||
}
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void SnapshotProjectsTheFullHierarchyWithoutTheCredential()
|
||||
{
|
||||
|
|
@ -756,6 +792,59 @@ public sealed class LauncherOrchestratorTests : IDisposable
|
|||
}
|
||||
}
|
||||
|
||||
private sealed class BlockingStopSupervisorFactory : ILauncherProcessSupervisorFactory
|
||||
{
|
||||
public List<BlockingStopSupervisor> Created { get; } = [];
|
||||
|
||||
public ILauncherProcessSupervisor Create()
|
||||
{
|
||||
var supervisor = new BlockingStopSupervisor();
|
||||
Created.Add(supervisor);
|
||||
return supervisor;
|
||||
}
|
||||
|
||||
public void AllowEveryStop()
|
||||
{
|
||||
foreach (BlockingStopSupervisor supervisor in Created)
|
||||
{
|
||||
supervisor.AllowTerminal.Set();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private sealed class BlockingStopSupervisor : ILauncherProcessSupervisor
|
||||
{
|
||||
public ManualResetEventSlim StopEntered { get; } = new(false);
|
||||
|
||||
public ManualResetEventSlim AllowTerminal { get; } = new(false);
|
||||
|
||||
public LauncherSessionState State { get; private set; } =
|
||||
LauncherSessionState.Starting;
|
||||
|
||||
public int? ExitCode { get; private set; }
|
||||
|
||||
public bool Disposed { get; private set; }
|
||||
|
||||
public event EventHandler<LauncherSessionState>? StateChanged;
|
||||
|
||||
public void Start(LauncherProcessSpec spec, string? password)
|
||||
{
|
||||
State = LauncherSessionState.Running;
|
||||
StateChanged?.Invoke(this, State);
|
||||
}
|
||||
|
||||
public void Stop(TimeSpan timeout)
|
||||
{
|
||||
StopEntered.Set();
|
||||
AllowTerminal.Wait();
|
||||
State = LauncherSessionState.Exited;
|
||||
ExitCode = 0;
|
||||
StateChanged?.Invoke(this, State);
|
||||
}
|
||||
|
||||
public void Dispose() => Disposed = true;
|
||||
}
|
||||
|
||||
private sealed class QueueStatusSourceFactory : IStatusEventSourceFactory
|
||||
{
|
||||
public List<QueueStatusSource> Created { get; } = [];
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue