perf(context)!: release retired fibers and bound diagnostic history
Publish UPM package / publish (push) Successful in 2s
Publish NuGet packages / publish (push) Successful in 1m29s

This commit is contained in:
2026-09-29 10:14:41 +08:00
parent b93f3e6122
commit d3eca1f155
8 changed files with 220 additions and 19 deletions
+91 -14
View File
@@ -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,