XFE Git
XFE Studio Git
Git 首页 全局搜索
XFE 主站 文档 NuGet

XFESystem

【Rust】XFE系统,基于Rust和汇编

公开
关注 0 Fork 0 Star 0
UTF-8
using System;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
using XFESystem.Collections;

namespace XFESystem.Kernel;

/// <summary>Actual guest gate for the interpreter's owned ephemeron storage.
/// Host success is a fixture control only, not proof about the native collector.</summary>
public static class NativeWeakCollectionsRegression
{
    public static int ChecksPassed { get; private set; }
    public static int Failure { get; private set; }
    public static bool LiveValuePreserved { get; private set; }
    public static bool UpdatedValuePreserved { get; private set; }
    public static bool DeadKeyCycleCollected { get; private set; }
    public static bool DeadTablePruned { get; private set; }
    public static bool ReleasedScopeRejectsAccess { get; private set; }
    public static bool HandleCountRestored { get; private set; }
    public static bool NativeHandleCountChecked { get; private set; }
    public static ulong DependentHandlesBefore { get; private set; }
    public static ulong DependentHandlesAfter { get; private set; }

    public static bool Run(Func<ulong>? dependentHandleCount = null)
    {
        Failure = ChecksPassed = 0;
        LiveValuePreserved = UpdatedValuePreserved = DeadKeyCycleCollected = DeadTablePruned = ReleasedScopeRejectsAccess = false;
        NativeHandleCountChecked = dependentHandleCount is not null;
        // Initialize the collection and ObjectDisposedException paths before
        // measuring native slots; every warmup handle is explicitly released.
        bool warmup = Warmup();
        DependentHandlesBefore = dependentHandleCount?.Invoke() ?? 0;

        var live = CreateLive();
        CollectTwice();
        LiveValuePreserved = VerifyLive(live, 17);
        Record(LiveValuePreserved, 1);
        ReplaceValue(live);
        CollectTwice();
        UpdatedValuePreserved = VerifyLive(live, 42) && live.Scope.Lifetime is { Entries: 1, Created: 1, Disposed: 0 };
        Record(UpdatedValuePreserved, 2);
        live.WeakValue.Free(); live.Scope.Release();
        GC.KeepAlive(live);

        var cycle = CreateDeadKeyCycle();
        CollectTwice();
        var cycleLifetime = cycle.Scope.Prune();
        DeadKeyCycleCollected = cycle.WeakKey.Target is null && cycle.WeakValue.Target is null &&
            cycleLifetime is { Tables: 1, Entries: 0, Created: 1, Disposed: 1 };
        Record(DeadKeyCycleCollected, 3);
        cycle.WeakKey.Free(); cycle.WeakValue.Free(); cycle.Scope.Release();
        GC.KeepAlive(cycle.Table);

        var abandoned = CreateDeadTable();
        CollectTwice();
        bool tableDied = abandoned.WeakTable.Target is null;
        var tableLifetime = abandoned.Scope.Prune();
        CollectTwice();
        DeadTablePruned = tableDied && abandoned.WeakValue.Target is null &&
            tableLifetime is { Tables: 0, Entries: 0, Created: 1, Disposed: 1, TableHandlesCreated: 1, TableHandlesDisposed: 1 };
        Record(DeadTablePruned, 4);
        abandoned.WeakTable.Free(); abandoned.WeakValue.Free(); abandoned.Scope.Release();
        GC.KeepAlive(abandoned.Key);

        ReleasedScopeRejectsAccess = warmup && VerifyReleasedScope();
        Record(ReleasedScopeRejectsAccess, 5);
        DependentHandlesAfter = dependentHandleCount?.Invoke() ?? 0;
        HandleCountRestored = !NativeHandleCountChecked || DependentHandlesAfter == DependentHandlesBefore;
        Record(HandleCountRestored, 6);
        return Failure == 0 && ChecksPassed == 6;
    }

    private static void Record(bool passed, int failure)
    {
        if (passed) ChecksPassed++;
        else if (Failure == 0) Failure = failure;
    }

    [MethodImpl(MethodImplOptions.NoInlining)]
    private static bool Warmup()
    {
        var scope = new WeakCollectionScope(); var table = new OwnedWeakTable<object, Payload>(scope);
        object key = new(); table.Add(key, new Payload { Key = key, Value = 1 });
        table.AddOrUpdate(key, new Payload { Key = key, Value = 2 });
        scope.Prune(); scope.Release();
        return RejectsDisposed(() => table.Remove(key)) && scope.Lifetime is { Entries: 0, Created: 1, Disposed: 1 };
    }

