Merge branch 'weak_handle_improvements' of https://github.com/GoaLitiuM/FlaxEngine into GoaLitiuM-weak_handle_improvements
This commit is contained in:
@@ -305,13 +305,11 @@ namespace FlaxEngine.Interop
|
||||
|
||||
public void Free()
|
||||
{
|
||||
if (handle != IntPtr.Zero)
|
||||
{
|
||||
ManagedHandlePool.FreeHandle(handle);
|
||||
handle = IntPtr.Zero;
|
||||
}
|
||||
if (handle == IntPtr.Zero)
|
||||
return;
|
||||
|
||||
ManagedHandlePool.TryCollectWeakHandles();
|
||||
ManagedHandlePool.FreeHandle(handle);
|
||||
handle = IntPtr.Zero;
|
||||
}
|
||||
|
||||
public object Target
|
||||
@@ -352,6 +350,8 @@ namespace FlaxEngine.Interop
|
||||
|
||||
private static class ManagedHandlePool
|
||||
{
|
||||
private const int WeakPoolCollectionThreshold = 10000000;
|
||||
|
||||
private static ulong normalHandleAccumulator = 0;
|
||||
private static ulong pinnedHandleAccumulator = 0;
|
||||
private static ulong weakHandleAccumulator = 0;
|
||||
@@ -360,31 +360,39 @@ namespace FlaxEngine.Interop
|
||||
private static Dictionary<IntPtr, object> persistentPool = new Dictionary<nint, object>();
|
||||
private static Dictionary<IntPtr, GCHandle> pinnedPool = new Dictionary<nint, GCHandle>();
|
||||
|
||||
private static Dictionary<IntPtr, object> weakPool1 = new Dictionary<nint, object>();
|
||||
private static Dictionary<IntPtr, object> weakPool2 = new Dictionary<nint, object>();
|
||||
private static Dictionary<IntPtr, object> weakPool = weakPool1;
|
||||
private static Dictionary<IntPtr, object> weakPoolOther = weakPool2;
|
||||
|
||||
private static int nextCollection = GC.CollectionCount(0) + 1;
|
||||
// Manage double-buffered pool for weak handles in order to avoid collecting in-flight handles
|
||||
[ThreadStatic] private static Dictionary<IntPtr, object> weakPool;
|
||||
[ThreadStatic] private static Dictionary<IntPtr, object> weakPoolOther;
|
||||
[ThreadStatic] private static ulong nextWeakPoolCollection;
|
||||
[ThreadStatic] private static int nextWeakPoolGCCollection;
|
||||
|
||||
/// <summary>
|
||||
/// Tries to free all references to old weak handles so GC can collect them.
|
||||
/// </summary>
|
||||
internal static void TryCollectWeakHandles()
|
||||
private static void TryCollectWeakHandles()
|
||||
{
|
||||
if (GC.CollectionCount(0) < nextCollection)
|
||||
if (weakHandleAccumulator < nextWeakPoolCollection)
|
||||
return;
|
||||
|
||||
lock (poolLock)
|
||||
nextWeakPoolCollection = weakHandleAccumulator + 1000;
|
||||
if (weakPool == null)
|
||||
{
|
||||
nextCollection = GC.CollectionCount(0) + 1;
|
||||
|
||||
var swap = weakPoolOther;
|
||||
weakPoolOther = weakPool;
|
||||
weakPool = swap;
|
||||
|
||||
weakPool.Clear();
|
||||
weakPool = new Dictionary<nint, object>();
|
||||
weakPoolOther = new Dictionary<nint, object>();
|
||||
nextWeakPoolGCCollection = GC.CollectionCount(0);
|
||||
return;
|
||||
}
|
||||
// Collect right after garbage collection or whenever the pool gets too large
|
||||
var gc0CollectionCount = GC.CollectionCount(0);
|
||||
if (gc0CollectionCount < nextWeakPoolGCCollection && weakPool.Count < WeakPoolCollectionThreshold)
|
||||
return;
|
||||
|
||||
nextWeakPoolGCCollection = gc0CollectionCount + 1;
|
||||
|
||||
var swap = weakPoolOther;
|
||||
weakPoolOther = weakPool;
|
||||
weakPool = swap;
|
||||
weakPool.Clear();
|
||||
}
|
||||
|
||||
private static IntPtr NewHandle(GCHandleType type)
|
||||
@@ -410,71 +418,107 @@ namespace FlaxEngine.Interop
|
||||
|
||||
internal static IntPtr AllocateHandle(object value, GCHandleType type)
|
||||
{
|
||||
TryCollectWeakHandles();
|
||||
IntPtr handle = NewHandle(type);
|
||||
lock (poolLock)
|
||||
if (type == GCHandleType.Normal)
|
||||
{
|
||||
if (type == GCHandleType.Normal)
|
||||
lock (poolLock)
|
||||
persistentPool.Add(handle, value);
|
||||
else if (type == GCHandleType.Pinned)
|
||||
pinnedPool.Add(handle, GCHandle.Alloc(value, GCHandleType.Pinned));
|
||||
else if (type == GCHandleType.Weak || type == GCHandleType.WeakTrackResurrection)
|
||||
weakPool.Add(handle, value);
|
||||
}
|
||||
else if (type == GCHandleType.Pinned)
|
||||
{
|
||||
lock (poolLock)
|
||||
pinnedPool.Add(handle, GCHandle.Alloc(value, GCHandleType.Pinned));
|
||||
}
|
||||
else if (type == GCHandleType.Weak || type == GCHandleType.WeakTrackResurrection)
|
||||
weakPool.Add(handle, value);
|
||||
|
||||
return handle;
|
||||
}
|
||||
|
||||
internal static object GetObject(IntPtr handle)
|
||||
{
|
||||
TryCollectWeakHandles();
|
||||
object value;
|
||||
GCHandleType type = GetHandleType(handle);
|
||||
lock (poolLock)
|
||||
if (type == GCHandleType.Normal)
|
||||
{
|
||||
if (type == GCHandleType.Normal && persistentPool.TryGetValue(handle, out value))
|
||||
return value;
|
||||
else if (type == GCHandleType.Pinned && pinnedPool.TryGetValue(handle, out GCHandle gchandle))
|
||||
return gchandle.Target;
|
||||
else if (weakPool.TryGetValue(handle, out value))
|
||||
return value;
|
||||
else if (weakPoolOther.TryGetValue(handle, out value))
|
||||
return value;
|
||||
lock (poolLock)
|
||||
{
|
||||
if (persistentPool.TryGetValue(handle, out value))
|
||||
return value;
|
||||
}
|
||||
}
|
||||
else if (type == GCHandleType.Pinned)
|
||||
{
|
||||
lock (poolLock)
|
||||
{
|
||||
if (pinnedPool.TryGetValue(handle, out GCHandle gchandle))
|
||||
return gchandle.Target;
|
||||
}
|
||||
}
|
||||
else if (weakPool.TryGetValue(handle, out value))
|
||||
return value;
|
||||
else if (weakPoolOther.TryGetValue(handle, out value))
|
||||
return value;
|
||||
|
||||
throw new Exception("Invalid ManagedHandle");
|
||||
}
|
||||
|
||||
internal static void SetObject(IntPtr handle, object value)
|
||||
{
|
||||
TryCollectWeakHandles();
|
||||
GCHandleType type = GetHandleType(handle);
|
||||
lock (poolLock)
|
||||
if (type == GCHandleType.Normal)
|
||||
{
|
||||
if (type == GCHandleType.Normal && persistentPool.ContainsKey(handle))
|
||||
persistentPool[handle] = value;
|
||||
else if (type == GCHandleType.Pinned && pinnedPool.TryGetValue(handle, out GCHandle gchandle))
|
||||
gchandle.Target = value;
|
||||
else if (weakPool.ContainsKey(handle))
|
||||
weakPool[handle] = value;
|
||||
else if (weakPoolOther.ContainsKey(handle))
|
||||
weakPoolOther[handle] = value;
|
||||
lock (poolLock)
|
||||
{
|
||||
if (persistentPool.ContainsKey(handle))
|
||||
persistentPool[handle] = value;
|
||||
}
|
||||
}
|
||||
else if (type == GCHandleType.Pinned)
|
||||
{
|
||||
lock (poolLock)
|
||||
{
|
||||
if (pinnedPool.TryGetValue(handle, out GCHandle gchandle))
|
||||
gchandle.Target = value;
|
||||
}
|
||||
}
|
||||
else if (weakPool.ContainsKey(handle))
|
||||
weakPool[handle] = value;
|
||||
else if (weakPoolOther.ContainsKey(handle))
|
||||
weakPoolOther[handle] = value;
|
||||
|
||||
throw new Exception("Invalid ManagedHandle");
|
||||
}
|
||||
|
||||
internal static void FreeHandle(IntPtr handle)
|
||||
{
|
||||
TryCollectWeakHandles();
|
||||
GCHandleType type = GetHandleType(handle);
|
||||
lock (poolLock)
|
||||
if (type == GCHandleType.Normal)
|
||||
{
|
||||
if (type == GCHandleType.Normal && persistentPool.Remove(handle))
|
||||
return;
|
||||
else if (type == GCHandleType.Pinned && pinnedPool.Remove(handle, out GCHandle gchandle))
|
||||
lock (poolLock)
|
||||
{
|
||||
gchandle.Free();
|
||||
return;
|
||||
if (persistentPool.Remove(handle))
|
||||
return;
|
||||
}
|
||||
else if (weakPool.Remove(handle))
|
||||
return;
|
||||
else if (weakPoolOther.Remove(handle))
|
||||
return;
|
||||
}
|
||||
else if (type == GCHandleType.Pinned)
|
||||
{
|
||||
lock (poolLock)
|
||||
{
|
||||
if (pinnedPool.Remove(handle, out GCHandle gchandle))
|
||||
{
|
||||
gchandle.Free();
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
return;
|
||||
|
||||
throw new Exception("Invalid ManagedHandle");
|
||||
}
|
||||
}
|
||||
|
||||
@@ -5,6 +5,7 @@ using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Globalization;
|
||||
using System.Runtime.CompilerServices;
|
||||
using System.Runtime.InteropServices;
|
||||
using System.Runtime.InteropServices.Marshalling;
|
||||
|
||||
#pragma warning disable 1591
|
||||
@@ -34,13 +35,16 @@ namespace FlaxEngine.Interop
|
||||
|
||||
public static class ManagedToNative
|
||||
{
|
||||
public static IntPtr ConvertToUnmanaged(object managed) => managed != null ? ManagedHandle.ToIntPtr(managed) : IntPtr.Zero;
|
||||
public static IntPtr ConvertToUnmanaged(object managed) => managed != null ? ManagedHandle.ToIntPtr(managed, GCHandleType.Weak) : IntPtr.Zero;
|
||||
|
||||
public static void Free(IntPtr unmanaged)
|
||||
{
|
||||
// This is a weak handle, no need to free it
|
||||
/*
|
||||
if (unmanaged == IntPtr.Zero)
|
||||
return;
|
||||
ManagedHandle.FromIntPtr(unmanaged).Free();
|
||||
*/
|
||||
}
|
||||
}
|
||||
|
||||
@@ -170,7 +174,7 @@ namespace FlaxEngine.Interop
|
||||
{
|
||||
if (managedArray == null)
|
||||
return IntPtr.Zero;
|
||||
handle = ManagedHandle.Alloc(managedArray);
|
||||
handle = ManagedHandle.Alloc(managedArray, GCHandleType.Weak);
|
||||
return ManagedHandle.ToIntPtr(handle);
|
||||
}
|
||||
|
||||
@@ -179,7 +183,7 @@ namespace FlaxEngine.Interop
|
||||
if (managedArray == null)
|
||||
return;
|
||||
managedArray.FreePooled();
|
||||
handle.Free();
|
||||
//handle.Free(); // No need to free weak handles
|
||||
}
|
||||
}
|
||||
|
||||
@@ -232,8 +236,11 @@ namespace FlaxEngine.Interop
|
||||
|
||||
public static class ManagedToNative
|
||||
{
|
||||
public static IntPtr ConvertToUnmanaged(Dictionary<T, U> managed) => DictionaryMarshaller<T, U>.ToNative(managed);
|
||||
public static void Free(IntPtr unmanaged) => DictionaryMarshaller<T, U>.Free(unmanaged);
|
||||
public static IntPtr ConvertToUnmanaged(Dictionary<T, U> managed) => DictionaryMarshaller<T, U>.ToNative(managed, GCHandleType.Weak);
|
||||
public static void Free(IntPtr unmanaged)
|
||||
{
|
||||
//DictionaryMarshaller<T, U>.Free(unmanaged); // No need to free weak handles
|
||||
}
|
||||
}
|
||||
|
||||
public struct Bidirectional
|
||||
@@ -265,7 +272,7 @@ namespace FlaxEngine.Interop
|
||||
public static IntPtr ConvertToUnmanaged(Dictionary<T, U> managed) => managed != null ? ManagedHandle.ToIntPtr(managed) : IntPtr.Zero;
|
||||
|
||||
public static Dictionary<T, U> ToManaged(IntPtr unmanaged) => unmanaged != IntPtr.Zero ? Unsafe.As<Dictionary<T, U>>(ManagedHandle.FromIntPtr(unmanaged).Target) : null;
|
||||
public static IntPtr ToNative(Dictionary<T, U> managed) => managed != null ? ManagedHandle.ToIntPtr(managed) : IntPtr.Zero;
|
||||
public static IntPtr ToNative(Dictionary<T, U> managed, GCHandleType handleType = GCHandleType.Normal) => managed != null ? ManagedHandle.ToIntPtr(managed, handleType) : IntPtr.Zero;
|
||||
|
||||
public static void Free(IntPtr unmanaged)
|
||||
{
|
||||
@@ -334,8 +341,7 @@ namespace FlaxEngine.Interop
|
||||
}
|
||||
numElements = managed.Length;
|
||||
ManagedArray managedArray = ManagedArray.AllocatePooledArray<TUnmanagedElement>(managed.Length);
|
||||
var ptr = ManagedHandle.ToIntPtr(managedArray);
|
||||
return (TUnmanagedElement*)ptr;
|
||||
return (TUnmanagedElement*)ManagedHandle.ToIntPtr(managedArray, GCHandleType.Weak);
|
||||
}
|
||||
|
||||
public static ReadOnlySpan<T> GetManagedValuesSource(T[]? managed) => managed;
|
||||
@@ -354,7 +360,7 @@ namespace FlaxEngine.Interop
|
||||
return;
|
||||
ManagedHandle handle = ManagedHandle.FromIntPtr(new IntPtr(unmanaged));
|
||||
(Unsafe.As<ManagedArray>(handle.Target)).FreePooled();
|
||||
handle.Free();
|
||||
//handle.Free(); // No need to free weak handles
|
||||
}
|
||||
}
|
||||
|
||||
@@ -475,10 +481,13 @@ namespace FlaxEngine.Interop
|
||||
{
|
||||
public static unsafe IntPtr ConvertToUnmanaged(string managed)
|
||||
{
|
||||
return managed == null ? IntPtr.Zero : ManagedHandle.ToIntPtr(managed);
|
||||
return managed == null ? IntPtr.Zero : ManagedHandle.ToIntPtr(managed, GCHandleType.Weak);
|
||||
}
|
||||
|
||||
public static void Free(IntPtr unmanaged) => ManagedString.Free(unmanaged);
|
||||
public static void Free(IntPtr unmanaged)
|
||||
{
|
||||
//ManagedString.Free(unmanaged); // No need to free weak handles
|
||||
}
|
||||
}
|
||||
|
||||
public struct Bidirectional
|
||||
|
||||
@@ -249,13 +249,13 @@ bool AndroidFileSystem::DeleteFile(const StringView& path)
|
||||
uint64 AndroidFileSystem::GetFileSize(const StringView& path)
|
||||
{
|
||||
struct stat fileInfo;
|
||||
fileInfo.st_size = -1;
|
||||
fileInfo.st_size = 0;
|
||||
const StringAsANSI<> pathANSI(*path, path.Length());
|
||||
if (stat(pathANSI.Get(), &fileInfo) != -1)
|
||||
{
|
||||
if (S_ISDIR(fileInfo.st_mode))
|
||||
{
|
||||
fileInfo.st_size = -1;
|
||||
fileInfo.st_size = 0;
|
||||
}
|
||||
}
|
||||
else
|
||||
|
||||
@@ -223,14 +223,14 @@ bool AppleFileSystem::DeleteFile(const StringView& path)
|
||||
uint64 AppleFileSystem::GetFileSize(const StringView& path)
|
||||
{
|
||||
struct stat fileInfo;
|
||||
fileInfo.st_size = -1;
|
||||
fileInfo.st_size = 0;
|
||||
const StringAsANSI<> pathANSI(*path, path.Length());
|
||||
if (stat(pathANSI.Get(), &fileInfo) != -1)
|
||||
{
|
||||
// Check for directories
|
||||
if (S_ISDIR(fileInfo.st_mode))
|
||||
{
|
||||
fileInfo.st_size = -1;
|
||||
fileInfo.st_size = 0;
|
||||
}
|
||||
}
|
||||
return fileInfo.st_size;
|
||||
|
||||
@@ -346,14 +346,14 @@ bool LinuxFileSystem::DeleteFile(const StringView& path)
|
||||
uint64 LinuxFileSystem::GetFileSize(const StringView& path)
|
||||
{
|
||||
struct stat fileInfo;
|
||||
fileInfo.st_size = -1;
|
||||
fileInfo.st_size = 0;
|
||||
const StringAsANSI<> pathANSI(*path, path.Length());
|
||||
if (stat(pathANSI.Get(), &fileInfo) != -1)
|
||||
{
|
||||
// Check for directories
|
||||
if (S_ISDIR(fileInfo.st_mode))
|
||||
{
|
||||
fileInfo.st_size = -1;
|
||||
fileInfo.st_size = 0;
|
||||
}
|
||||
}
|
||||
return fileInfo.st_size;
|
||||
|
||||
Reference in New Issue
Block a user