perf(context)!: release retired fibers and bound diagnostic history
This commit is contained in:
@@ -20,16 +20,22 @@ namespace ShrinkContext
|
||||
{
|
||||
private readonly ShrinkCtx _rootCtx;
|
||||
private readonly List<ShrinkFiber> _fibers = new();
|
||||
private readonly Queue<ShrinkFiberDiagnostic> _retiredHistory = new();
|
||||
private readonly int _retiredHistoryCapacity;
|
||||
private readonly Dictionary<string, ShrinkBinding> _store = new();
|
||||
private readonly Dictionary<string, HashSet<ShrinkFiber>> _injectIndex =
|
||||
new(StringComparer.Ordinal);
|
||||
private readonly Queue<Action> _transitionWork = new();
|
||||
private bool _drainingTransitionWork;
|
||||
private long _uidCounter;
|
||||
private long _notificationDispatchCount;
|
||||
private long _notificationCandidateVisitCount;
|
||||
private int _lastNotificationCandidateCount;
|
||||
|
||||
public ShrinkContextRuntime()
|
||||
public ShrinkContextRuntime(int retiredHistoryCapacity = 64)
|
||||
{
|
||||
if (retiredHistoryCapacity < 0) throw new ArgumentOutOfRangeException(nameof(retiredHistoryCapacity));
|
||||
_retiredHistoryCapacity = retiredHistoryCapacity;
|
||||
_rootCtx = new ShrinkCtx(this, null, null, null);
|
||||
}
|
||||
|
||||
@@ -65,6 +71,8 @@ namespace ShrinkContext
|
||||
var instantiationHandle = new ShrinkEffectHandle();
|
||||
instantiationHandle.AttachInverse(() => RetireCoreAsync(fiber));
|
||||
parent.Attach(instantiationHandle);
|
||||
fiber.EffectOwner = parent;
|
||||
fiber.Instantiation = instantiationHandle;
|
||||
|
||||
Refresh(fiber);
|
||||
return fiber;
|
||||
@@ -77,11 +85,16 @@ namespace ShrinkContext
|
||||
public async UniTask RetireAsync(ShrinkFiber fiber)
|
||||
{
|
||||
if (fiber.Retired)
|
||||
{
|
||||
if (fiber.InTransition) await fiber.Inertia;
|
||||
await ReleaseRetiredAsync(fiber);
|
||||
return;
|
||||
}
|
||||
UnindexFiber(fiber);
|
||||
if (fiber.State == ShrinkFiberState.Inactive && !fiber.InTransition)
|
||||
{
|
||||
fiber.Retired = true;
|
||||
await ReleaseRetiredAsync(fiber);
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -89,12 +102,33 @@ namespace ShrinkContext
|
||||
fiber.Target = null;
|
||||
if (!fiber.InTransition)
|
||||
{
|
||||
fiber.InTransition = true;
|
||||
fiber.Inertia = RunTransitionAsync(fiber);
|
||||
ScheduleTransition(fiber);
|
||||
}
|
||||
|
||||
if (fiber.InTransition)
|
||||
await fiber.Inertia;
|
||||
await ReleaseRetiredAsync(fiber);
|
||||
}
|
||||
|
||||
private async UniTask ReleaseRetiredAsync(ShrinkFiber fiber)
|
||||
{
|
||||
if (fiber.InTransition || fiber.State != ShrinkFiberState.Inactive || !_fibers.Remove(fiber)) return;
|
||||
var owner = fiber.EffectOwner;
|
||||
var handle = fiber.Instantiation;
|
||||
fiber.EffectOwner = null;
|
||||
fiber.Instantiation = null;
|
||||
if (handle != null)
|
||||
{
|
||||
owner?.Detach(handle);
|
||||
await handle.DisposeAsync();
|
||||
}
|
||||
if (_retiredHistoryCapacity == 0) return;
|
||||
while (_retiredHistory.Count >= _retiredHistoryCapacity) _retiredHistory.Dequeue();
|
||||
_retiredHistory.Enqueue(new ShrinkFiberDiagnostic(fiber.Uid, fiber.Name,
|
||||
ShrinkFiberDiagnosticStatus.Retired, fiber.State, true, false,
|
||||
fiber.InjectSet.ToArray(), fiber.ProvideSet.ToArray(), Array.Empty<long>(),
|
||||
Array.Empty<ShrinkDependencyDiagnostic>(), Array.Empty<string>(),
|
||||
fiber.LastError?.GetType().FullName, fiber.LastError?.Message));
|
||||
}
|
||||
|
||||
/// <summary>卸载全部纤程并回滚根上下文效应。</summary>
|
||||
@@ -123,13 +157,47 @@ namespace ShrinkContext
|
||||
fiber.Target = target;
|
||||
if (!fiber.InTransition)
|
||||
{
|
||||
fiber.InTransition = true;
|
||||
fiber.Inertia = RunTransitionAsync(fiber);
|
||||
ScheduleTransition(fiber);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
private void ScheduleTransition(ShrinkFiber fiber)
|
||||
{
|
||||
var completion = new UniTaskCompletionSource();
|
||||
fiber.InTransition = true;
|
||||
fiber.Inertia = completion.Task.Preserve();
|
||||
EnqueueTransitionWork(() => RunScheduledTransitionAsync(fiber, completion).Forget());
|
||||
}
|
||||
|
||||
private async UniTask RunScheduledTransitionAsync(ShrinkFiber fiber, UniTaskCompletionSource completion)
|
||||
{
|
||||
try
|
||||
{
|
||||
await RunTransitionAsync(fiber);
|
||||
EnqueueTransitionWork(() => completion.TrySetResult());
|
||||
}
|
||||
catch (Exception error)
|
||||
{
|
||||
EnqueueTransitionWork(() => completion.TrySetException(error));
|
||||
}
|
||||
}
|
||||
|
||||
private void EnqueueTransitionWork(Action work)
|
||||
{
|
||||
_transitionWork.Enqueue(work);
|
||||
if (_drainingTransitionWork) return;
|
||||
_drainingTransitionWork = true;
|
||||
try
|
||||
{
|
||||
// Starting transitions and completing their awaiters both use this trampoline.
|
||||
// Deep synchronous dependency chains therefore keep a bounded CLR call stack.
|
||||
while (_transitionWork.Count > 0) _transitionWork.Dequeue()();
|
||||
}
|
||||
finally { _drainingTransitionWork = false; }
|
||||
}
|
||||
|
||||
private async UniTask RunTransitionAsync(ShrinkFiber fiber)
|
||||
{
|
||||
try
|
||||
@@ -457,8 +525,21 @@ namespace ShrinkContext
|
||||
/// <summary>捕获不含可变运行时引用的诊断快照。</summary>
|
||||
public ShrinkContextRuntimeDiagnostic CaptureDiagnostic()
|
||||
{
|
||||
var diagnostics = new List<ShrinkFiberDiagnostic>(_fibers.Count);
|
||||
foreach (var fiber in _fibers.OrderBy(item => item.Uid))
|
||||
var providers = new Dictionary<(string key, string realm), List<long>>();
|
||||
foreach (var candidate in _fibers)
|
||||
{
|
||||
if (candidate.Retired) continue;
|
||||
foreach (var key in candidate.ProvideSet)
|
||||
{
|
||||
if (!_injectIndex.ContainsKey(key)) continue;
|
||||
var id = (key, candidate.Ctx.ResolveRealm(key));
|
||||
if (!providers.TryGetValue(id, out var entries)) providers.Add(id, entries = new List<long>());
|
||||
entries.Add(candidate.Uid);
|
||||
}
|
||||
}
|
||||
var diagnostics = new List<ShrinkFiberDiagnostic>(_fibers.Count + _retiredHistory.Count);
|
||||
diagnostics.AddRange(_retiredHistory);
|
||||
foreach (var fiber in _fibers)
|
||||
{
|
||||
var dependencies = new List<ShrinkDependencyDiagnostic>();
|
||||
foreach (var key in fiber.Component.Inject ?? Array.Empty<string>())
|
||||
@@ -467,12 +548,8 @@ namespace ShrinkContext
|
||||
var current = ResolveBinding(fiber.Ctx, key);
|
||||
ShrinkBinding? committed = null;
|
||||
fiber.Committed?.TryGetValue(key, out committed);
|
||||
var potential = _fibers
|
||||
.Where(candidate => !candidate.Retired && candidate.ProvideSet.Contains(key) &&
|
||||
candidate.Ctx.ResolveRealm(key) == realm)
|
||||
.Select(candidate => candidate.Uid)
|
||||
.OrderBy(uid => uid)
|
||||
.ToArray();
|
||||
var potential = providers.TryGetValue((key, realm), out var candidates)
|
||||
? candidates.ToArray() : Array.Empty<long>();
|
||||
|
||||
dependencies.Add(new ShrinkDependencyDiagnostic(
|
||||
key,
|
||||
@@ -499,7 +576,7 @@ namespace ShrinkContext
|
||||
}
|
||||
|
||||
return new ShrinkContextRuntimeDiagnostic(
|
||||
diagnostics,
|
||||
diagnostics.OrderBy(item => item.Uid).ToArray(),
|
||||
_store.Count,
|
||||
new ShrinkNotificationDiagnostic(
|
||||
_notificationDispatchCount,
|
||||
|
||||
Reference in New Issue
Block a user