#nullable enable using System; using System.Collections.Generic; using System.Linq; using Cysharp.Threading.Tasks; namespace ShrinkContext { /// /// Cordis 核心原型运行时:纤程注册表 + 共享余效应存储 + 惯性生命周期状态机 /// (论文算法 2-5 的 C#/UniTask 移植)。 /// /// 约定: /// - 仅在主线程使用; /// - 异步转换在同步可完成路径上内联跑完(无需 PlayerLoop 泵即可确定性测试); /// - 目标(Target)以提供者 uid 集合表示,uid 永不复用,因此“同值不同提供者”的替换也会触发重载。 /// public sealed class ShrinkContextRuntime { private readonly ShrinkCtx _rootCtx; private readonly List _fibers = new(); private readonly Dictionary _store = new(); private long _uidCounter; public ShrinkContextRuntime() { _rootCtx = new ShrinkCtx(this, null, null, null); } public ShrinkCtx RootContext => _rootCtx; public IReadOnlyList Fibers => _fibers; // ---------------- 实例化(论文算法 4) ---------------- /// /// 实例化组件为纤程:在父上下文(默认根上下文)上注册一个可逆效应, /// 前向 = 启动子纤程生命周期,逆 = 强制子纤程目标为 ⊥ 并卸载——父卸载自动级联子卸载。 /// isolate 为该纤程自身的键解析叠加隔离域(不影响父上下文)。 /// public ShrinkFiber Use(IShrinkComponent component, object? config = null, ShrinkCtx? parentCtx = null, IReadOnlyDictionary? isolate = null) { if (component == null) throw new ArgumentNullException(nameof(component)); var parent = parentCtx ?? _rootCtx; var fiber = new ShrinkFiber(++_uidCounter, component, parent.OwnerFiber, config); var fiberCtx = parent.CreateChildForFiber(fiber); if (isolate != null && isolate.Count > 0) fiberCtx = fiberCtx.WithIsolate(isolate); fiber.Ctx = fiberCtx; _fibers.Add(fiber); var instantiationHandle = new ShrinkEffectHandle(); instantiationHandle.AttachInverse(() => RetireCoreAsync(fiber)); parent.Attach(instantiationHandle); Refresh(fiber); return fiber; } /// /// 退役并卸载纤程(论文 O-Retire):目标强制 ⊥,等待纤程到达静止态。 /// 退役是永久的:之后不再参与依赖通知。 /// public async UniTask RetireAsync(ShrinkFiber fiber) { if (fiber.Retired) return; if (fiber.State == ShrinkFiberState.Inactive && !fiber.InTransition) { fiber.Retired = true; return; } fiber.Retired = true; fiber.Target = null; if (!fiber.InTransition) { fiber.InTransition = true; fiber.Inertia = RunTransitionAsync(fiber); } if (fiber.InTransition) await fiber.Inertia; } /// 卸载全部纤程并回滚根上下文效应。 public async UniTask ShutdownAsync() { foreach (var fiber in _fibers.ToArray()) await RetireAsync(fiber); await _rootCtx.DisposeAsync(); } // ---------------- 生命周期(论文算法 5) ---------------- /// /// 重算目标:任一依赖键不可解析 → ⊥;否则为全部提供者 uid 的有序集合。 /// 目标变化即启动(或在惯性边界链式触发)reload / unload 转换。 /// internal bool Refresh(ShrinkFiber fiber) { if (fiber.Retired) return false; var target = ComputeTarget(fiber); if (TargetEquals(fiber.Target, target)) return false; fiber.Target = target; if (!fiber.InTransition) { fiber.InTransition = true; fiber.Inertia = RunTransitionAsync(fiber); } return true; } private async UniTask RunTransitionAsync(ShrinkFiber fiber) { try { while (true) { if (fiber.Target is null) { // L-Leave:先停止供给(状态置 Unloading),再调度任何逆操作 fiber.State = ShrinkFiberState.Unloading; await UnloadCoreAsync(fiber); if (fiber.Target is null) { fiber.State = ShrinkFiberState.Inactive; return; } // 卸载中途目标恢复 → 链式重载(惯性) continue; } fiber.State = ShrinkFiberState.Loading; var stable = await ReloadCoreAsync(fiber); if (stable) { fiber.State = ShrinkFiberState.Active; fiber.EverActive = true; fiber.LastError = null; Notify(fiber.Ctx, fiber.Component.Provide); return; } // 装载中途目标变化(⊥ 或换提供者)→ 链式卸载(惯性) } } finally { fiber.InTransition = false; } } private async UniTask ReloadCoreAsync(ShrinkFiber fiber) { var target0 = fiber.Target!; // 提交视图:装载期间(含自身卸载过程)读取同一份绑定(定理 63) fiber._committed = ResolveView(fiber); try { if (fiber.Component is IShrinkIterativeComponent iterative) { var stepper = iterative.ApplySteps(fiber.Ctx, fiber.Config); // 守卫在每步步进边界检查目标稳定性(算法 1 的 guard 循环) while (TargetEquals(fiber.Target, target0)) { if (!await stepper.MoveNextAsync()) break; var handle = new ShrinkEffectHandle(); handle.AttachInverse(stepper.Current); fiber.Ctx.Attach(handle); } } else { await fiber.Component.ApplyAsync(fiber.Ctx, fiber.Config); } } catch (Exception ex) { // L-Raise:记录错误并强制走卸载路径,回收已执行的部分效应 fiber.LastError = ex; fiber.Target = null; return false; } return TargetEquals(fiber.Target, target0); } private async UniTask UnloadCoreAsync(ShrinkFiber fiber) { // 先通知依赖者并等待它们到达静止态(drain),再运行自身的逆操作(L-Unload 守卫) var affected = Notify(fiber.Ctx, fiber.Component.Provide); foreach (var dependent in affected) { if (dependent.InTransition) await dependent.Inertia; } await fiber.Ctx.DisposeAsync(); // 卸载完成后才丢弃提交视图,保证自身拆除期间仍可读取依赖 fiber._committed = null; } // ---------------- 通知(论文算法 3) ---------------- /// /// 将键变更传播给声明了该键、且 realm 解析一致的依赖纤程; /// 返回本次被触发刷新的纤程(供卸载方等待)。 /// internal IReadOnlyList Notify(ShrinkCtx sourceCtx, IReadOnlyCollection keys) { var affected = new List(); foreach (var fiber in _fibers.ToArray()) { foreach (var key in keys) { if (!fiber.InjectSet.Contains(key)) continue; if (fiber.Ctx.ResolveRealm(key) != sourceCtx.ResolveRealm(key)) continue; if (Refresh(fiber)) affected.Add(fiber); break; } } return affected; } // ---------------- 余效应存储(论文算法 2) ---------------- internal ShrinkEffectHandle SetBinding(ShrinkCtx ctx, string key, object? value) { var realm = ctx.ResolveRealm(key); var setter = ctx.OwnerFiber; InstallBinding(realm, key, value, setter, ctx); var handle = new ShrinkEffectHandle(); handle.AttachInverse(() => { RemoveBinding(realm, key, setter, ctx); return UniTask.CompletedTask; }); ctx.Attach(handle); return handle; } private void InstallBinding(string realm, string key, object? value, ShrinkFiber? setter, ShrinkCtx ctx) { if (_store.TryGetValue(realm, out var existing) && !IsTakeoverAllowed(existing, setter)) throw new ShrinkSupplyConflictException(key, existing.Provider, setter); _store[realm] = new ShrinkBinding(value, setter, key); Notify(ctx, new[] { key }); } private void RemoveBinding(string realm, string key, ShrinkFiber? setter, ShrinkCtx ctx) { // 只撤回仍归属本设置者的绑定,不误删后继提供者 if (_store.TryGetValue(realm, out var current) && current.Provider == setter) _store.Remove(realm); Notify(ctx, new[] { key }); } private static bool IsTakeoverAllowed(ShrinkBinding existing, ShrinkFiber? setter) { if (existing.Provider == null || existing.Provider == setter) return true; var state = existing.Provider.State; return state == ShrinkFiberState.Unloading || state == ShrinkFiberState.Inactive; } /// 解析绑定:realm 不存在、或提供者非 Active(正在装载/卸载/失活)时视为不可用。 internal ShrinkBinding? ResolveBinding(ShrinkCtx ctx, string key) { var realm = ctx.ResolveRealm(key); if (!_store.TryGetValue(realm, out var binding)) return null; if (binding.Provider != null && binding.Provider.State != ShrinkFiberState.Active) return null; return binding; } /// 绕过介导规则的原始查找(编排层/测试用):active 提供者或无主绑定才命中。 public bool TryGetRaw(ShrinkCtx ctx, string key, out T value) { value = default!; var binding = ResolveBinding(ctx, key); if (binding == null) return false; value = (T)binding.Value!; return true; } // ---------------- 目标计算 ---------------- private long[]? ComputeTarget(ShrinkFiber fiber) { var uids = new List(); foreach (var key in fiber.Component.Inject) { var binding = ResolveBinding(fiber.Ctx, key); if (binding == null) return null; uids.Add(binding.Provider?.Uid ?? 0); } return uids.Distinct().OrderBy(uid => uid).ToArray(); } private Dictionary ResolveView(ShrinkFiber fiber) { var view = new Dictionary(); foreach (var key in fiber.Component.Inject) { var binding = ResolveBinding(fiber.Ctx, key); if (binding != null) view[key] = binding; } return view; } private static bool TargetEquals(long[]? a, long[]? b) { if (a == null || b == null) return a == null && b == null; if (a.Length != b.Length) return false; for (var i = 0; i < a.Length; i++) { if (a[i] != b[i]) return false; } return true; } private async UniTask RetireCoreAsync(ShrinkFiber fiber) { if (fiber.Retired) return; await RetireAsync(fiber); } } }