summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--modules/mono/csharp_script.cpp1
-rw-r--r--modules/mono/editor/bindings_generator.cpp4
-rw-r--r--modules/mono/glue/GodotSharp/GodotSharp/Core/Array.cs11
-rw-r--r--modules/mono/glue/GodotSharp/GodotSharp/Core/Bridge/ManagedCallbacks.cs2
-rw-r--r--modules/mono/glue/GodotSharp/GodotSharp/Core/Bridge/ScriptManagerBridge.cs17
-rw-r--r--modules/mono/glue/GodotSharp/GodotSharp/Core/Dictionary.cs5
-rw-r--r--modules/mono/glue/GodotSharp/GodotSharp/Core/DisposablesTracker.cs101
-rw-r--r--modules/mono/glue/GodotSharp/GodotSharp/Core/NodePath.cs7
-rw-r--r--modules/mono/glue/GodotSharp/GodotSharp/Core/Object.base.cs20
-rw-r--r--modules/mono/glue/GodotSharp/GodotSharp/Core/StringName.cs17
-rw-r--r--modules/mono/glue/GodotSharp/GodotSharp/GodotSharp.csproj1
-rw-r--r--modules/mono/godotsharp_defs.h5
-rw-r--r--modules/mono/mono_gd/gd_mono.cpp23
-rw-r--r--modules/mono/mono_gd/gd_mono_cache.cpp1
-rw-r--r--modules/mono/mono_gd/gd_mono_cache.h2
15 files changed, 185 insertions, 32 deletions
diff --git a/modules/mono/csharp_script.cpp b/modules/mono/csharp_script.cpp
index 80e127bbc2..52b0e82c6e 100644
--- a/modules/mono/csharp_script.cpp
+++ b/modules/mono/csharp_script.cpp
@@ -1581,6 +1581,7 @@ void CSharpLanguage::tie_managed_to_unmanaged_with_pre_setup(GCHandleIntPtr p_gc
CSharpInstance *instance = CAST_CSHARP_INSTANCE(p_unmanaged->get_script_instance());
if (!instance) {
+ // Native bindings don't need post-setup
return;
}
diff --git a/modules/mono/editor/bindings_generator.cpp b/modules/mono/editor/bindings_generator.cpp
index 89265d8f2b..fdc50e22f3 100644
--- a/modules/mono/editor/bindings_generator.cpp
+++ b/modules/mono/editor/bindings_generator.cpp
@@ -75,6 +75,10 @@ StringBuilder &operator<<(StringBuilder &r_sb, const char *p_cstring) {
#define CLOSE_BLOCK_L3 INDENT3 CLOSE_BLOCK
#define CLOSE_BLOCK_L4 INDENT4 CLOSE_BLOCK
+#define BINDINGS_GLOBAL_SCOPE_CLASS "GD"
+#define BINDINGS_PTR_FIELD "NativePtr"
+#define BINDINGS_NATIVE_NAME_FIELD "NativeName"
+
#define CS_PARAM_MEMORYOWN "memoryOwn"
#define CS_PARAM_METHODBIND "method"
#define CS_PARAM_INSTANCE "ptr"
diff --git a/modules/mono/glue/GodotSharp/GodotSharp/Core/Array.cs b/modules/mono/glue/GodotSharp/GodotSharp/Core/Array.cs
index 9fa221a0cc..cd6655b857 100644
--- a/modules/mono/glue/GodotSharp/GodotSharp/Core/Array.cs
+++ b/modules/mono/glue/GodotSharp/GodotSharp/Core/Array.cs
@@ -13,16 +13,21 @@ namespace Godot.Collections
/// interfacing with the engine. Otherwise prefer .NET collections
/// such as <see cref="System.Array"/> or <see cref="List{T}"/>.
/// </summary>
- public sealed class Array : IList, IDisposable
+ public sealed class Array :
+ IList,
+ IDisposable
{
internal godot_array.movable NativeValue;
+ private WeakReference<IDisposable> _weakReferenceToSelf;
+
/// <summary>
/// Constructs a new empty <see cref="Array"/>.
/// </summary>
public Array()
{
NativeValue = (godot_array.movable)NativeFuncs.godotsharp_array_new();
+ _weakReferenceToSelf = DisposablesTracker.RegisterDisposable(this);
}
/// <summary>
@@ -51,6 +56,8 @@ namespace Godot.Collections
throw new ArgumentNullException(nameof(array));
NativeValue = (godot_array.movable)NativeFuncs.godotsharp_array_new();
+ _weakReferenceToSelf = DisposablesTracker.RegisterDisposable(this);
+
int length = array.Length;
Resize(length);
@@ -64,6 +71,7 @@ namespace Godot.Collections
NativeValue = (godot_array.movable)(nativeValueToOwn.IsAllocated ?
nativeValueToOwn :
NativeFuncs.godotsharp_array_new());
+ _weakReferenceToSelf = DisposablesTracker.RegisterDisposable(this);
}
// Explicit name to make it very clear
@@ -88,6 +96,7 @@ namespace Godot.Collections
{
// Always dispose `NativeValue` even if disposing is true
NativeValue.DangerousSelfRef.Dispose();
+ DisposablesTracker.UnregisterDisposable(_weakReferenceToSelf);
}
/// <summary>
diff --git a/modules/mono/glue/GodotSharp/GodotSharp/Core/Bridge/ManagedCallbacks.cs b/modules/mono/glue/GodotSharp/GodotSharp/Core/Bridge/ManagedCallbacks.cs
index 1d19376cdd..bd939ef27d 100644
--- a/modules/mono/glue/GodotSharp/GodotSharp/Core/Bridge/ManagedCallbacks.cs
+++ b/modules/mono/glue/GodotSharp/GodotSharp/Core/Bridge/ManagedCallbacks.cs
@@ -33,6 +33,7 @@ namespace Godot.Bridge
public delegate* unmanaged<IntPtr, void> GCHandleBridge_FreeGCHandle;
public delegate* unmanaged<void> DebuggingUtils_InstallTraceListener;
public delegate* unmanaged<void> Dispatcher_InitializeDefaultGodotTaskScheduler;
+ public delegate* unmanaged<void> DisposablesTracker_OnGodotShuttingDown;
// @formatter:on
public static ManagedCallbacks Create()
@@ -65,6 +66,7 @@ namespace Godot.Bridge
GCHandleBridge_FreeGCHandle = &GCHandleBridge.FreeGCHandle,
DebuggingUtils_InstallTraceListener = &DebuggingUtils.InstallTraceListener,
Dispatcher_InitializeDefaultGodotTaskScheduler = &Dispatcher.InitializeDefaultGodotTaskScheduler,
+ DisposablesTracker_OnGodotShuttingDown = &DisposablesTracker.OnGodotShuttingDown,
// @formatter:on
};
}
diff --git a/modules/mono/glue/GodotSharp/GodotSharp/Core/Bridge/ScriptManagerBridge.cs b/modules/mono/glue/GodotSharp/GodotSharp/Core/Bridge/ScriptManagerBridge.cs
index 689d6cddbb..e87b7f4d4b 100644
--- a/modules/mono/glue/GodotSharp/GodotSharp/Core/Bridge/ScriptManagerBridge.cs
+++ b/modules/mono/glue/GodotSharp/GodotSharp/Core/Bridge/ScriptManagerBridge.cs
@@ -44,16 +44,19 @@ namespace Godot.Bridge
internal static unsafe IntPtr CreateManagedForGodotObjectBinding(godot_string_name* nativeTypeName,
IntPtr godotObject)
{
+ // TODO: Optimize with source generators and delegate pointers
+
try
{
Type nativeType = TypeGetProxyClass(nativeTypeName);
var obj = (Object)FormatterServices.GetUninitializedObject(nativeType);
- obj.NativePtr = godotObject;
-
var ctor = nativeType.GetConstructor(
BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance,
null, Type.EmptyTypes, null);
+
+ obj.NativePtr = godotObject;
+
_ = ctor!.Invoke(obj, null);
return GCHandle.ToIntPtr(GCHandle.Alloc(obj));
@@ -70,14 +73,14 @@ namespace Godot.Bridge
IntPtr godotObject,
godot_variant** args, int argCount)
{
+ // TODO: Optimize with source generators and delegate pointers
+
try
{
// Performance is not critical here as this will be replaced with source generators.
Type scriptType = _scriptBridgeMap[scriptPtr];
var obj = (Object)FormatterServices.GetUninitializedObject(scriptType);
- obj.NativePtr = godotObject;
-
var ctor = scriptType
.GetConstructors(BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance)
.Where(c => c.GetParameters().Length == argCount)
@@ -108,7 +111,11 @@ namespace Godot.Bridge
*args[i], parameters[i].ParameterType);
}
- ctor.Invoke(obj, invokeParams);
+ obj.NativePtr = godotObject;
+
+ _ = ctor.Invoke(obj, invokeParams);
+
+
return true.ToGodotBool();
}
catch (Exception e)
diff --git a/modules/mono/glue/GodotSharp/GodotSharp/Core/Dictionary.cs b/modules/mono/glue/GodotSharp/GodotSharp/Core/Dictionary.cs
index 89fc2210b8..4a99872e7b 100644
--- a/modules/mono/glue/GodotSharp/GodotSharp/Core/Dictionary.cs
+++ b/modules/mono/glue/GodotSharp/GodotSharp/Core/Dictionary.cs
@@ -18,12 +18,15 @@ namespace Godot.Collections
{
internal godot_dictionary.movable NativeValue;
+ private WeakReference<IDisposable> _weakReferenceToSelf;
+
/// <summary>
/// Constructs a new empty <see cref="Dictionary"/>.
/// </summary>
public Dictionary()
{
NativeValue = (godot_dictionary.movable)NativeFuncs.godotsharp_dictionary_new();
+ _weakReferenceToSelf = DisposablesTracker.RegisterDisposable(this);
}
/// <summary>
@@ -45,6 +48,7 @@ namespace Godot.Collections
NativeValue = (godot_dictionary.movable)(nativeValueToOwn.IsAllocated ?
nativeValueToOwn :
NativeFuncs.godotsharp_dictionary_new());
+ _weakReferenceToSelf = DisposablesTracker.RegisterDisposable(this);
}
// Explicit name to make it very clear
@@ -69,6 +73,7 @@ namespace Godot.Collections
{
// Always dispose `NativeValue` even if disposing is true
NativeValue.DangerousSelfRef.Dispose();
+ DisposablesTracker.UnregisterDisposable(_weakReferenceToSelf);
}
/// <summary>
diff --git a/modules/mono/glue/GodotSharp/GodotSharp/Core/DisposablesTracker.cs b/modules/mono/glue/GodotSharp/GodotSharp/Core/DisposablesTracker.cs
new file mode 100644
index 0000000000..bf3b3b083a
--- /dev/null
+++ b/modules/mono/glue/GodotSharp/GodotSharp/Core/DisposablesTracker.cs
@@ -0,0 +1,101 @@
+using System;
+using System.Collections.Concurrent;
+using System.Runtime.InteropServices;
+using System.Runtime.Loader;
+using Godot.NativeInterop;
+
+#nullable enable
+
+namespace Godot
+{
+ internal static class DisposablesTracker
+ {
+ static DisposablesTracker()
+ {
+ AssemblyLoadContext.Default.Unloading += _ => OnUnloading();
+ }
+
+ [UnmanagedCallersOnly]
+ internal static void OnGodotShuttingDown()
+ {
+ try
+ {
+ OnUnloading();
+ }
+ catch (Exception e)
+ {
+ ExceptionUtils.DebugUnhandledException(e);
+ }
+ }
+
+ private static void OnUnloading()
+ {
+ bool isStdoutVerbose;
+
+ try
+ {
+ isStdoutVerbose = OS.IsStdoutVerbose();
+ }
+ catch (ObjectDisposedException)
+ {
+ // OS singleton already disposed. Maybe OnUnloading was called twice.
+ isStdoutVerbose = false;
+ }
+
+ if (isStdoutVerbose)
+ GD.Print("Unloading: Disposing tracked instances...");
+
+ // Dispose Godot Objects first, and only then dispose other disposables
+ // like StringName, NodePath, Godot.Collections.Array/Dictionary, etc.
+ // The Godot Object Dispose() method may need any of the later instances.
+
+ foreach (WeakReference<Object> item in GodotObjectInstances.Keys)
+ {
+ if (item.TryGetTarget(out Object? self))
+ self.Dispose();
+ }
+
+ foreach (WeakReference<IDisposable> item in OtherInstances.Keys)
+ {
+ if (item.TryGetTarget(out IDisposable? self))
+ self.Dispose();
+ }
+
+ if (isStdoutVerbose)
+ GD.Print("Unloading: Finished disposing tracked instances.");
+ }
+
+ // ReSharper disable once RedundantNameQualifier
+ private static ConcurrentDictionary<WeakReference<Godot.Object>, object?> GodotObjectInstances { get; } =
+ new();
+
+ private static ConcurrentDictionary<WeakReference<IDisposable>, object?> OtherInstances { get; } =
+ new();
+
+ public static WeakReference<Object> RegisterGodotObject(Object godotObject)
+ {
+ var weakReferenceToSelf = new WeakReference<Object>(godotObject);
+ GodotObjectInstances.TryAdd(weakReferenceToSelf, null);
+ return weakReferenceToSelf;
+ }
+
+ public static WeakReference<IDisposable> RegisterDisposable(IDisposable disposable)
+ {
+ var weakReferenceToSelf = new WeakReference<IDisposable>(disposable);
+ OtherInstances.TryAdd(weakReferenceToSelf, null);
+ return weakReferenceToSelf;
+ }
+
+ public static void UnregisterGodotObject(WeakReference<Object> weakReference)
+ {
+ if (!GodotObjectInstances.TryRemove(weakReference, out _))
+ throw new ArgumentException("Godot Object not registered", nameof(weakReference));
+ }
+
+ public static void UnregisterDisposable(WeakReference<IDisposable> weakReference)
+ {
+ if (!OtherInstances.TryRemove(weakReference, out _))
+ throw new ArgumentException("Disposable not registered", nameof(weakReference));
+ }
+ }
+}
diff --git a/modules/mono/glue/GodotSharp/GodotSharp/Core/NodePath.cs b/modules/mono/glue/GodotSharp/GodotSharp/Core/NodePath.cs
index d7b736fbcf..6edc19c4d6 100644
--- a/modules/mono/glue/GodotSharp/GodotSharp/Core/NodePath.cs
+++ b/modules/mono/glue/GodotSharp/GodotSharp/Core/NodePath.cs
@@ -43,6 +43,8 @@ namespace Godot
{
internal godot_node_path.movable NativeValue;
+ private WeakReference<IDisposable> _weakReferenceToSelf;
+
~NodePath()
{
Dispose(false);
@@ -61,11 +63,13 @@ namespace Godot
{
// Always dispose `NativeValue` even if disposing is true
NativeValue.DangerousSelfRef.Dispose();
+ DisposablesTracker.UnregisterDisposable(_weakReferenceToSelf);
}
private NodePath(godot_node_path nativeValueToOwn)
{
NativeValue = (godot_node_path.movable)nativeValueToOwn;
+ _weakReferenceToSelf = DisposablesTracker.RegisterDisposable(this);
}
// Explicit name to make it very clear
@@ -111,7 +115,10 @@ namespace Godot
public NodePath(string path)
{
if (!string.IsNullOrEmpty(path))
+ {
NativeValue = (godot_node_path.movable)NativeFuncs.godotsharp_node_path_new_from_string(path);
+ _weakReferenceToSelf = DisposablesTracker.RegisterDisposable(this);
+ }
}
/// <summary>
diff --git a/modules/mono/glue/GodotSharp/GodotSharp/Core/Object.base.cs b/modules/mono/glue/GodotSharp/GodotSharp/Core/Object.base.cs
index dbffd1d5d1..a3a4c2599e 100644
--- a/modules/mono/glue/GodotSharp/GodotSharp/Core/Object.base.cs
+++ b/modules/mono/glue/GodotSharp/GodotSharp/Core/Object.base.cs
@@ -11,7 +11,9 @@ namespace Godot
private Type _cachedType = typeof(Object);
internal IntPtr NativePtr;
- internal bool MemoryOwn;
+ private bool _memoryOwn;
+
+ private WeakReference<Object> _weakReferenceToSelf;
/// <summary>
/// Constructs a new <see cref="Object"/>.
@@ -34,6 +36,8 @@ namespace Godot
GetType(), _cachedType);
}
+ _weakReferenceToSelf = DisposablesTracker.RegisterGodotObject(this);
+
_InitializeGodotScriptInstanceInternals();
}
@@ -61,7 +65,7 @@ namespace Godot
internal Object(bool memoryOwn)
{
- MemoryOwn = memoryOwn;
+ _memoryOwn = memoryOwn;
}
/// <summary>
@@ -74,7 +78,12 @@ namespace Godot
if (instance == null)
return IntPtr.Zero;
- if (instance._disposed)
+ // We check if NativePtr is null because this may be called by the debugger.
+ // If the debugger puts a breakpoint in one of the base constructors, before
+ // NativePtr is assigned, that would result in UB or crashes when calling
+ // native functions that receive the pointer, which can happen because the
+ // debugger calls ToString() and tries to get the value of properties.
+ if (instance._disposed || instance.NativePtr == IntPtr.Zero)
throw new ObjectDisposedException(instance.GetType().FullName);
return instance.NativePtr;
@@ -104,9 +113,8 @@ namespace Godot
if (NativePtr != IntPtr.Zero)
{
- if (MemoryOwn)
+ if (_memoryOwn)
{
- MemoryOwn = false;
NativeFuncs.godotsharp_internal_refcounted_disposed(NativePtr, (!disposing).ToGodotBool());
}
else
@@ -117,6 +125,8 @@ namespace Godot
NativePtr = IntPtr.Zero;
}
+ DisposablesTracker.UnregisterGodotObject(_weakReferenceToSelf);
+
_disposed = true;
}
diff --git a/modules/mono/glue/GodotSharp/GodotSharp/Core/StringName.cs b/modules/mono/glue/GodotSharp/GodotSharp/Core/StringName.cs
index 3a415d3deb..b993a1b3e9 100644
--- a/modules/mono/glue/GodotSharp/GodotSharp/Core/StringName.cs
+++ b/modules/mono/glue/GodotSharp/GodotSharp/Core/StringName.cs
@@ -14,6 +14,8 @@ namespace Godot
{
internal godot_string_name.movable NativeValue;
+ private WeakReference<IDisposable> _weakReferenceToSelf;
+
~StringName()
{
Dispose(false);
@@ -32,11 +34,13 @@ namespace Godot
{
// Always dispose `NativeValue` even if disposing is true
NativeValue.DangerousSelfRef.Dispose();
+ DisposablesTracker.UnregisterDisposable(_weakReferenceToSelf);
}
private StringName(godot_string_name nativeValueToOwn)
{
NativeValue = (godot_string_name.movable)nativeValueToOwn;
+ _weakReferenceToSelf = DisposablesTracker.RegisterDisposable(this);
}
// Explicit name to make it very clear
@@ -57,7 +61,10 @@ namespace Godot
public StringName(string name)
{
if (!string.IsNullOrEmpty(name))
+ {
NativeValue = (godot_string_name.movable)NativeFuncs.godotsharp_string_name_new_from_string(name);
+ _weakReferenceToSelf = DisposablesTracker.RegisterDisposable(this);
+ }
}
/// <summary>
@@ -138,5 +145,15 @@ namespace Godot
{
return NativeValue.DangerousSelfRef == other;
}
+
+ public override bool Equals(object obj)
+ {
+ return ReferenceEquals(this, obj) || (obj is StringName other && Equals(other));
+ }
+
+ public override int GetHashCode()
+ {
+ return NativeValue.GetHashCode();
+ }
}
}
diff --git a/modules/mono/glue/GodotSharp/GodotSharp/GodotSharp.csproj b/modules/mono/glue/GodotSharp/GodotSharp/GodotSharp.csproj
index d5bbbfb7ca..f1a397f8fa 100644
--- a/modules/mono/glue/GodotSharp/GodotSharp/GodotSharp.csproj
+++ b/modules/mono/glue/GodotSharp/GodotSharp/GodotSharp.csproj
@@ -60,6 +60,7 @@
<Compile Include="Core\GodotTaskScheduler.cs" />
<Compile Include="Core\GodotTraceListener.cs" />
<Compile Include="Core\GodotUnhandledExceptionEvent.cs" />
+ <Compile Include="Core\DisposablesTracker.cs" />
<Compile Include="Core\Interfaces\IAwaitable.cs" />
<Compile Include="Core\Interfaces\IAwaiter.cs" />
<Compile Include="Core\Interfaces\ISerializationListener.cs" />
diff --git a/modules/mono/godotsharp_defs.h b/modules/mono/godotsharp_defs.h
index 872c875cbe..a81a52e4b8 100644
--- a/modules/mono/godotsharp_defs.h
+++ b/modules/mono/godotsharp_defs.h
@@ -33,15 +33,10 @@
#define BINDINGS_NAMESPACE "Godot"
#define BINDINGS_NAMESPACE_COLLECTIONS BINDINGS_NAMESPACE ".Collections"
-#define BINDINGS_NAMESPACE_BRIDGE BINDINGS_NAMESPACE ".Bridge"
-#define BINDINGS_GLOBAL_SCOPE_CLASS "GD"
-#define BINDINGS_PTR_FIELD "NativePtr"
-#define BINDINGS_NATIVE_NAME_FIELD "NativeName"
#define API_SOLUTION_NAME "GodotSharp"
#define CORE_API_ASSEMBLY_NAME "GodotSharp"
#define EDITOR_API_ASSEMBLY_NAME "GodotSharpEditor"
#define TOOLS_ASM_NAME "GodotTools"
-#define TOOLS_PROJECT_EDITOR_ASM_NAME "GodotTools.ProjectEditor"
#define BINDINGS_CLASS_NATIVECALLS "NativeCalls"
#define BINDINGS_CLASS_NATIVECALLS_EDITOR "EditorNativeCalls"
diff --git a/modules/mono/mono_gd/gd_mono.cpp b/modules/mono/mono_gd/gd_mono.cpp
index dcda799f32..7a3fd1af10 100644
--- a/modules/mono/mono_gd/gd_mono.cpp
+++ b/modules/mono/mono_gd/gd_mono.cpp
@@ -471,26 +471,17 @@ GDMono::GDMono() {
}
GDMono::~GDMono() {
+ finalizing_scripts_domain = true;
+
if (is_runtime_initialized()) {
-#warning "TODO assembly unloading for cleanup of disposables (including managed RefCounteds)"
-#if 0
- if (scripts_domain) {
- Error err = _unload_scripts_domain();
- if (err != OK) {
- ERR_PRINT("Mono: Failed to unload scripts domain.");
- }
+ if (GDMonoCache::godot_api_cache_updated) {
+ GDMonoCache::managed_callbacks.DisposablesTracker_OnGodotShuttingDown();
}
-
- print_verbose("Mono: Runtime cleanup...");
-
- mono_jit_cleanup(root_domain);
-
- print_verbose("Mono: Finalized");
-#endif
-
- runtime_initialized = false;
}
+ finalizing_scripts_domain = false;
+ runtime_initialized = false;
+
#if defined(ANDROID_ENABLED)
gdmono::android::support::cleanup();
#endif
diff --git a/modules/mono/mono_gd/gd_mono_cache.cpp b/modules/mono/mono_gd/gd_mono_cache.cpp
index 17addfb49d..e8b25cb119 100644
--- a/modules/mono/mono_gd/gd_mono_cache.cpp
+++ b/modules/mono/mono_gd/gd_mono_cache.cpp
@@ -68,6 +68,7 @@ void update_godot_api_cache(const ManagedCallbacks &p_managed_callbacks) {
CHECK_CALLBACK_NOT_NULL(GCHandleBridge, FreeGCHandle);
CHECK_CALLBACK_NOT_NULL(DebuggingUtils, InstallTraceListener);
CHECK_CALLBACK_NOT_NULL(Dispatcher, InitializeDefaultGodotTaskScheduler);
+ CHECK_CALLBACK_NOT_NULL(DisposablesTracker, OnGodotShuttingDown);
managed_callbacks = p_managed_callbacks;
diff --git a/modules/mono/mono_gd/gd_mono_cache.h b/modules/mono/mono_gd/gd_mono_cache.h
index 56bf4cef94..17c8c9fa51 100644
--- a/modules/mono/mono_gd/gd_mono_cache.h
+++ b/modules/mono/mono_gd/gd_mono_cache.h
@@ -78,6 +78,7 @@ struct ManagedCallbacks {
using FuncGCHandleBridge_FreeGCHandle = void(GD_CLR_STDCALL *)(GCHandleIntPtr);
using FuncDebuggingUtils_InstallTraceListener = void(GD_CLR_STDCALL *)();
using FuncDispatcher_InitializeDefaultGodotTaskScheduler = void(GD_CLR_STDCALL *)();
+ using FuncDisposablesTracker_OnGodotShuttingDown = void(GD_CLR_STDCALL *)();
FuncSignalAwaiter_SignalCallback SignalAwaiter_SignalCallback;
FuncDelegateUtils_InvokeWithVariantArgs DelegateUtils_InvokeWithVariantArgs;
@@ -104,6 +105,7 @@ struct ManagedCallbacks {
FuncGCHandleBridge_FreeGCHandle GCHandleBridge_FreeGCHandle;
FuncDebuggingUtils_InstallTraceListener DebuggingUtils_InstallTraceListener;
FuncDispatcher_InitializeDefaultGodotTaskScheduler Dispatcher_InitializeDefaultGodotTaskScheduler;
+ FuncDisposablesTracker_OnGodotShuttingDown DisposablesTracker_OnGodotShuttingDown;
};
extern ManagedCallbacks managed_callbacks;