#nullable enable using System; using System.Collections.Generic; using Cysharp.Threading.Tasks; namespace ShrinkContext { /// /// 一等上下文(论文 Γ∞ 的运行时对应物)。 /// - 树结构:由 派生隔离子上下文,或由运行时为纤程派生子上下文; /// - 效应:本上下文注册的可逆效应按注册逆序(LIFO)在 中回滚; /// - 余效应:Set/Get 委托给运行时共享存储,键先经隔离表解析为 realm 再查表。 /// public sealed class ShrinkCtx { private readonly List _effects = new(); private readonly Dictionary? _isolateOverlay; private Dictionary>? _interceptOverlay; internal ShrinkCtx(ShrinkContextRuntime runtime, ShrinkCtx? parent, ShrinkFiber? ownerFiber, Dictionary? isolateOverlay, IReadOnlyDictionary>? interceptOverlay = null) { Runtime = runtime; Parent = parent; OwnerFiber = ownerFiber; _isolateOverlay = isolateOverlay; ReplaceIntercept(interceptOverlay); } public ShrinkContextRuntime Runtime { get; } public ShrinkCtx? Parent { get; } public ShrinkFiber? OwnerFiber { get; } // ---------------- 效应(论文 3.1 / 算法 1) ---------------- /// 同步前向 + 可选同步逆操作的可逆效应;前向立即执行。 public ShrinkEffectHandle Effect(Action forward, Action? inverse = null) { if (forward == null) throw new ArgumentNullException(nameof(forward)); forward(); var handle = new ShrinkEffectHandle(); if (inverse != null) handle.AttachInverse(() => { inverse(); return UniTask.CompletedTask; }); Attach(handle); return handle; } /// 异步前向,完成时产出异步逆操作(论文 𝔈Γ)。DisposeAsync 会等待进行中的前向。 public async UniTask EffectAsync(Func>> effectFn) { if (effectFn == null) throw new ArgumentNullException(nameof(effectFn)); var handle = new ShrinkEffectHandle(); var gate = new UniTaskCompletionSource(); handle.BeginForward(gate.Task); Attach(handle); Func? inverse = null; try { inverse = await effectFn(); handle.CompleteForward(inverse); } finally { // 先登记逆操作再放行 gate,保证等待前向的 DisposeAsync 能读到完整逆链 gate.TrySetResult(); } return handle; } internal void Attach(ShrinkEffectHandle handle) => _effects.Add(handle); /// /// 前向已在外部执行完毕的效应:仅登记逆操作(供桥接适配器把既有 API 的"注册/注销"对包装为可逆效应)。 /// public ShrinkEffectHandle EffectInverse(Func inverse) { if (inverse == null) throw new ArgumentNullException(nameof(inverse)); var handle = new ShrinkEffectHandle(); handle.AttachInverse(inverse); Attach(handle); return handle; } /// 按 LIFO 顺序执行本上下文注册的全部逆操作。 public async UniTask DisposeAsync() { for (var i = _effects.Count - 1; i >= 0; i--) await _effects[i].DisposeAsync(); _effects.Clear(); } // ---------------- 余效应(论文 3.2 / 算法 2) ---------------- /// /// 提供依赖(set 是可逆效应):立即安装绑定并通知依赖者; /// 逆操作撤回绑定并再次通知。句柄同时挂在当前上下文上,随上下文卸载自动撤回。 /// public ShrinkEffectHandle Set(string key, T value) => Runtime.SetBinding(this, key, value, null); /// 提供带访问策略的依赖;策略只在介导 Get 时运行,原始编排查找不会运行策略。 public ShrinkEffectHandle Set(string key, T value, IShrinkCoeffectAccessPolicy accessPolicy) => Runtime.SetBinding(this, key, value, accessPolicy ?? throw new ArgumentNullException(nameof(accessPolicy))); public ShrinkEffectHandle Set(ShrinkKey key, T value) => Set((key ?? throw new ArgumentNullException(nameof(key))).Id, value); public ShrinkEffectHandle Set(ShrinkKey key, T value, IShrinkCoeffectAccessPolicy accessPolicy) => Set((key ?? throw new ArgumentNullException(nameof(key))).Id, value, accessPolicy); /// /// 代理介导的依赖访问(论文算法 6):沿纤程链解析已提交视图; /// 声明了但未激活的键抛 , /// 全链未声明的键抛 。 /// public T Get(string key) { var fiber = OwnerFiber; while (fiber != null) { if (fiber.Committed != null && fiber.Committed.TryGetValue(key, out var binding)) return Runtime.ResolveAccess(this, key, binding); if (fiber.InjectSet.Contains(key)) throw new ShrinkInactiveAccessException(key, fiber.Name); fiber = fiber.Parent; } throw new ShrinkUndeclaredAccessException(key); } public T Get(ShrinkKey key) => Get( (key ?? throw new ArgumentNullException(nameof(key))).Id); /// 相同的介导规则;未激活返回 false 而不抛异常。 public bool TryGet(string key, out T value) { var fiber = OwnerFiber; while (fiber != null) { if (fiber.Committed != null && fiber.Committed.TryGetValue(key, out var binding)) { value = Runtime.ResolveAccess(this, key, binding); return true; } if (fiber.InjectSet.Contains(key)) break; fiber = fiber.Parent; } value = default!; return false; } public bool TryGet(ShrinkKey key, out T value) => TryGet( (key ?? throw new ArgumentNullException(nameof(key))).Id, out value); /// /// 派生隔离子上下文(论文定义 28/29 的派生实现): /// 覆盖 key 的 realm 解析;父上下文不受影响;丢弃子上下文即隐式恢复。 /// public ShrinkCtx Isolate(string key, string realm) { if (string.IsNullOrEmpty(key)) throw new ArgumentException("Key must not be null or empty.", nameof(key)); if (string.IsNullOrEmpty(realm)) throw new ArgumentException("Realm must not be null or empty.", nameof(realm)); return new ShrinkCtx(Runtime, this, OwnerFiber, new Dictionary { [key] = realm }); } public ShrinkCtx Isolate(ShrinkKey key, string realm) => Isolate( (key ?? throw new ArgumentNullException(nameof(key))).Id, realm); /// /// 派生带拦截元数据的子上下文。子级同名 metadata 覆盖父级;丢弃子上下文即恢复。 /// 该操作不改变 realm、provider 或 target,因此不会触发 fiber 重载。 /// public ShrinkCtx Intercept(string key, IReadOnlyDictionary metadata) { if (string.IsNullOrWhiteSpace(key)) throw new ArgumentException("Key must not be null or empty.", nameof(key)); if (metadata == null) throw new ArgumentNullException(nameof(metadata)); var metadataCopy = new Dictionary(StringComparer.Ordinal); foreach (var pair in metadata) metadataCopy[pair.Key] = pair.Value; var overlay = new Dictionary>(StringComparer.Ordinal) { [key] = metadataCopy }; return new ShrinkCtx(Runtime, this, OwnerFiber, null, overlay); } public ShrinkCtx Intercept(ShrinkKey key, IReadOnlyDictionary metadata) => Intercept((key ?? throw new ArgumentNullException(nameof(key))).Id, metadata); internal ShrinkCtx CreateChildForFiber(ShrinkFiber fiber) => new(Runtime, this, fiber, null); /// 在派生子上下文中叠加隔离覆盖(同键后者优先),供加载器为条目配置隔离域。 internal ShrinkCtx WithIsolate(IReadOnlyDictionary isolate) { var overlay = new Dictionary(); if (_isolateOverlay != null) { foreach (var pair in _isolateOverlay) overlay[pair.Key] = pair.Value; } foreach (var pair in isolate) overlay[pair.Key] = pair.Value; return new ShrinkCtx(Runtime, this, OwnerFiber, overlay); } /// 在当前纤程上下文上叠加 loader 条目配置的 intercept。 internal ShrinkCtx WithIntercept( IReadOnlyDictionary> intercept) => new(Runtime, this, OwnerFiber, null, interceptOverlay: intercept); /// 原地替换本层 intercept,供 loader 在不重载 fiber 的情况下更新访问策略。 internal void ReplaceIntercept( IReadOnlyDictionary>? intercept) { if (intercept == null || intercept.Count == 0) { _interceptOverlay = null; return; } var copy = new Dictionary>(StringComparer.Ordinal); foreach (var pair in intercept) { if (string.IsNullOrWhiteSpace(pair.Key)) throw new ArgumentException("Intercept key must not be empty.", nameof(intercept)); if (pair.Value == null) throw new ArgumentException($"Intercept metadata for '{pair.Key}' must not be null.", nameof(intercept)); var metadataCopy = new Dictionary(StringComparer.Ordinal); foreach (var metadataPair in pair.Value) metadataCopy[metadataPair.Key] = metadataPair.Value; copy[pair.Key] = metadataCopy; } _interceptOverlay = copy; } /// 解析 key 在本上下文中归属的 realm:沿父链找最近的隔离覆盖,默认 realm 为 key 自身。 internal string ResolveRealm(string key) { var current = this; while (current != null) { if (current._isolateOverlay != null && current._isolateOverlay.TryGetValue(key, out var realm)) return realm; current = current.Parent; } return key; } /// 按根到叶顺序合并拦截元数据;越接近访问方的上下文优先。 internal IReadOnlyDictionary ResolveInterceptMetadata(string key) { var chain = new Stack(); for (var current = this; current != null; current = current.Parent) chain.Push(current); var merged = new Dictionary(StringComparer.Ordinal); while (chain.Count > 0) { var current = chain.Pop(); if (current._interceptOverlay == null || !current._interceptOverlay.TryGetValue(key, out var metadata)) continue; foreach (var pair in metadata) merged[pair.Key] = pair.Value; } return merged; } internal IReadOnlyCollection GetLocalInterceptKeys() => _interceptOverlay?.Keys ?? (IReadOnlyCollection)Array.Empty(); } }