    [MethodImpl(MethodImplOptions.NoInlining)]
    private static Live CreateLive()
    {
        var scope = new WeakCollectionScope(); var table = new OwnedWeakTable<object, Payload>(scope);
        object key = new(); var value = new Payload { Key = key, Value = 17 };
        table.Add(key, value);
        return new Live { Scope = scope, Table = table, Key = key, WeakValue = GCHandle.Alloc(value, GCHandleType.Weak) };
    }

    [MethodImpl(MethodImplOptions.NoInlining)]
    private static bool VerifyLive(Live live, int expected) => live.Table.TryGetValue(live.Key, out var value) && value.Value == expected &&
        ReferenceEquals(value.Key, live.Key) && ReferenceEquals(live.WeakValue.Target, value);

    [MethodImpl(MethodImplOptions.NoInlining)]
    private static void ReplaceValue(Live live)
    {
        var value = new Payload { Key = live.Key, Value = 42 };
        live.Table.AddOrUpdate(live.Key, value);
        live.WeakValue.Free(); live.WeakValue = GCHandle.Alloc(value, GCHandleType.Weak);
    }

    [MethodImpl(MethodImplOptions.NoInlining)]
    private static DeadCycle CreateDeadKeyCycle()
    {
        var scope = new WeakCollectionScope(); var table = new OwnedWeakTable<object, Payload>(scope);
        object key = new(); var value = new Payload { Key = key, Value = 23 }; table.Add(key, value);
        return new DeadCycle { Scope = scope, Table = table, WeakKey = GCHandle.Alloc(key, GCHandleType.Weak), WeakValue = GCHandle.Alloc(value, GCHandleType.Weak) };
    }

    [MethodImpl(MethodImplOptions.NoInlining)]
    private static DeadTable CreateDeadTable()
    {
        var scope = new WeakCollectionScope(); var table = new OwnedWeakTable<object, Payload>(scope);
        object key = new(); var value = new Payload { Key = key, Value = 29 }; table.Add(key, value);
        return new DeadTable { Scope = scope, Key = key, WeakTable = GCHandle.Alloc(table, GCHandleType.Weak), WeakValue = GCHandle.Alloc(value, GCHandleType.Weak) };
    }

    [MethodImpl(MethodImplOptions.NoInlining)]
    private static bool VerifyReleasedScope()
    {
        var scope = new WeakCollectionScope(); var table = new OwnedWeakTable<object, Payload>(scope);
        object key = new(); var value = new Payload { Key = key, Value = 31 }; table.Add(key, value);
        scope.Release(); scope.Release();
        var before = scope.Lifetime;
        bool rejected = RejectsDisposed(() => table.Add(key, value)) & RejectsDisposed(() => table.AddOrUpdate(key, value)) &
            RejectsDisposed(() => table.TryGetValue(key, out _)) & RejectsDisposed(() => table.Remove(key)) &
            RejectsDisposed(() => { _ = new OwnedWeakTable<object, Payload>(scope); });
        var after = scope.Lifetime;
        return rejected && before == after && after is { Tables: 0, Entries: 0, Created: 1, Disposed: 1, TableHandlesCreated: 1, TableHandlesDisposed: 1 };
    }

    [MethodImpl(MethodImplOptions.NoInlining)]
    private static bool RejectsDisposed(Action action)
    {
        try { action(); }
        catch (ObjectDisposedException) { return true; }
        return false;
    }

    [MethodImpl(MethodImplOptions.NoInlining)]
    private static void CollectTwice() { GC.Collect(); GC.Collect(); }

    private sealed class Payload { public object Key = null!; public int Value; }
    private sealed class Live
    {
        public WeakCollectionScope Scope = null!;
        public OwnedWeakTable<object, Payload> Table = null!;
        public object Key = null!;
        public GCHandle WeakValue;
    }
    private sealed class DeadCycle
    {
        public WeakCollectionScope Scope = null!;
        public OwnedWeakTable<object, Payload> Table = null!;
        public GCHandle WeakKey, WeakValue;
    }
    private sealed class DeadTable
    {
        public WeakCollectionScope Scope = null!;
        public object Key = null!;
        public GCHandle WeakTable, WeakValue;
    }
}