From 1b886cde944b7c51f401d15be84970b49c26a169 Mon Sep 17 00:00:00 2001 From: Ari Vuollet Date: Thu, 18 Dec 2025 14:09:07 +0200 Subject: [PATCH] _managed handle pool start --- Source/Engine/Engine/NativeInterop.Managed.cs | 69 +++++++++++++++++-- 1 file changed, 65 insertions(+), 4 deletions(-) diff --git a/Source/Engine/Engine/NativeInterop.Managed.cs b/Source/Engine/Engine/NativeInterop.Managed.cs index 9d9a7f63f..f489cde46 100644 --- a/Source/Engine/Engine/NativeInterop.Managed.cs +++ b/Source/Engine/Engine/NativeInterop.Managed.cs @@ -2,7 +2,7 @@ #define USE_CONCURRENT_DICT #define USE_GCHANDLE -//#define TRACK_HANDLES +#define TRACK_HANDLES #if USE_NETCORE using System; @@ -368,10 +368,17 @@ namespace FlaxEngine.Interop private static HashSet _weakHandles = new HashSet(); private static ConcurrentDictionary _handles = new(); private static ConcurrentDictionary _handles2 = new(); + private static long _allocations = 0; + private static long _deallocations = 0; + + public static long Allocations => Interlocked.Read(ref _allocations); + public static long Deallocations => Interlocked.Read(ref _deallocations); #endif + [MethodImpl(MethodImplOptions.AggressiveInlining)] private ManagedHandle(IntPtr handle) => this.handle = GCHandle.FromIntPtr(handle); + //[MethodImpl(MethodImplOptions.AggressiveInlining)] private ManagedHandle(object value, GCHandleType type) //=> handle = GCHandle.Alloc(value, type); { #if TRACK_HANDLES @@ -394,6 +401,7 @@ namespace FlaxEngine.Interop type = type; _handles2.TryAdd((IntPtr)handle, value?.GetType().FullName ?? ""); } + Interlocked.Increment(ref _allocations); #endif } #else @@ -430,14 +438,25 @@ namespace FlaxEngine.Interop { handle = handle; } + Interlocked.Increment(ref _deallocations); #endif handle.Free(); } - - public object Target => handle.Target; + public object Target + { + [MethodImpl(MethodImplOptions.AggressiveInlining)] + get => handle.Target; - public bool IsAllocated => handle.IsAllocated; + [MethodImpl(MethodImplOptions.AggressiveInlining)] + set => handle.Target = value; + } + + public bool IsAllocated + { + [MethodImpl(MethodImplOptions.AggressiveInlining)] + get => handle.IsAllocated; + } #else public void Free() { @@ -485,22 +504,64 @@ namespace FlaxEngine.Interop public static IntPtr ToIntPtr(ManagedHandle value) => value.handle; #endif + [MethodImpl(MethodImplOptions.AggressiveInlining)] public override int GetHashCode() => handle.GetHashCode(); + [MethodImpl(MethodImplOptions.AggressiveInlining)] public override bool Equals(object obj) => obj is ManagedHandle other && handle == other.handle; + [MethodImpl(MethodImplOptions.AggressiveInlining)] public bool Equals(ManagedHandle other) => handle == other.handle; + [MethodImpl(MethodImplOptions.AggressiveInlining)] public static bool operator ==(ManagedHandle a, ManagedHandle b) => a.handle == b.handle; + [MethodImpl(MethodImplOptions.AggressiveInlining)] public static bool operator !=(ManagedHandle a, ManagedHandle b) => a.handle != b.handle; #if USE_GCHANDLE + /// + /// A pool of shared managed handles used with short-lived temporary GCHandle allocations during interop. + /// internal static class ManagedHandlePool { + [ThreadStatic] + private static List<(bool InUse, ManagedHandle Handle)> _pool; + internal static void TryCollectWeakHandles(bool force = false) { } + + internal static ManagedHandle Rent() + { + foreach (ref var tuple in CollectionsMarshal.AsSpan(_pool)) + { + if (tuple.InUse) + continue; + + tuple.InUse = true; + return tuple.Handle; + } + + var newTuple = (InUse: true, Array: new ManagedHandle()); + _pool.Add(newTuple); + return newTuple.Array; + } + + internal static void Return(ManagedHandle handle) + { + foreach (ref var tuple in CollectionsMarshal.AsSpan(_pool)) + { + if (tuple.Handle != handle) + continue; + + tuple.InUse = false; + handle.Target = null; + return; + } + + throw new Exception("Tried to return non-pooled ManagedHandle"); + } } #else