diff options
Diffstat (limited to 'modules/mono/mono_gd')
-rw-r--r-- | modules/mono/mono_gd/gd_mono_internals.cpp | 43 | ||||
-rw-r--r-- | modules/mono/mono_gd/gd_mono_utils.cpp | 15 |
2 files changed, 51 insertions, 7 deletions
diff --git a/modules/mono/mono_gd/gd_mono_internals.cpp b/modules/mono/mono_gd/gd_mono_internals.cpp index 0daa394e63..14ec24466b 100644 --- a/modules/mono/mono_gd/gd_mono_internals.cpp +++ b/modules/mono/mono_gd/gd_mono_internals.cpp @@ -34,6 +34,8 @@ #include "../mono_gc_handle.h" #include "../utils/macros.h" #include "../utils/thread_local.h" +#include "gd_mono_class.h" +#include "gd_mono_marshal.h" #include "gd_mono_utils.h" #include <mono/metadata/exception.h> @@ -55,10 +57,49 @@ void tie_managed_to_unmanaged(MonoObject *managed, Object *unmanaged) { CRASH_COND(!klass); + GDMonoClass *native = GDMonoUtils::get_class_native_base(klass); + + CRASH_COND(native == NULL); + + if (native == klass) { + // If it's just a wrapper Godot class and not a custom inheriting class, then attach a + // script binding instead. One of the advantages of this is that if a script is attached + // later and it's not a C# script, then the managed object won't have to be disposed. + // Another reason for doing this is that this instance could outlive CSharpLanguage, which would + // be problematic when using a script. See: https://github.com/godotengine/godot/issues/25621 + + CSharpScriptBinding script_binding; + + script_binding.inited = true; + script_binding.type_name = NATIVE_GDMONOCLASS_NAME(klass); + script_binding.wrapper_class = klass; + script_binding.gchandle = MonoGCHandle::create_strong(managed); + + Reference *ref = Object::cast_to<Reference>(unmanaged); + if (ref) { + // Unsafe refcount increment. The managed instance also counts as a reference. + // This way if the unmanaged world has no references to our owner + // but the managed instance is alive, the refcount will be 1 instead of 0. + // See: godot_icall_Reference_Dtor(MonoObject *p_obj, Object *p_ptr) + + ref->reference(); + } + + // The object was just created, no script instance binding should have been attached + CRASH_COND(unmanaged->has_script_instance_binding(CSharpLanguage::get_singleton()->get_language_index())); + + void *data = (void *)CSharpLanguage::get_singleton()->insert_script_binding(unmanaged, script_binding); + + // Should be thread safe because the object was just created and nothing else should be referencing it + unmanaged->set_script_instance_binding(CSharpLanguage::get_singleton()->get_language_index(), data); + + return; + } + Ref<MonoGCHandle> gchandle = ref ? MonoGCHandle::create_weak(managed) : MonoGCHandle::create_strong(managed); - Ref<CSharpScript> script = CSharpScript::create_for_managed_type(klass); + Ref<CSharpScript> script = CSharpScript::create_for_managed_type(klass, native); CRASH_COND(script.is_null()); diff --git a/modules/mono/mono_gd/gd_mono_utils.cpp b/modules/mono/mono_gd/gd_mono_utils.cpp index ac6ccac3a7..3b97339fea 100644 --- a/modules/mono/mono_gd/gd_mono_utils.cpp +++ b/modules/mono/mono_gd/gd_mono_utils.cpp @@ -39,6 +39,7 @@ #include "../csharp_script.h" #include "../utils/macros.h" +#include "../utils/mutex_utils.h" #include "gd_mono.h" #include "gd_mono_class.h" #include "gd_mono_marshal.h" @@ -281,17 +282,19 @@ MonoObject *unmanaged_get_managed(Object *unmanaged) { void *data = unmanaged->get_script_instance_binding(CSharpLanguage::get_singleton()->get_language_index()); - if (!data) - return NULL; + ERR_FAIL_NULL_V(data, NULL); CSharpScriptBinding &script_binding = ((Map<Object *, CSharpScriptBinding>::Element *)data)->value(); if (!script_binding.inited) { - // Already had a binding that needs to be setup - CSharpLanguage::get_singleton()->setup_csharp_script_binding(script_binding, unmanaged); + SCOPED_MUTEX_LOCK(CSharpLanguage::get_singleton()->get_language_bind_mutex()); + + if (!script_binding.inited) { // Other thread may have set it up + // Already had a binding that needs to be setup + CSharpLanguage::get_singleton()->setup_csharp_script_binding(script_binding, unmanaged); - if (!script_binding.inited) - return NULL; + ERR_FAIL_COND_V(!script_binding.inited, NULL); + } } Ref<MonoGCHandle> &gchandle = script_binding.gchandle; |