From 62c2008cbcb1f395a4e3c20f19263187381b0caa Mon Sep 17 00:00:00 2001 From: Wojtek Figat Date: Fri, 22 Sep 2023 12:50:50 +0200 Subject: [PATCH] Fix crash in C# `JobSystem.Dispatch` due to GC collecting Delegate object --- Source/Engine/Engine/NativeInterop.cs | 14 ++++++++++++++ .../Bindings/BindingsGenerator.CSharp.cs | 2 +- 2 files changed, 15 insertions(+), 1 deletion(-) diff --git a/Source/Engine/Engine/NativeInterop.cs b/Source/Engine/Engine/NativeInterop.cs index dfef614b3..e0ef84a61 100644 --- a/Source/Engine/Engine/NativeInterop.cs +++ b/Source/Engine/Engine/NativeInterop.cs @@ -148,6 +148,20 @@ namespace FlaxEngine.Interop NativeMemory.AlignedFree(ptr); } + /// + /// Converts a delegate into a function pointer that is callable from unmanaged code via but cached delegate to prevent collecting it by GC. + /// + /// The type of delegate to convert. + /// The delegate to be passed to unmanaged code. + /// A value that can be passed to unmanaged code, which, in turn, can use it to call the underlying managed delegate. + public static IntPtr GetFunctionPointerForDelegate(TDelegate d) where TDelegate : notnull + { + // Example use-case: C# script runs actions via JobSystem.Dispatch which causes crash due to GC collecting Delegate object + ManagedHandle.Alloc(d, GCHandleType.Weak); + + return Marshal.GetFunctionPointerForDelegate(d); + } + /// /// Converts array of GC Handles from native runtime to managed array. /// diff --git a/Source/Tools/Flax.Build/Bindings/BindingsGenerator.CSharp.cs b/Source/Tools/Flax.Build/Bindings/BindingsGenerator.CSharp.cs index 6a9aa7669..3685cb271 100644 --- a/Source/Tools/Flax.Build/Bindings/BindingsGenerator.CSharp.cs +++ b/Source/Tools/Flax.Build/Bindings/BindingsGenerator.CSharp.cs @@ -464,7 +464,7 @@ namespace Flax.Build.Bindings return "FlaxEngine.Object.GetUnmanagedPtr({0})"; case "Function": // delegate - return "Marshal.GetFunctionPointerForDelegate({0})"; + return "NativeInterop.GetFunctionPointerForDelegate({0})"; default: var apiType = FindApiTypeInfo(buildData, typeInfo, caller); if (apiType != null)