using AcDream.App.Rendering; namespace AcDream.App.Tests.Rendering; public sealed class HostQuiescenceGateTests { [Fact] public async Task ExternalStopWaitsForAdmittedCallbackToReturn() { var gate = new HostQuiescenceGate(); using var callbackEntered = new ManualResetEventSlim(false); using var releaseCallback = new ManualResetEventSlim(false); int calls = 0; Task callback = Task.Run(() => gate.Invoke(() => { callbackEntered.Set(); Assert.True(releaseCallback.Wait(TimeSpan.FromSeconds(5))); calls++; })); Assert.True(callbackEntered.Wait(TimeSpan.FromSeconds(5))); using var stopStarted = new ManualResetEventSlim(false); Exception? stopError = null; var stopThread = new Thread(() => { stopStarted.Set(); try { gate.StopAccepting(); } catch (Exception error) { stopError = error; } }) { IsBackground = true, Name = "HostQuiescenceGate external stop contract", }; stopThread.Start(); bool stopStartedInTime = stopStarted.Wait(TimeSpan.FromSeconds(5)); bool stopBlockedOnCallback = stopStartedInTime && SpinWait.SpinUntil( () => (stopThread.ThreadState & ThreadState.WaitSleepJoin) != 0, TimeSpan.FromSeconds(5)); releaseCallback.Set(); await callback; bool stopJoined = stopThread.Join(TimeSpan.FromSeconds(5)); gate.Invoke(() => calls++); Assert.True(stopStartedInTime, "the dedicated stop thread did not start"); Assert.True(stopBlockedOnCallback, "stop never waited for the admitted callback"); Assert.True(stopJoined, "stop did not finish after the callback returned"); Assert.Null(stopError); Assert.Equal(1, calls); Assert.False(gate.IsAccepting); } [Fact] public void CallbackCanStopGateReentrantlyWithoutDeadlock() { var gate = new HostQuiescenceGate(); int calls = 0; gate.Invoke(() => { calls++; gate.StopAccepting(); }); gate.Invoke(() => calls++); Assert.Equal(1, calls); Assert.False(gate.IsAccepting); } }