Initial import of IC.Rebuild
This commit is contained in:
@@ -0,0 +1,99 @@
|
||||
using System.Collections.Concurrent;
|
||||
|
||||
namespace ICRebuild.Core;
|
||||
|
||||
// Every resource is registered before activation, including during partial startup.
|
||||
public sealed class ResourceScope : IDisposable
|
||||
{
|
||||
private readonly Stack<IDisposable> resources = new();
|
||||
private bool disposed;
|
||||
public T Own<T>(T resource) where T : IDisposable
|
||||
{
|
||||
ObjectDisposedException.ThrowIf(disposed, this);
|
||||
resources.Push(resource);
|
||||
return resource;
|
||||
}
|
||||
public void Defer(Action action) => Own(new ActionDisposable(action));
|
||||
public void Dispose()
|
||||
{
|
||||
if (disposed) return;
|
||||
disposed = true;
|
||||
List<Exception> errors = [];
|
||||
while (resources.TryPop(out var resource))
|
||||
try { resource.Dispose(); } catch (Exception ex) { errors.Add(ex); }
|
||||
if (errors.Count > 0) throw new AggregateException("资源释放失败", errors);
|
||||
}
|
||||
}
|
||||
|
||||
public sealed class ActionDisposable(Action action) : IDisposable
|
||||
{
|
||||
private Action? release = action;
|
||||
public void Dispose() => Interlocked.Exchange(ref release, null)?.Invoke();
|
||||
}
|
||||
|
||||
public sealed class ModuleManager : IDisposable
|
||||
{
|
||||
private readonly Dictionary<int, Action<ResourceScope>> factories = [];
|
||||
private readonly ConcurrentDictionary<int, ResourceScope> active = [];
|
||||
private readonly Dictionary<int, int[]> dependencies = [];
|
||||
private readonly Dictionary<int, string> unavailable = [];
|
||||
private readonly HashSet<int> cleanupFailed = [];
|
||||
private bool disposed;
|
||||
public Dictionary<int, ModuleStatus> Status { get; } = Features.All.ToDictionary(f => f.Id, f => new ModuleStatus(f));
|
||||
public bool IsEnabled(int id) => active.ContainsKey(id);
|
||||
|
||||
public void Register(int id, Action<ResourceScope> start, params int[] required)
|
||||
{
|
||||
factories.Add(id, start); dependencies[id] = required;
|
||||
Status[id].HasImplementation=true;
|
||||
Status[id].Detail = "已关闭";
|
||||
}
|
||||
public void Unavailable(int id, string reason)
|
||||
{
|
||||
unavailable[id] = reason;
|
||||
if (Status.TryGetValue(id, out var state)) state.Detail = reason;
|
||||
}
|
||||
public void ClearUnavailable(int id) => unavailable.Remove(id);
|
||||
|
||||
public bool Enable(int id)
|
||||
{
|
||||
ObjectDisposedException.ThrowIf(disposed, this);
|
||||
if (!Status.TryGetValue(id, out var state)) return false;
|
||||
if (cleanupFailed.Contains(id)) { state.Detail="上次资源释放失败,重载插件前禁止重复挂接"; return false; }
|
||||
if (unavailable.TryGetValue(id, out var blockedReason)) { state.State = ModuleState.Failed; state.Detail = blockedReason; return false; }
|
||||
state.Requested = true;
|
||||
if (active.ContainsKey(id)) return true;
|
||||
if (!factories.TryGetValue(id, out var start)) return false;
|
||||
var missing = dependencies[id].Where(x => !IsEnabled(x)).ToArray();
|
||||
if (missing.Length > 0)
|
||||
{ state.State = ModuleState.Failed; state.Detail = $"先启用依赖:{string.Join(", ", missing)}"; return false; }
|
||||
var scope = new ResourceScope();
|
||||
try
|
||||
{
|
||||
start(scope);
|
||||
if (!active.TryAdd(id, scope)) throw new InvalidOperationException("模块已经启用");
|
||||
state.State = ModuleState.Enabled;
|
||||
if(state.Adaptation!=Adaptation.Verified) state.Adaptation = Adaptation.Testable;
|
||||
state.Detail = "已开启";
|
||||
return true;
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
try { scope.Dispose(); } catch (Exception cleanup) { cleanupFailed.Add(id); ex = new AggregateException(ex, cleanup); }
|
||||
state.State = ModuleState.Failed; state.Detail = ex.Message;
|
||||
return false;
|
||||
}
|
||||
}
|
||||
public void Disable(int id)
|
||||
{
|
||||
if (!Status.TryGetValue(id, out var state)) return;
|
||||
foreach (var dependent in dependencies.Where(x => x.Value.Contains(id) && IsEnabled(x.Key)).Select(x => x.Key).ToArray())
|
||||
Disable(dependent);
|
||||
state.Requested = false;
|
||||
if (!active.TryRemove(id, out var scope)) return;
|
||||
try { scope.Dispose(); state.State = ModuleState.Disabled; state.Detail = "已关闭"; }
|
||||
catch (Exception ex) { cleanupFailed.Add(id); state.State = ModuleState.Failed; state.Detail = $"卸载失败:{ex.Message}"; }
|
||||
}
|
||||
public void DisableAll() { foreach (var id in active.Keys.Reverse().ToArray()) Disable(id); }
|
||||
public void Dispose() { if (disposed) return; DisableAll(); disposed = true; }
|
||||
}
|
||||
Reference in New Issue
Block a user