Merge branch 'GoaLitiuM-weak_handle_improvements' into 1.6
This commit is contained in:
@@ -69,7 +69,7 @@ void iOSPlatformTools::OnBuildStarted(CookingData& data)
|
||||
{
|
||||
// Adjust the cooking output folders for packaging app
|
||||
const auto appName = GetAppName();
|
||||
String contents = appName + TEXT(".app/");
|
||||
String contents = String(TEXT("/Payload/")) / appName + TEXT(".app/");
|
||||
data.DataOutputPath /= contents;
|
||||
data.NativeCodeOutputPath /= contents;
|
||||
data.ManagedCodeOutputPath /= contents;
|
||||
@@ -160,6 +160,13 @@ bool iOSPlatformTools::OnPostProcess(CookingData& data)
|
||||
return true;
|
||||
}
|
||||
FileSystem::DeleteDirectory(tmpFolderPath);
|
||||
String iTunesArtworkPath = data.OriginalOutputPath / TEXT("iTunesArtwork.png");
|
||||
if (EditorUtilities::ExportApplicationImage(iconData, 512, 512, PixelFormat::R8G8B8A8_UNorm, iTunesArtworkPath))
|
||||
{
|
||||
LOG(Error, "Failed to export application icon.");
|
||||
return true;
|
||||
}
|
||||
FileSystem::MoveFile(data.OriginalOutputPath / TEXT("iTunesArtwork"), iTunesArtworkPath, true);
|
||||
}
|
||||
|
||||
// Create PkgInfo file
|
||||
@@ -238,27 +245,24 @@ bool iOSPlatformTools::OnPostProcess(CookingData& data)
|
||||
}
|
||||
}
|
||||
|
||||
// TODO: sign binaries
|
||||
|
||||
// TODO: expose event to inject custom post-processing before app packaging (eg. third-party plugins)
|
||||
|
||||
|
||||
// Package application
|
||||
/*const auto buildSettings = BuildSettings::Get();
|
||||
const auto buildSettings = BuildSettings::Get();
|
||||
if (buildSettings->SkipPackaging)
|
||||
return false;
|
||||
GameCooker::PackageFiles();
|
||||
LOG(Info, "Building app package...");
|
||||
const String dmgPath = data.OriginalOutputPath / appName + TEXT(".dmg");
|
||||
const String dmgCommand = String::Format(TEXT("hdiutil create {0}.dmg -volname {0} -fs HFS+ -srcfolder {0}.app"), appName);
|
||||
const int32 result = Platform::RunProcess(dmgCommand, data.OriginalOutputPath);
|
||||
const String ipaPath = data.OriginalOutputPath / appName + TEXT(".ipa");
|
||||
const String ipaCommand = String::Format(TEXT("zip -r -X {0}.ipa Payload iTunesArtwork"), appName);
|
||||
const int32 result = Platform::RunProcess(ipaCommand, data.OriginalOutputPath);
|
||||
if (result != 0)
|
||||
{
|
||||
data.Error(TEXT("Failed to package app (result code: {0}). See log for more info."), result);
|
||||
return true;
|
||||
}
|
||||
// TODO: sign dmg
|
||||
LOG(Info, "Output application package: {0} (size: {1} MB)", dmgPath, FileSystem::GetFileSize(dmgPath) / 1024 / 1024);
|
||||
*/
|
||||
LOG(Info, "Output application package: {0} (size: {1} MB)", ipaPath, FileSystem::GetFileSize(ipaPath) / 1024 / 1024);
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -20,6 +20,7 @@ namespace Flax.Build
|
||||
/// </summary>
|
||||
public class Assembler
|
||||
{
|
||||
internal static string CacheFileName = "BuilderRules.dll";
|
||||
private string _cacheFolderPath;
|
||||
|
||||
/// <summary>
|
||||
@@ -70,7 +71,7 @@ namespace Flax.Build
|
||||
string cacheAssemblyPath = null, cacheInfoPath = null, buildInfo = null;
|
||||
if (_cacheFolderPath != null)
|
||||
{
|
||||
cacheAssemblyPath = Path.Combine(_cacheFolderPath, "BuilderRules.dll");
|
||||
cacheAssemblyPath = Path.Combine(_cacheFolderPath, CacheFileName);
|
||||
cacheInfoPath = Path.Combine(_cacheFolderPath, "BuilderRulesInfo.txt");
|
||||
|
||||
foreach (var sourceFile in SourceFiles)
|
||||
@@ -162,7 +163,7 @@ namespace Flax.Build
|
||||
syntaxTrees.Add(parsedSyntaxTree);
|
||||
}
|
||||
|
||||
var compilation = CSharpCompilation.Create("BuilderRulesCache.dll", syntaxTrees.ToArray(), defaultReferences, defaultCompilationOptions);
|
||||
var compilation = CSharpCompilation.Create(CacheFileName, syntaxTrees.ToArray(), defaultReferences, defaultCompilationOptions);
|
||||
EmitResult emitResult = compilation.Emit(memoryStream);
|
||||
|
||||
// Process warnings and errors
|
||||
|
||||
@@ -500,7 +500,8 @@ namespace Flax.Build
|
||||
return true;
|
||||
|
||||
// Skip Flax.Build rules assembly
|
||||
if (Path.GetFileName(x) == "BuilderRulesCache.dll")
|
||||
var fileName = Path.GetFileName(x);
|
||||
if (fileName == Assembler.CacheFileName || fileName == "BuilderRulesCache.dll")
|
||||
return true;
|
||||
|
||||
// Skip non-C# DLLs
|
||||
|
||||
@@ -172,7 +172,7 @@ namespace Flax.Build
|
||||
catch (Exception ex)
|
||||
{
|
||||
Log.Exception(ex);
|
||||
return 1;
|
||||
failed = true;
|
||||
}
|
||||
finally
|
||||
{
|
||||
|
||||
Reference in New Issue
Block a user