test(streaming): close lifecycle gate after cutover

This commit is contained in:
Erik 2026-07-21 23:41:15 +02:00
parent db3ca59fd0
commit 4a205a3e56
7 changed files with 173 additions and 17 deletions

View file

@ -259,6 +259,11 @@ public sealed class LiveSessionController : IDisposable
get { lock (_gate) return _generation; }
}
internal bool IsDisposalComplete
{
get { lock (_gate) return _disposed; }
}
internal LiveSessionStartResult Start(
RuntimeOptions options,
ILiveSessionLifecycleHost host)

View file

@ -8606,7 +8606,30 @@ public sealed class GameWindow : IDisposable, ILiveAnimationPresentationContext
}
private ResourceShutdownTransaction CreateShutdownTransaction() => new(
new ResourceShutdownStage("session owners",
// Live-session reset retires the complete streaming window. Every
// reset callback owner, especially LandblockStreamer, must remain live
// until that transaction converges.
new ResourceShutdownStage("session lifetime",
[
new("live session", () =>
{
AcDream.App.Net.LiveSessionController? controller =
_liveSessionController;
if (controller is null)
return;
controller.Dispose();
if (!controller.IsDisposalComplete)
{
throw new InvalidOperationException(
"Live-session disposal was deferred by a reentrant lifecycle callback.");
}
if (ReferenceEquals(_liveSessionController, controller))
_liveSessionController = null;
}),
]),
new ResourceShutdownStage("session dependents",
[
new("mouse capture", EndMouseLookAndRestoreCursor),
new("retail UI", () =>
@ -8641,11 +8664,6 @@ public sealed class GameWindow : IDisposable, ILiveAnimationPresentationContext
_magicCatalog = null;
}),
new("streamer", () => _streamer?.Dispose()),
new("live session", () =>
{
_liveSessionController?.Dispose();
_liveSessionController = null;
}),
new("equipped children", () => _equippedChildRenderer?.Dispose()),
]),
// Retained tombstones are retried while every callback owner is alive.