refactor(app): complete session startup composition
Move the live-session reset and routing graph, combat and diagnostic command targets, and the sole gameplay input subscriber into Phase 7 before frame publication. Add exact retryable ownership for late bindings so partial startup cannot strand session or component teardown edges. Co-authored-by: Codex <codex@openai.com>
This commit is contained in:
parent
7fa60971e2
commit
826f9ea9b5
22 changed files with 924 additions and 412 deletions
|
|
@ -10,7 +10,9 @@ namespace AcDream.App.Composition;
|
|||
/// </summary>
|
||||
internal sealed class LivePresentationRuntimeBindings : IDisposable
|
||||
{
|
||||
private readonly List<(string Name, IDisposable Binding)> _bindings = [];
|
||||
private sealed record Entry(string Name, IDisposable Binding);
|
||||
|
||||
private readonly List<Entry> _bindings = [];
|
||||
private bool _deactivationStarted;
|
||||
|
||||
public void Adopt(string name, IDisposable binding)
|
||||
|
|
@ -18,7 +20,17 @@ internal sealed class LivePresentationRuntimeBindings : IDisposable
|
|||
ArgumentException.ThrowIfNullOrWhiteSpace(name);
|
||||
ArgumentNullException.ThrowIfNull(binding);
|
||||
ObjectDisposedException.ThrowIf(_deactivationStarted, this);
|
||||
_bindings.Add((name, binding));
|
||||
_bindings.Add(new Entry(name, binding));
|
||||
}
|
||||
|
||||
public IDisposable AdoptOwned(string name, IDisposable binding)
|
||||
{
|
||||
ArgumentException.ThrowIfNullOrWhiteSpace(name);
|
||||
ArgumentNullException.ThrowIfNull(binding);
|
||||
ObjectDisposedException.ThrowIf(_deactivationStarted, this);
|
||||
var entry = new Entry(name, binding);
|
||||
_bindings.Add(entry);
|
||||
return new Adoption(this, entry);
|
||||
}
|
||||
|
||||
public void AdoptRelease(string name, Action release) =>
|
||||
|
|
@ -56,16 +68,16 @@ internal sealed class LivePresentationRuntimeBindings : IDisposable
|
|||
List<Exception>? failures = null;
|
||||
for (int i = _bindings.Count - 1; i >= 0; i--)
|
||||
{
|
||||
(string name, IDisposable binding) = _bindings[i];
|
||||
Entry entry = _bindings[i];
|
||||
try
|
||||
{
|
||||
binding.Dispose();
|
||||
entry.Binding.Dispose();
|
||||
_bindings.RemoveAt(i);
|
||||
}
|
||||
catch (Exception failure)
|
||||
{
|
||||
(failures ??= []).Add(new InvalidOperationException(
|
||||
$"Live-presentation binding '{name}' did not detach.",
|
||||
$"Live-presentation binding '{entry.Name}' did not detach.",
|
||||
failure));
|
||||
}
|
||||
}
|
||||
|
|
@ -78,6 +90,38 @@ internal sealed class LivePresentationRuntimeBindings : IDisposable
|
|||
}
|
||||
}
|
||||
|
||||
private void ReleaseAdoption(Entry expected)
|
||||
{
|
||||
int index = _bindings.IndexOf(expected);
|
||||
if (index < 0)
|
||||
return;
|
||||
expected.Binding.Dispose();
|
||||
_bindings.RemoveAt(index);
|
||||
}
|
||||
|
||||
private sealed class Adoption : IDisposable
|
||||
{
|
||||
private LivePresentationRuntimeBindings? _owner;
|
||||
private readonly Entry _expected;
|
||||
|
||||
public Adoption(
|
||||
LivePresentationRuntimeBindings owner,
|
||||
Entry expected)
|
||||
{
|
||||
_owner = owner;
|
||||
_expected = expected;
|
||||
}
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
LivePresentationRuntimeBindings? owner = _owner;
|
||||
if (owner is null)
|
||||
return;
|
||||
owner.ReleaseAdoption(_expected);
|
||||
_owner = null;
|
||||
}
|
||||
}
|
||||
|
||||
private sealed class DelegateBinding : IDisposable
|
||||
{
|
||||
private Action? _release;
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue