diff options
-rw-r--r-- | core/object/object.cpp | 15 | ||||
-rw-r--r-- | core/object/object.h | 1 | ||||
-rw-r--r-- | modules/mono/csharp_script.cpp | 191 | ||||
-rw-r--r-- | modules/mono/csharp_script.h | 17 | ||||
-rw-r--r-- | modules/mono/glue/base_object_glue.cpp | 10 | ||||
-rw-r--r-- | modules/mono/mono_gd/gd_mono_internals.cpp | 7 | ||||
-rw-r--r-- | modules/mono/mono_gd/gd_mono_utils.cpp | 19 |
7 files changed, 137 insertions, 123 deletions
diff --git a/core/object/object.cpp b/core/object/object.cpp index c191109a8f..2bb4b981b9 100644 --- a/core/object/object.cpp +++ b/core/object/object.cpp @@ -1811,6 +1811,21 @@ void *Object::get_instance_binding(void *p_token, const GDNativeInstanceBindingC return binding; } +bool Object::has_instance_binding(void *p_token) { + bool found = false; + _instance_binding_mutex.lock(); + for (uint32_t i = 0; i < _instance_binding_count; i++) { + if (_instance_bindings[i].token == p_token) { + found = true; + break; + } + } + + _instance_binding_mutex.unlock(); + + return found; +} + void Object::_construct_object(bool p_reference) { type_is_reference = p_reference; _instance_id = ObjectDB::add_instance(this); diff --git a/core/object/object.h b/core/object/object.h index 1f5e17c99f..6523105820 100644 --- a/core/object/object.h +++ b/core/object/object.h @@ -809,6 +809,7 @@ public: void *get_instance_binding(void *p_token, const GDNativeInstanceBindingCallbacks *p_callbacks); // Used on creation by binding only. void set_instance_binding(void *p_token, void *p_binding, const GDNativeInstanceBindingCallbacks *p_callbacks); + bool has_instance_binding(void *p_token); void clear_internal_resource_paths(); diff --git a/modules/mono/csharp_script.cpp b/modules/mono/csharp_script.cpp index 520262c0eb..5c5a4fc1ec 100644 --- a/modules/mono/csharp_script.cpp +++ b/modules/mono/csharp_script.cpp @@ -82,6 +82,12 @@ static bool _create_project_solution_if_needed() { CSharpLanguage *CSharpLanguage::singleton = nullptr; +GDNativeInstanceBindingCallbacks CSharpLanguage::_instance_binding_callbacks = { + &_instance_binding_create_callback, + &_instance_binding_free_callback, + &_instance_binding_reference_callback +}; + String CSharpLanguage::get_name() const { return "C#"; } @@ -1444,46 +1450,46 @@ bool CSharpLanguage::setup_csharp_script_binding(CSharpScriptBinding &r_script_b return true; } -void *CSharpLanguage::alloc_instance_binding_data(Object *p_object) { - MutexLock lock(language_bind_mutex); +Map<Object *, CSharpScriptBinding>::Element *CSharpLanguage::insert_script_binding(Object *p_object, const CSharpScriptBinding &p_script_binding) { + return script_bindings.insert(p_object, p_script_binding); +} + +void *CSharpLanguage::_instance_binding_create_callback(void *, void *p_instance) { + CSharpLanguage *csharp_lang = CSharpLanguage::get_singleton(); - Map<Object *, CSharpScriptBinding>::Element *match = script_bindings.find(p_object); + MutexLock lock(csharp_lang->language_bind_mutex); + + Map<Object *, CSharpScriptBinding>::Element *match = csharp_lang->script_bindings.find((Object *)p_instance); if (match) { return (void *)match; } CSharpScriptBinding script_binding; - if (!setup_csharp_script_binding(script_binding, p_object)) { - return nullptr; - } - - return (void *)insert_script_binding(p_object, script_binding); + return (void *)csharp_lang->insert_script_binding((Object *)p_instance, script_binding); } -Map<Object *, CSharpScriptBinding>::Element *CSharpLanguage::insert_script_binding(Object *p_object, const CSharpScriptBinding &p_script_binding) { - return script_bindings.insert(p_object, p_script_binding); -} +void CSharpLanguage::_instance_binding_free_callback(void *, void *, void *p_binding) { + CSharpLanguage *csharp_lang = CSharpLanguage::get_singleton(); -void CSharpLanguage::free_instance_binding_data(void *p_data) { if (GDMono::get_singleton() == nullptr) { #ifdef DEBUG_ENABLED - CRASH_COND(!script_bindings.is_empty()); + CRASH_COND(!csharp_lang->script_bindings.is_empty()); #endif // Mono runtime finalized, all the gchandle bindings were already released return; } - if (finalizing) { + if (csharp_lang->finalizing) { return; // inside CSharpLanguage::finish(), all the gchandle bindings are released there } GD_MONO_ASSERT_THREAD_ATTACHED; { - MutexLock lock(language_bind_mutex); + MutexLock lock(csharp_lang->language_bind_mutex); - Map<Object *, CSharpScriptBinding>::Element *data = (Map<Object *, CSharpScriptBinding>::Element *)p_data; + Map<Object *, CSharpScriptBinding>::Element *data = (Map<Object *, CSharpScriptBinding>::Element *)p_binding; CSharpScriptBinding &script_binding = data->value(); @@ -1497,92 +1503,110 @@ void CSharpLanguage::free_instance_binding_data(void *p_data) { script_binding.gchandle.release(); } - script_bindings.erase(data); + csharp_lang->script_bindings.erase(data); } } -void CSharpLanguage::refcount_incremented_instance_binding(Object *p_object) { -#if 0 - RefCounted *rc_owner = Object::cast_to<RefCounted>(p_object); +GDNativeBool CSharpLanguage::_instance_binding_reference_callback(void *p_token, void *p_binding, GDNativeBool p_reference) { + CRASH_COND(!p_binding); + + CSharpScriptBinding &script_binding = ((Map<Object *, CSharpScriptBinding>::Element *)p_binding)->get(); + + RefCounted *rc_owner = Object::cast_to<RefCounted>(script_binding.owner); #ifdef DEBUG_ENABLED CRASH_COND(!rc_owner); - CRASH_COND(!p_object->has_script_instance_binding(get_language_index())); #endif - void *data = p_object->get_script_instance_binding(get_language_index()); - CRASH_COND(!data); - - CSharpScriptBinding &script_binding = ((Map<Object *, CSharpScriptBinding>::Element *)data)->get(); MonoGCHandleData &gchandle = script_binding.gchandle; + int refcount = rc_owner->reference_get_count(); + if (!script_binding.inited) { - return; + return refcount == 0; } - if (rc_owner->reference_get_count() > 1 && gchandle.is_weak()) { // The managed side also holds a reference, hence 1 instead of 0 - GD_MONO_SCOPE_THREAD_ATTACH; + if (p_reference) { + // Refcount incremented + if (refcount > 1 && gchandle.is_weak()) { // The managed side also holds a reference, hence 1 instead of 0 + GD_MONO_SCOPE_THREAD_ATTACH; - // The reference count was increased after the managed side was the only one referencing our owner. - // This means the owner is being referenced again by the unmanaged side, - // so the owner must hold the managed side alive again to avoid it from being GCed. + // The reference count was increased after the managed side was the only one referencing our owner. + // This means the owner is being referenced again by the unmanaged side, + // so the owner must hold the managed side alive again to avoid it from being GCed. + + MonoObject *target = gchandle.get_target(); + if (!target) { + return false; // Called after the managed side was collected, so nothing to do here + } - MonoObject *target = gchandle.get_target(); - if (!target) { - return; // Called after the managed side was collected, so nothing to do here + // Release the current weak handle and replace it with a strong handle. + MonoGCHandleData strong_gchandle = MonoGCHandleData::new_strong_handle(target); + gchandle.release(); + gchandle = strong_gchandle; } - // Release the current weak handle and replace it with a strong handle. - MonoGCHandleData strong_gchandle = MonoGCHandleData::new_strong_handle(target); - gchandle.release(); - gchandle = strong_gchandle; - } -#endif -} + return false; + } else { + // Refcount decremented + if (refcount == 1 && !gchandle.is_released() && !gchandle.is_weak()) { // The managed side also holds a reference, hence 1 instead of 0 + GD_MONO_SCOPE_THREAD_ATTACH; -bool CSharpLanguage::refcount_decremented_instance_binding(Object *p_object) { -#if 0 - RefCounted *rc_owner = Object::cast_to<RefCounted>(p_object); + // If owner owner is no longer referenced by the unmanaged side, + // the managed instance takes responsibility of deleting the owner when GCed. -#ifdef DEBUG_ENABLED - CRASH_COND(!rc_owner); - CRASH_COND(!p_object->has_script_instance_binding(get_language_index())); -#endif + MonoObject *target = gchandle.get_target(); + if (!target) { + return refcount == 0; // Called after the managed side was collected, so nothing to do here + } - void *data = p_object->get_script_instance_binding(get_language_index()); - CRASH_COND(!data); + // Release the current strong handle and replace it with a weak handle. + MonoGCHandleData weak_gchandle = MonoGCHandleData::new_weak_handle(target); + gchandle.release(); + gchandle = weak_gchandle; - CSharpScriptBinding &script_binding = ((Map<Object *, CSharpScriptBinding>::Element *)data)->get(); - MonoGCHandleData &gchandle = script_binding.gchandle; - - int refcount = rc_owner->reference_get_count(); + return false; + } - if (!script_binding.inited) { return refcount == 0; } +} - if (refcount == 1 && !gchandle.is_released() && !gchandle.is_weak()) { // The managed side also holds a reference, hence 1 instead of 0 - GD_MONO_SCOPE_THREAD_ATTACH; +void *CSharpLanguage::get_instance_binding(Object *p_object) { + void *binding = p_object->get_instance_binding(get_singleton(), &_instance_binding_callbacks); - // If owner owner is no longer referenced by the unmanaged side, - // the managed instance takes responsibility of deleting the owner when GCed. + // Initially this was in `_instance_binding_create_callback`. However, after the new instance + // binding re-write it was resulting in a deadlock in `_instance_binding_reference`, as + // `setup_csharp_script_binding` may call `reference()`. It was moved here outside to fix that. - MonoObject *target = gchandle.get_target(); - if (!target) { - return refcount == 0; // Called after the managed side was collected, so nothing to do here - } + if (binding) { + CSharpScriptBinding &script_binding = ((Map<Object *, CSharpScriptBinding>::Element *)binding)->value(); - // Release the current strong handle and replace it with a weak handle. - MonoGCHandleData weak_gchandle = MonoGCHandleData::new_weak_handle(target); - gchandle.release(); - gchandle = weak_gchandle; + if (!script_binding.inited) { + MutexLock lock(CSharpLanguage::get_singleton()->get_language_bind_mutex()); - return false; + if (!script_binding.inited) { // Another thread may have set it up + CSharpLanguage::get_singleton()->setup_csharp_script_binding(script_binding, p_object); + } + } } - return refcount == 0; + return binding; +} + +void *CSharpLanguage::get_existing_instance_binding(Object *p_object) { +#ifdef DEBUG_ENABLED + CRASH_COND(p_object->has_instance_binding(p_object)); #endif - return false; + return p_object->get_instance_binding(get_singleton(), &_instance_binding_callbacks); +} + +void CSharpLanguage::set_instance_binding(Object *p_object, void *p_binding) { + p_object->set_instance_binding(get_singleton(), p_binding, &_instance_binding_callbacks); +} + +bool CSharpLanguage::has_instance_binding(Object *p_object) { + return p_object->has_instance_binding(get_singleton()); } CSharpInstance *CSharpInstance::create_for_managed_type(Object *p_owner, CSharpScript *p_script, const MonoGCHandleData &p_gchandle) { @@ -2260,29 +2284,16 @@ CSharpInstance::~CSharpInstance() { // Otherwise, the unsafe reference debug checks will incorrectly detect a bug. bool die = _unreference_owner_unsafe(); CRASH_COND(die); // `owner_keep_alive` holds a reference, so it can't die -#if 0 - void *data = owner->get_script_instance_binding(CSharpLanguage::get_singleton()->get_language_index()); - + void *data = CSharpLanguage::get_instance_binding(owner); CRASH_COND(data == nullptr); - CSharpScriptBinding &script_binding = ((Map<Object *, CSharpScriptBinding>::Element *)data)->get(); - - if (!script_binding.inited) { - MutexLock 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, owner); - CRASH_COND(!script_binding.inited); - } - } + CRASH_COND(!script_binding.inited); #ifdef DEBUG_ENABLED // The "instance binding" holds a reference so the refcount should be at least 2 before `scope_keep_owner_alive` goes out of scope CRASH_COND(rc_owner->reference_get_count() <= 1); #endif -#endif } if (script.is_valid() && owner) { @@ -3100,10 +3111,10 @@ CSharpInstance *CSharpScript::_create_instance(const Variant **p_args, int p_arg // Hold it alive. Important if we have to dispose a script instance binding before creating the CSharpInstance. ref = Ref<RefCounted>(static_cast<RefCounted *>(p_owner)); } -#if 0 + // If the object had a script instance binding, dispose it before adding the CSharpInstance - if (p_owner->has_script_instance_binding(CSharpLanguage::get_singleton()->get_language_index())) { - void *data = p_owner->get_script_instance_binding(CSharpLanguage::get_singleton()->get_language_index()); + if (CSharpLanguage::has_instance_binding(p_owner)) { + void *data = CSharpLanguage::get_existing_instance_binding(p_owner); CRASH_COND(data == nullptr); CSharpScriptBinding &script_binding = ((Map<Object *, CSharpScriptBinding>::Element *)data)->get(); @@ -3122,7 +3133,7 @@ CSharpInstance *CSharpScript::_create_instance(const Variant **p_args, int p_arg script_binding.inited = false; } } -#endif + CSharpInstance *instance = memnew(CSharpInstance(Ref<CSharpScript>(this))); instance->base_ref_counted = p_is_ref_counted; instance->owner = p_owner; @@ -3192,7 +3203,7 @@ Variant CSharpScript::_new(const Variant **p_args, int p_argcount, Callable::Cal CSharpInstance *instance = _create_instance(p_args, p_argcount, owner, r != nullptr, r_error); if (!instance) { if (ref.is_null()) { - memdelete(owner); //no owner, sorry + memdelete(owner); // no owner, sorry } return Variant(); } diff --git a/modules/mono/csharp_script.h b/modules/mono/csharp_script.h index da6b60aee2..4552f376d0 100644 --- a/modules/mono/csharp_script.h +++ b/modules/mono/csharp_script.h @@ -401,7 +401,18 @@ class CSharpLanguage : public ScriptLanguage { static void _editor_init_callback(); #endif + static void *_instance_binding_create_callback(void *p_token, void *p_instance); + static void _instance_binding_free_callback(void *p_token, void *p_instance, void *p_binding); + static GDNativeBool _instance_binding_reference_callback(void *p_token, void *p_binding, GDNativeBool p_reference); + + static GDNativeInstanceBindingCallbacks _instance_binding_callbacks; + public: + static void *get_instance_binding(Object *p_object); + static void *get_existing_instance_binding(Object *p_object); + static void set_instance_binding(Object *p_object, void *p_binding); + static bool has_instance_binding(Object *p_object); + StringNameCache string_names; const Mutex &get_language_bind_mutex() { return language_bind_mutex; } @@ -507,12 +518,6 @@ public: void thread_enter() override; void thread_exit() override; - // Don't use these. I'm watching you - void *alloc_instance_binding_data(Object *p_object) override; - void free_instance_binding_data(void *p_data) override; - void refcount_incremented_instance_binding(Object *p_object) override; - bool refcount_decremented_instance_binding(Object *p_object) override; - Map<Object *, CSharpScriptBinding>::Element *insert_script_binding(Object *p_object, const CSharpScriptBinding &p_script_binding); bool setup_csharp_script_binding(CSharpScriptBinding &r_script_binding, Object *p_object); diff --git a/modules/mono/glue/base_object_glue.cpp b/modules/mono/glue/base_object_glue.cpp index a99dff8432..6c5503a3bd 100644 --- a/modules/mono/glue/base_object_glue.cpp +++ b/modules/mono/glue/base_object_glue.cpp @@ -64,8 +64,8 @@ void godot_icall_Object_Disposed(MonoObject *p_obj, Object *p_ptr) { return; } } -#if 0 - void *data = p_ptr->get_script_instance_binding(CSharpLanguage::get_singleton()->get_language_index()); + + void *data = CSharpLanguage::get_existing_instance_binding(p_ptr); if (data) { CSharpScriptBinding &script_binding = ((Map<Object *, CSharpScriptBinding>::Element *)data)->get(); @@ -76,7 +76,6 @@ void godot_icall_Object_Disposed(MonoObject *p_obj, Object *p_ptr) { } } } -#endif } void godot_icall_RefCounted_Disposed(MonoObject *p_obj, Object *p_ptr, MonoBoolean p_is_finalizer) { @@ -85,7 +84,7 @@ void godot_icall_RefCounted_Disposed(MonoObject *p_obj, Object *p_ptr, MonoBoole // This is only called with RefCounted derived classes CRASH_COND(!Object::cast_to<RefCounted>(p_ptr)); #endif -#if 0 + RefCounted *rc = static_cast<RefCounted *>(p_ptr); if (rc->get_script_instance()) { @@ -113,7 +112,7 @@ void godot_icall_RefCounted_Disposed(MonoObject *p_obj, Object *p_ptr, MonoBoole if (rc->unreference()) { memdelete(rc); } else { - void *data = rc->get_script_instance_binding(CSharpLanguage::get_singleton()->get_language_index()); + void *data = CSharpLanguage::get_existing_instance_binding(rc); if (data) { CSharpScriptBinding &script_binding = ((Map<Object *, CSharpScriptBinding>::Element *)data)->get(); @@ -125,7 +124,6 @@ void godot_icall_RefCounted_Disposed(MonoObject *p_obj, Object *p_ptr, MonoBoole } } } -#endif } void godot_icall_Object_ConnectEventSignals(Object *p_ptr) { diff --git a/modules/mono/mono_gd/gd_mono_internals.cpp b/modules/mono/mono_gd/gd_mono_internals.cpp index d6545d50ec..60047545c4 100644 --- a/modules/mono/mono_gd/gd_mono_internals.cpp +++ b/modules/mono/mono_gd/gd_mono_internals.cpp @@ -45,7 +45,7 @@ namespace GDMonoInternals { void tie_managed_to_unmanaged(MonoObject *managed, Object *unmanaged) { // This method should not fail -#if 0 + CRASH_COND(!unmanaged); // All mono objects created from the managed world (e.g.: 'new Player()') @@ -89,12 +89,12 @@ void tie_managed_to_unmanaged(MonoObject *managed, Object *unmanaged) { } // 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())); + CRASH_COND(CSharpLanguage::has_instance_binding(unmanaged)); 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); + CSharpLanguage::set_instance_binding(unmanaged, data); return; } @@ -108,7 +108,6 @@ void tie_managed_to_unmanaged(MonoObject *managed, Object *unmanaged) { CSharpInstance *csharp_instance = CSharpInstance::create_for_managed_type(unmanaged, script.ptr(), gchandle); unmanaged->set_script_and_instance(script, csharp_instance); -#endif } void unhandled_exception(MonoException *p_exc) { diff --git a/modules/mono/mono_gd/gd_mono_utils.cpp b/modules/mono/mono_gd/gd_mono_utils.cpp index 080398c997..13939bd014 100644 --- a/modules/mono/mono_gd/gd_mono_utils.cpp +++ b/modules/mono/mono_gd/gd_mono_utils.cpp @@ -54,7 +54,6 @@ namespace GDMonoUtils { MonoObject *unmanaged_get_managed(Object *unmanaged) { -#if 0 if (!unmanaged) { return nullptr; } @@ -69,22 +68,10 @@ MonoObject *unmanaged_get_managed(Object *unmanaged) { // If the owner does not have a CSharpInstance... - void *data = unmanaged->get_script_instance_binding(CSharpLanguage::get_singleton()->get_language_index()); - + void *data = CSharpLanguage::get_instance_binding(unmanaged); ERR_FAIL_NULL_V(data, nullptr); - CSharpScriptBinding &script_binding = ((Map<Object *, CSharpScriptBinding>::Element *)data)->value(); - - if (!script_binding.inited) { - MutexLock 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); - - ERR_FAIL_COND_V(!script_binding.inited, nullptr); - } - } + ERR_FAIL_COND_V(!script_binding.inited, nullptr); MonoGCHandleData &gchandle = script_binding.gchandle; @@ -121,8 +108,6 @@ MonoObject *unmanaged_get_managed(Object *unmanaged) { } return mono_object; -#endif - return nullptr; } void set_main_thread(MonoThread *p_thread) { |