From 70edfbb1466e163da7bba805a6a62dc5e855cbe3 Mon Sep 17 00:00:00 2001 From: Ari Vuollet Date: Sun, 27 Aug 2023 11:13:27 +0300 Subject: [PATCH] _gchandle --- Source/Editor/Editor.cs | 2 + Source/Engine/Engine/NativeInterop.Managed.cs | 130 +++++++++++++----- 2 files changed, 99 insertions(+), 33 deletions(-) diff --git a/Source/Editor/Editor.cs b/Source/Editor/Editor.cs index d0076068e..0378ce6e0 100644 --- a/Source/Editor/Editor.cs +++ b/Source/Editor/Editor.cs @@ -497,6 +497,8 @@ namespace FlaxEditor Debug.LogException(ex); } + ManagedHandle.ManagedHandlePool.TryCollectWeakHandles(); + Profiler.EndEvent(); } diff --git a/Source/Engine/Engine/NativeInterop.Managed.cs b/Source/Engine/Engine/NativeInterop.Managed.cs index c7507c778..8f63b788d 100644 --- a/Source/Engine/Engine/NativeInterop.Managed.cs +++ b/Source/Engine/Engine/NativeInterop.Managed.cs @@ -417,25 +417,26 @@ namespace FlaxEngine.Interop private const int WeakPoolCollectionTimeThreshold = 500; // Rolling numbers for handles, two bits reserved for the type - private static ulong normalHandleAccumulator = ((ulong)GCHandleType.Normal << 62) & 0xC000000000000000; - private static ulong pinnedHandleAccumulator = ((ulong)GCHandleType.Pinned << 62) & 0xC000000000000000; - private static ulong weakHandleAccumulator = ((ulong)GCHandleType.Weak << 62) & 0xC000000000000000; + //private static ulong normalHandleAccumulator = ((ulong)GCHandleType.Normal << 62) & 0xC000000000000000; + //private static ulong pinnedHandleAccumulator = ((ulong)GCHandleType.Pinned << 62) & 0xC000000000000000; + //private static ulong weakHandleAccumulator = ((ulong)GCHandleType.Weak << 62) & 0xC000000000000000; // Dictionaries for storing the valid handles. // Note: Using locks seems to be generally the fastest when adding or fetching from the dictionary. // Concurrent dictionaries could also be considered, but they perform much slower when adding to the dictionary. - private static Dictionary persistentPool = new(); - private static Dictionary pinnedPool = new(); + //private static Dictionary persistentPool = new(); + //private static Dictionary pinnedPool = new(); // TODO: Performance of pinned handles are poor at the moment due to GCHandle wrapping. // TODO: .NET8: Experiment with pinned arrays for faster pinning: https://github.com/dotnet/runtime/pull/89293 // Manage double-buffered pool for weak handles in order to avoid collecting in-flight handles. // Periodically when the pools are being accessed and conditions are met, the other pool is cleared and swapped. - private static Dictionary weakPool = new(); - private static Dictionary weakPoolOther = new(); + private static HashSet weakPool = new(); + private static HashSet weakPoolOther = new(); private static object weakPoolLock = new object(); private static ulong nextWeakPoolCollection; + private static ulong weakPoolCycle; private static int nextWeakPoolGCCollection; private static long lastWeakPoolCollectionTime; @@ -444,28 +445,47 @@ namespace FlaxEngine.Interop /// internal static void TryCollectWeakHandles() { - if (weakHandleAccumulator < nextWeakPoolCollection) + nextWeakPoolCollection++; + if (nextWeakPoolCollection < 60) return; - nextWeakPoolCollection = weakHandleAccumulator + 1000; + nextWeakPoolCollection = 0; - // Try to swap pools after garbage collection or whenever the pool gets too large - var gc0CollectionCount = GC.CollectionCount(0); - if (gc0CollectionCount < nextWeakPoolGCCollection && weakPool.Count < WeakPoolCollectionSizeThreshold) - return; - nextWeakPoolGCCollection = gc0CollectionCount + 1; + //ulong currentCycle = weakPoolCycle; - // Prevent huge allocations from swapping the pools in the middle of the operation - if (System.Diagnostics.Stopwatch.GetElapsedTime(lastWeakPoolCollectionTime).TotalMilliseconds < WeakPoolCollectionTimeThreshold) - return; - lastWeakPoolCollectionTime = System.Diagnostics.Stopwatch.GetTimestamp(); + //lock (weakPoolLock) + { + //if (currentCycle != weakPoolCycle) + // return; - // Swap the pools and release the oldest pool for GC - (weakPool, weakPoolOther) = (weakPoolOther, weakPool); - weakPool.Clear(); + weakPoolCycle++; + + // Try to swap pools after garbage collection or whenever the pool gets too large + var gc0CollectionCount = GC.CollectionCount(0); + if (gc0CollectionCount < nextWeakPoolGCCollection && weakPool.Count < WeakPoolCollectionSizeThreshold) + return; + nextWeakPoolGCCollection = gc0CollectionCount + 1; + + // Prevent huge allocations from swapping the pools in the middle of the operation + if (System.Diagnostics.Stopwatch.GetElapsedTime(lastWeakPoolCollectionTime).TotalMilliseconds < WeakPoolCollectionTimeThreshold) + return; + lastWeakPoolCollectionTime = System.Diagnostics.Stopwatch.GetTimestamp(); + + foreach (IntPtr ptr in weakPoolOther) + { + GCHandle gcHandle = GCHandle.FromIntPtr(ptr); + + gcHandle.Free(); + } + + // Swap the pools and release the oldest pool for GC + (weakPool, weakPoolOther) = (weakPoolOther, weakPool); + weakPool.Clear(); + //FlaxEditor.Editor.LogWarning("collected handles"); + } } - [MethodImpl(MethodImplOptions.AggressiveInlining)] + /*[MethodImpl(MethodImplOptions.AggressiveInlining)] private static IntPtr NewHandle(GCHandleType type) => type switch { GCHandleType.Normal => (IntPtr)Interlocked.Increment(ref normalHandleAccumulator), @@ -473,14 +493,30 @@ namespace FlaxEngine.Interop GCHandleType.Weak => (IntPtr)Interlocked.Increment(ref weakHandleAccumulator), GCHandleType.WeakTrackResurrection => (IntPtr)Interlocked.Increment(ref weakHandleAccumulator), _ => throw new NotImplementedException(type.ToString()) - }; + };*/ - [MethodImpl(MethodImplOptions.AggressiveInlining)] - private static GCHandleType GetHandleType(IntPtr handle) => (GCHandleType)(((ulong)handle & 0xC000000000000000) >> 62); + //[MethodImpl(MethodImplOptions.AggressiveInlining)] + //private static GCHandleType GetHandleType(IntPtr handle) => (GCHandleType)(((ulong)handle & 0xC000000000000000) >> 62); internal static IntPtr AllocateHandle(object value, GCHandleType type) { - IntPtr handle = NewHandle(type); + GCHandleType type2 = type; + if (type == GCHandleType.Weak || type == GCHandleType.WeakTrackResurrection) + { + type2 = GCHandleType.Normal; + } + + GCHandle gcHandle = GCHandle.Alloc(value, type2); + IntPtr handle = GCHandle.ToIntPtr(gcHandle); + + if (type == GCHandleType.Weak || type == GCHandleType.WeakTrackResurrection) + { + //TryCollectWeakHandles(); + weakPool.Add(handle/*, gcHandle.Target*/); + } + + return handle; + /*IntPtr handle = NewHandle(type); switch (type) { case GCHandleType.Normal: @@ -500,12 +536,22 @@ namespace FlaxEngine.Interop } break; } - return handle; + return handle;*/ } internal static object GetObject(IntPtr handle) { - switch (GetHandleType(handle)) + //if (weakPool.Contains(handle) || weakPoolOther.Contains(handle)) + // handle = handle; + + GCHandle gcHandle = GCHandle.FromIntPtr(handle); + + if (!gcHandle.IsAllocated) + throw new NativeInteropException("Invalid ManagedHandle"); + + return gcHandle.Target; + + /*switch (GetHandleType(handle)) { case GCHandleType.Normal: lock (persistentPool) @@ -533,12 +579,19 @@ namespace FlaxEngine.Interop } break; } - throw new NativeInteropException("Invalid ManagedHandle"); + throw new NativeInteropException("Invalid ManagedHandle");*/ } internal static void SetObject(IntPtr handle, object value) { - switch (GetHandleType(handle)) + GCHandle gcHandle = GCHandle.FromIntPtr(handle); + + if (!gcHandle.IsAllocated) + throw new NativeInteropException("Invalid ManagedHandle"); + + gcHandle.Target = value; + + /*switch (GetHandleType(handle)) { case GCHandleType.Normal: lock (persistentPool) @@ -586,12 +639,23 @@ namespace FlaxEngine.Interop } break; } - throw new NativeInteropException("Invalid ManagedHandle"); + throw new NativeInteropException("Invalid ManagedHandle");*/ } internal static void FreeHandle(IntPtr handle) { - switch (GetHandleType(handle)) + if (weakPool.Contains(handle) || weakPoolOther.Contains(handle)) + return; + + GCHandle gcHandle = GCHandle.FromIntPtr(handle); + + if (!gcHandle.IsAllocated) + throw new NativeInteropException("Invalid ManagedHandle"); + + gcHandle.Free(); + //if (weakPool.Contains(handle) || ) + + /*switch (GetHandleType(handle)) { case GCHandleType.Normal: lock (persistentPool) @@ -616,7 +680,7 @@ namespace FlaxEngine.Interop TryCollectWeakHandles(); return; } - throw new NativeInteropException("Invalid ManagedHandle"); + throw new NativeInteropException("Invalid ManagedHandle");*/ } } }