Browse Source

ManagedCallable: use delegate target instead of middleman when possible

If the delegate target is an Object, the connected signal will be registered in that object instead of the middleman. So when that object is destroyed, the signal will be properly disconnected.
Patrick Dawson 2 years ago
parent
commit
161f295f52

+ 1 - 1
modules/mono/glue/GodotSharp/GodotSharp/Core/Callable.cs

@@ -72,7 +72,7 @@ namespace Godot
         /// <param name="delegate">Delegate method that will be called.</param>
         public Callable(Delegate @delegate)
         {
-            _target = null;
+            _target = @delegate?.Target as Object;
             _method = null;
             _delegate = @delegate;
         }

+ 2 - 1
modules/mono/glue/GodotSharp/GodotSharp/Core/NativeInterop/Marshaling.cs

@@ -721,8 +721,9 @@ namespace Godot.NativeInterop
             if (p_managed_callable.Delegate != null)
             {
                 var gcHandle = CustomGCHandle.AllocStrong(p_managed_callable.Delegate);
+                IntPtr objectPtr = p_managed_callable.Target != null ? Object.GetPtr(p_managed_callable.Target) : IntPtr.Zero;
                 NativeFuncs.godotsharp_callable_new_with_delegate(
-                    GCHandle.ToIntPtr(gcHandle), out godot_callable callable);
+                    GCHandle.ToIntPtr(gcHandle), objectPtr, out godot_callable callable);
                 return callable;
             }
             else

+ 1 - 1
modules/mono/glue/GodotSharp/GodotSharp/Core/NativeInterop/NativeFuncs.cs

@@ -141,7 +141,7 @@ namespace Godot.NativeInterop
         public static partial void godotsharp_packed_string_array_add(ref godot_packed_string_array r_dest,
             in godot_string p_element);
 
-        public static partial void godotsharp_callable_new_with_delegate(IntPtr p_delegate_handle,
+        public static partial void godotsharp_callable_new_with_delegate(IntPtr p_delegate_handle, IntPtr p_object,
             out godot_callable r_callable);
 
         internal static partial godot_bool godotsharp_callable_get_data_for_marshalling(in godot_callable p_callable,

+ 3 - 2
modules/mono/glue/runtime_interop.cpp

@@ -447,9 +447,10 @@ void godotsharp_packed_string_array_add(PackedStringArray *r_dest, const String
 	r_dest->append(*p_element);
 }
 
-void godotsharp_callable_new_with_delegate(GCHandleIntPtr p_delegate_handle, Callable *r_callable) {
+void godotsharp_callable_new_with_delegate(GCHandleIntPtr p_delegate_handle, const Object *p_object, Callable *r_callable) {
 	// TODO: Use pooling for ManagedCallable instances.
-	CallableCustom *managed_callable = memnew(ManagedCallable(p_delegate_handle));
+	ObjectID objid = p_object ? p_object->get_instance_id() : ObjectID();
+	CallableCustom *managed_callable = memnew(ManagedCallable(p_delegate_handle, objid));
 	memnew_placement(r_callable, Callable(managed_callable));
 }
 

+ 4 - 2
modules/mono/managed_callable.cpp

@@ -79,7 +79,9 @@ CallableCustom::CompareLessFunc ManagedCallable::get_compare_less_func() const {
 }
 
 ObjectID ManagedCallable::get_object() const {
-	// TODO: If the delegate target extends Godot.Object, use that instead!
+	if (object_id != ObjectID()) {
+		return object_id;
+	}
 	return CSharpLanguage::get_singleton()->get_managed_callable_middleman()->get_instance_id();
 }
 
@@ -104,7 +106,7 @@ void ManagedCallable::release_delegate_handle() {
 
 // Why you do this clang-format...
 /* clang-format off */
-ManagedCallable::ManagedCallable(GCHandleIntPtr p_delegate_handle) : delegate_handle(p_delegate_handle) {
+ManagedCallable::ManagedCallable(GCHandleIntPtr p_delegate_handle, ObjectID p_object_id) : delegate_handle(p_delegate_handle), object_id(p_object_id) {
 #ifdef GD_MONO_HOT_RELOAD
 	{
 		MutexLock lock(instances_mutex);

+ 2 - 1
modules/mono/managed_callable.h

@@ -40,6 +40,7 @@
 class ManagedCallable : public CallableCustom {
 	friend class CSharpLanguage;
 	GCHandleIntPtr delegate_handle;
+	ObjectID object_id;
 
 #ifdef GD_MONO_HOT_RELOAD
 	SelfList<ManagedCallable> self_instance = this;
@@ -66,7 +67,7 @@ public:
 
 	void release_delegate_handle();
 
-	ManagedCallable(GCHandleIntPtr p_delegate_handle);
+	ManagedCallable(GCHandleIntPtr p_delegate_handle, ObjectID p_object_id);
 	~ManagedCallable();
 };