_gchandle
This commit is contained in:
@@ -497,6 +497,8 @@ namespace FlaxEditor
|
|||||||
Debug.LogException(ex);
|
Debug.LogException(ex);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
ManagedHandle.ManagedHandlePool.TryCollectWeakHandles();
|
||||||
|
|
||||||
Profiler.EndEvent();
|
Profiler.EndEvent();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -417,25 +417,26 @@ namespace FlaxEngine.Interop
|
|||||||
private const int WeakPoolCollectionTimeThreshold = 500;
|
private const int WeakPoolCollectionTimeThreshold = 500;
|
||||||
|
|
||||||
// Rolling numbers for handles, two bits reserved for the type
|
// Rolling numbers for handles, two bits reserved for the type
|
||||||
private static ulong normalHandleAccumulator = ((ulong)GCHandleType.Normal << 62) & 0xC000000000000000;
|
//private static ulong normalHandleAccumulator = ((ulong)GCHandleType.Normal << 62) & 0xC000000000000000;
|
||||||
private static ulong pinnedHandleAccumulator = ((ulong)GCHandleType.Pinned << 62) & 0xC000000000000000;
|
//private static ulong pinnedHandleAccumulator = ((ulong)GCHandleType.Pinned << 62) & 0xC000000000000000;
|
||||||
private static ulong weakHandleAccumulator = ((ulong)GCHandleType.Weak << 62) & 0xC000000000000000;
|
//private static ulong weakHandleAccumulator = ((ulong)GCHandleType.Weak << 62) & 0xC000000000000000;
|
||||||
|
|
||||||
// Dictionaries for storing the valid handles.
|
// Dictionaries for storing the valid handles.
|
||||||
// Note: Using locks seems to be generally the fastest when adding or fetching from the dictionary.
|
// 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.
|
// Concurrent dictionaries could also be considered, but they perform much slower when adding to the dictionary.
|
||||||
private static Dictionary<IntPtr, object> persistentPool = new();
|
//private static Dictionary<IntPtr, GCHandle> persistentPool = new();
|
||||||
private static Dictionary<IntPtr, GCHandle> pinnedPool = new();
|
//private static Dictionary<IntPtr, GCHandle> pinnedPool = new();
|
||||||
|
|
||||||
// TODO: Performance of pinned handles are poor at the moment due to GCHandle wrapping.
|
// 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
|
// 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.
|
// 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.
|
// Periodically when the pools are being accessed and conditions are met, the other pool is cleared and swapped.
|
||||||
private static Dictionary<IntPtr, object> weakPool = new();
|
private static HashSet<IntPtr> weakPool = new();
|
||||||
private static Dictionary<IntPtr, object> weakPoolOther = new();
|
private static HashSet<IntPtr> weakPoolOther = new();
|
||||||
private static object weakPoolLock = new object();
|
private static object weakPoolLock = new object();
|
||||||
private static ulong nextWeakPoolCollection;
|
private static ulong nextWeakPoolCollection;
|
||||||
|
private static ulong weakPoolCycle;
|
||||||
private static int nextWeakPoolGCCollection;
|
private static int nextWeakPoolGCCollection;
|
||||||
private static long lastWeakPoolCollectionTime;
|
private static long lastWeakPoolCollectionTime;
|
||||||
|
|
||||||
@@ -444,28 +445,47 @@ namespace FlaxEngine.Interop
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
internal static void TryCollectWeakHandles()
|
internal static void TryCollectWeakHandles()
|
||||||
{
|
{
|
||||||
if (weakHandleAccumulator < nextWeakPoolCollection)
|
nextWeakPoolCollection++;
|
||||||
|
if (nextWeakPoolCollection < 60)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
nextWeakPoolCollection = weakHandleAccumulator + 1000;
|
nextWeakPoolCollection = 0;
|
||||||
|
|
||||||
// Try to swap pools after garbage collection or whenever the pool gets too large
|
//ulong currentCycle = weakPoolCycle;
|
||||||
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
|
//lock (weakPoolLock)
|
||||||
if (System.Diagnostics.Stopwatch.GetElapsedTime(lastWeakPoolCollectionTime).TotalMilliseconds < WeakPoolCollectionTimeThreshold)
|
{
|
||||||
return;
|
//if (currentCycle != weakPoolCycle)
|
||||||
lastWeakPoolCollectionTime = System.Diagnostics.Stopwatch.GetTimestamp();
|
// return;
|
||||||
|
|
||||||
// Swap the pools and release the oldest pool for GC
|
weakPoolCycle++;
|
||||||
(weakPool, weakPoolOther) = (weakPoolOther, weakPool);
|
|
||||||
weakPool.Clear();
|
// 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
|
private static IntPtr NewHandle(GCHandleType type) => type switch
|
||||||
{
|
{
|
||||||
GCHandleType.Normal => (IntPtr)Interlocked.Increment(ref normalHandleAccumulator),
|
GCHandleType.Normal => (IntPtr)Interlocked.Increment(ref normalHandleAccumulator),
|
||||||
@@ -473,14 +493,30 @@ namespace FlaxEngine.Interop
|
|||||||
GCHandleType.Weak => (IntPtr)Interlocked.Increment(ref weakHandleAccumulator),
|
GCHandleType.Weak => (IntPtr)Interlocked.Increment(ref weakHandleAccumulator),
|
||||||
GCHandleType.WeakTrackResurrection => (IntPtr)Interlocked.Increment(ref weakHandleAccumulator),
|
GCHandleType.WeakTrackResurrection => (IntPtr)Interlocked.Increment(ref weakHandleAccumulator),
|
||||||
_ => throw new NotImplementedException(type.ToString())
|
_ => throw new NotImplementedException(type.ToString())
|
||||||
};
|
};*/
|
||||||
|
|
||||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
//[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||||
private static GCHandleType GetHandleType(IntPtr handle) => (GCHandleType)(((ulong)handle & 0xC000000000000000) >> 62);
|
//private static GCHandleType GetHandleType(IntPtr handle) => (GCHandleType)(((ulong)handle & 0xC000000000000000) >> 62);
|
||||||
|
|
||||||
internal static IntPtr AllocateHandle(object value, GCHandleType type)
|
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)
|
switch (type)
|
||||||
{
|
{
|
||||||
case GCHandleType.Normal:
|
case GCHandleType.Normal:
|
||||||
@@ -500,12 +536,22 @@ namespace FlaxEngine.Interop
|
|||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
return handle;
|
return handle;*/
|
||||||
}
|
}
|
||||||
|
|
||||||
internal static object GetObject(IntPtr 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:
|
case GCHandleType.Normal:
|
||||||
lock (persistentPool)
|
lock (persistentPool)
|
||||||
@@ -533,12 +579,19 @@ namespace FlaxEngine.Interop
|
|||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
throw new NativeInteropException("Invalid ManagedHandle");
|
throw new NativeInteropException("Invalid ManagedHandle");*/
|
||||||
}
|
}
|
||||||
|
|
||||||
internal static void SetObject(IntPtr handle, object value)
|
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:
|
case GCHandleType.Normal:
|
||||||
lock (persistentPool)
|
lock (persistentPool)
|
||||||
@@ -586,12 +639,23 @@ namespace FlaxEngine.Interop
|
|||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
throw new NativeInteropException("Invalid ManagedHandle");
|
throw new NativeInteropException("Invalid ManagedHandle");*/
|
||||||
}
|
}
|
||||||
|
|
||||||
internal static void FreeHandle(IntPtr handle)
|
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:
|
case GCHandleType.Normal:
|
||||||
lock (persistentPool)
|
lock (persistentPool)
|
||||||
@@ -616,7 +680,7 @@ namespace FlaxEngine.Interop
|
|||||||
TryCollectWeakHandles();
|
TryCollectWeakHandles();
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
throw new NativeInteropException("Invalid ManagedHandle");
|
throw new NativeInteropException("Invalid ManagedHandle");*/
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user