Files
Workspace/Assets/Modules/ShrinkContext.Core/Runtime/ShrinkContextRuntime.cs
T

366 lines
13 KiB
C#
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
#nullable enable
using System;
using System.Collections.Generic;
using System.Linq;
using Cysharp.Threading.Tasks;
namespace ShrinkContext
{
/// <summary>
/// Cordis 核心原型运行时:纤程注册表 + 共享余效应存储 + 惯性生命周期状态机
/// (论文算法 2-5 的 C#/UniTask 移植)。
///
/// 约定:
/// - 仅在主线程使用;
/// - 异步转换在同步可完成路径上内联跑完(无需 PlayerLoop 泵即可确定性测试);
/// - 目标(Target)以提供者 uid 集合表示,uid 永不复用,因此“同值不同提供者”的替换也会触发重载。
/// </summary>
public sealed class ShrinkContextRuntime
{
private readonly ShrinkCtx _rootCtx;
private readonly List<ShrinkFiber> _fibers = new();
private readonly Dictionary<string, ShrinkBinding> _store = new();
private long _uidCounter;
public ShrinkContextRuntime()
{
_rootCtx = new ShrinkCtx(this, null, null, null);
}
public ShrinkCtx RootContext => _rootCtx;
public IReadOnlyList<ShrinkFiber> Fibers => _fibers;
// ---------------- 实例化(论文算法 4 ----------------
/// <summary>
/// 实例化组件为纤程:在父上下文(默认根上下文)上注册一个可逆效应,
/// 前向 = 启动子纤程生命周期,逆 = 强制子纤程目标为 ⊥ 并卸载——父卸载自动级联子卸载。
/// isolate 为该纤程自身的键解析叠加隔离域(不影响父上下文)。
/// </summary>
public ShrinkFiber Use(IShrinkComponent component, object? config = null, ShrinkCtx? parentCtx = null,
IReadOnlyDictionary<string, string>? 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;
}
/// <summary>
/// 退役并卸载纤程(论文 O-Retire):目标强制 ⊥,等待纤程到达静止态。
/// 退役是永久的:之后不再参与依赖通知。
/// </summary>
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;
}
/// <summary>卸载全部纤程并回滚根上下文效应。</summary>
public async UniTask ShutdownAsync()
{
foreach (var fiber in _fibers.ToArray())
await RetireAsync(fiber);
await _rootCtx.DisposeAsync();
}
// ---------------- 生命周期(论文算法 5 ----------------
/// <summary>
/// 重算目标:任一依赖键不可解析 → ⊥;否则为全部提供者 uid 的有序集合。
/// 目标变化即启动(或在惯性边界链式触发)reload / unload 转换。
/// </summary>
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<bool> 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 ----------------
/// <summary>
/// 将键变更传播给声明了该键、且 realm 解析一致的依赖纤程;
/// 返回本次被触发刷新的纤程(供卸载方等待)。
/// </summary>
internal IReadOnlyList<ShrinkFiber> Notify(ShrinkCtx sourceCtx, IReadOnlyCollection<string> keys)
{
var affected = new List<ShrinkFiber>();
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;
}
/// <summary>解析绑定:realm 不存在、或提供者非 Active(正在装载/卸载/失活)时视为不可用。</summary>
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;
}
/// <summary>绕过介导规则的原始查找(编排层/测试用):active 提供者或无主绑定才命中。</summary>
public bool TryGetRaw<T>(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<long>();
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<string, ShrinkBinding> ResolveView(ShrinkFiber fiber)
{
var view = new Dictionary<string, ShrinkBinding>();
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);
}
}
}