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;
}
}
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;
}
}