fix(plugins): close LA5 ownership races
This commit is contained in:
parent
fbe9c8a288
commit
f820eb258d
14 changed files with 467 additions and 107 deletions
|
|
@ -52,8 +52,9 @@ internal sealed class HeadlessPluginHost
|
|||
public ISelectionService Selection => _runtime.ActionOwner.Selection;
|
||||
public IUiRegistry Ui => NoOpUiRegistry.Instance;
|
||||
|
||||
/// <summary>Test-only barrier after the borrowed replay snapshot is
|
||||
/// captured and before delivery starts.</summary>
|
||||
/// <summary>Test-only barrier invoked after the first replay item is
|
||||
/// captured while Runtime's exact active-membership read lease is still
|
||||
/// held.</summary>
|
||||
internal Action? ReplayCapturedForTest { get; set; }
|
||||
|
||||
/// <summary>
|
||||
|
|
@ -88,11 +89,12 @@ internal sealed class HeadlessPluginHost
|
|||
// Arm the pending queue before borrowing Runtime's snapshot. This
|
||||
// avoids a host-lock/Runtime-lock inversion while the identity
|
||||
// dedup below collapses any registration present in both views.
|
||||
var visitor = new SnapshotVisitor(_runtime);
|
||||
var visitor = new SnapshotVisitor(
|
||||
_runtime,
|
||||
ReplayCapturedForTest);
|
||||
_runtime.Entities.Visit(visitor);
|
||||
ReplayEntity[] replay = visitor.Items.ToArray();
|
||||
|
||||
ReplayCapturedForTest?.Invoke();
|
||||
foreach (ReplayEntity item in replay)
|
||||
{
|
||||
lock (_eventGate)
|
||||
|
|
@ -234,16 +236,23 @@ internal sealed class HeadlessPluginHost
|
|||
catch { }
|
||||
}
|
||||
|
||||
private sealed class SnapshotVisitor(GameRuntime runtime)
|
||||
private sealed class SnapshotVisitor(
|
||||
GameRuntime runtime,
|
||||
Action? captureBarrier = null)
|
||||
: IRuntimeEntityVisitor
|
||||
{
|
||||
private Action? _captureBarrier = captureBarrier;
|
||||
|
||||
internal List<ReplayEntity> Items { get; } =
|
||||
new(runtime.Entities.Count);
|
||||
|
||||
public void Visit(in RuntimeEntitySnapshot entity) =>
|
||||
public void Visit(in RuntimeEntitySnapshot entity)
|
||||
{
|
||||
Items.Add(new ReplayEntity(
|
||||
entity.Identity,
|
||||
Convert(runtime, entity)));
|
||||
Interlocked.Exchange(ref _captureBarrier, null)?.Invoke();
|
||||
}
|
||||
}
|
||||
|
||||
private void RebuildLiveSnapshotLocked()
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue