refactor(app): own native window callback lifetime

Publish the fixed Silk callback binding before acquisition, quiesce admitted callbacks before teardown, and retain failed physical detach ownership for retry. Preserve the frozen callback order while covering partial event accessors, concurrency, reentrancy, and shutdown completion.

Co-authored-by: Codex <codex@openai.com>
This commit is contained in:
Erik 2026-07-22 09:59:10 +02:00
parent 476c2e6de1
commit 18d4b999de
7 changed files with 1416 additions and 50 deletions

View file

@ -22,31 +22,25 @@ public sealed class GameWindowSlice8BoundaryTests
"Samples = startupQuality.MsaaSamples",
"Window.Create(options)",
"_displayFramePacing.BindSurface(",
"_window.Load += OnLoad;",
"_window.Update += OnUpdate;",
"_window.Render += OnRender;",
"_window.Render += _displayFramePacing.OnFrameRendered;",
"_window.Closing += OnClosing;",
"_window.FocusChanged += OnFocusChanged;",
"_window.Move += _displayFramePacing.OnWindowMoved;",
"_window.StateChanged += _displayFramePacing.OnWindowStateChanged;",
"_window.FramebufferResize += OnFramebufferResize;",
"_windowCallbacks = SilkWindowCallbackBinding.Create(",
"new WindowCallbackTargets(",
"OnLoad,",
"OnUpdate,",
"OnRender,",
"OnClosing,",
"OnFocusChanged,",
"OnFramebufferResize),",
"_displayFramePacing,",
"_hostQuiescence);",
"_windowCallbacks.Attach();",
"_window.Run();");
string[] registrations =
[
"_window.Load += OnLoad;",
"_window.Update += OnUpdate;",
"_window.Render += OnRender;",
"_window.Render += _displayFramePacing.OnFrameRendered;",
"_window.Closing += OnClosing;",
"_window.FocusChanged += OnFocusChanged;",
"_window.Move += _displayFramePacing.OnWindowMoved;",
"_window.StateChanged += _displayFramePacing.OnWindowStateChanged;",
"_window.FramebufferResize += OnFramebufferResize;",
];
foreach (string registration in registrations)
Assert.Equal(1, CountOccurrences(body, registration));
Assert.Equal(1, CountOccurrences(body, "SilkWindowCallbackBinding.Create("));
Assert.Equal(1, CountOccurrences(body, "_windowCallbacks.Attach();"));
Assert.DoesNotContain("_window.Load +=", body, StringComparison.Ordinal);
Assert.DoesNotContain("_window.Update +=", body, StringComparison.Ordinal);
Assert.DoesNotContain("_window.Render +=", body, StringComparison.Ordinal);
Assert.DoesNotContain("_window.Closing +=", body, StringComparison.Ordinal);
}
[Fact]
@ -160,7 +154,11 @@ public sealed class GameWindowSlice8BoundaryTests
"new AcDream.App.Rendering.RenderFrameInput(");
Assert.DoesNotContain("FramebufferSize", render, StringComparison.Ordinal);
AssertAppearsInOrder(focus, "if (!focused)", "_gameplayInputFrame?.EndMouseLook();");
AssertAppearsInOrder(close, "=> CompleteShutdown();", "_shutdown.CompleteOrThrow();");
AssertAppearsInOrder(
close,
"_hostQuiescence.StopAccepting();",
"CompleteShutdown();",
"_shutdown.CompleteOrThrow();");
}
[Fact]
@ -197,6 +195,11 @@ public sealed class GameWindowSlice8BoundaryTests
Assert.Equal(stages.Length, CountOccurrences(manifest, "new ResourceShutdownStage("));
foreach (string stage in stages)
Assert.Equal(1, CountOccurrences(manifest, stage));
AssertAppearsInOrder(
manifest,
"binding.Dispose();",
"if (!binding.IsDisposalComplete)",
"_windowCallbacks = null;");
AssertAppearsInOrder(
dispose,
"CompleteShutdown();",