From 44691448911f1d29d4d79dbdd5553734761e57c4 Mon Sep 17 00:00:00 2001 From: reduz Date: Thu, 8 Jul 2021 16:16:02 -0300 Subject: [PATCH] Redo how instance bindings work * The harcoded 8 slots are no more and impose limits in the new extension system. * New system is limitless, although it will impose small performance hit with a mutex. * Use a token to request the instance binding. **Warning**: Mono will most likely break as a result of this, will need to be modified to use the new system. --- core/extension/gdnative_interface.cpp | 6 ++ core/extension/gdnative_interface.h | 11 ++++ core/object/object.cpp | 61 +++++++++---------- core/object/object.h | 35 ++++++++--- core/object/ref_counted.cpp | 19 ++---- .../nativescript/godot_nativescript.cpp | 2 +- .../gdnative/nativescript/nativescript.cpp | 13 ++++ modules/mono/csharp_script.cpp | 14 ++++- modules/mono/glue/base_object_glue.cpp | 6 +- modules/mono/mono_gd/gd_mono_internals.cpp | 3 +- modules/mono/mono_gd/gd_mono_utils.cpp | 3 + 11 files changed, 111 insertions(+), 62 deletions(-) diff --git a/core/extension/gdnative_interface.cpp b/core/extension/gdnative_interface.cpp index 324933d7b74..8f68b8d848f 100644 --- a/core/extension/gdnative_interface.cpp +++ b/core/extension/gdnative_interface.cpp @@ -520,6 +520,11 @@ static GDNativeObjectPtr gdnative_global_get_singleton(const char *p_name) { return (GDNativeObjectPtr)Engine::get_singleton()->get_singleton_object(String(p_name)); } +static void *gdnative_object_get_instance_binding(GDNativeObjectPtr p_instance, void *p_token, GDNativeInstanceBindingCallbacks *p_callbacks) { + Object *o = (Object *)p_instance; + return o->get_instance_binding(p_token, p_callbacks); +} + static GDNativeObjectPtr gdnative_object_get_instance_from_id(GDObjectInstanceID p_instance_id) { return (GDNativeObjectPtr)ObjectDB::get_instance(ObjectID(p_instance_id)); } @@ -665,6 +670,7 @@ void gdnative_setup_interface(GDNativeInterface *p_interface) { gdni.object_method_bind_ptrcall = gdnative_object_method_bind_ptrcall; gdni.object_destroy = gdnative_object_destroy; gdni.global_get_singleton = gdnative_global_get_singleton; + gdni.object_get_instance_binding = gdnative_object_get_instance_binding; gdni.object_cast_to = gdnative_object_cast_to; gdni.object_get_instance_from_id = gdnative_object_get_instance_from_id; diff --git a/core/extension/gdnative_interface.h b/core/extension/gdnative_interface.h index 318912e8890..c1ebb3e76af 100644 --- a/core/extension/gdnative_interface.h +++ b/core/extension/gdnative_interface.h @@ -171,6 +171,16 @@ typedef void (*GDNativePtrUtilityFunction)(GDNativeTypePtr r_return, const GDNat typedef GDNativeObjectPtr (*GDNativeClassConstructor)(); +typedef void *(*GDNativeInstanceBindingCreateCallback)(void *p_token, void *p_instance); +typedef void (*GDNativeInstanceBindingFreeCallback)(void *p_token, void *p_instance, void *p_binding); +typedef GDNativeBool (*GDNativeInstanceBindingReferenceCallback)(void *p_token, void *p_instance, GDNativeBool p_reference); + +struct GDNativeInstanceBindingCallbacks { + GDNativeInstanceBindingCreateCallback create_callback; + GDNativeInstanceBindingFreeCallback free_callback; + GDNativeInstanceBindingReferenceCallback reference_callback; +}; + /* EXTENSION CLASSES */ typedef void *GDExtensionClassInstancePtr; @@ -373,6 +383,7 @@ typedef struct { void (*object_method_bind_ptrcall)(GDNativeMethodBindPtr p_method_bind, GDNativeObjectPtr p_instance, const GDNativeTypePtr *p_args, GDNativeTypePtr r_ret); void (*object_destroy)(GDNativeObjectPtr p_o); GDNativeObjectPtr (*global_get_singleton)(const char *p_name); + void *(*object_get_instance_binding)(GDNativeObjectPtr p_o, void *p_token, GDNativeInstanceBindingCallbacks *p_callbacks); GDNativeObjectPtr (*object_cast_to)(const GDNativeObjectPtr p_object, void *p_class_tag); GDNativeObjectPtr (*object_get_instance_from_id)(GDObjectInstanceID p_instance_id); diff --git a/core/object/object.cpp b/core/object/object.cpp index 1c8db89e5e2..296d8767014 100644 --- a/core/object/object.cpp +++ b/core/object/object.cpp @@ -1769,42 +1769,41 @@ uint32_t Object::get_edited_version() const { } #endif -void *Object::get_script_instance_binding(int p_script_language_index) { -#ifdef DEBUG_ENABLED - ERR_FAIL_INDEX_V(p_script_language_index, MAX_SCRIPT_INSTANCE_BINDINGS, nullptr); -#endif - - //it's up to the script language to make this thread safe, if the function is called twice due to threads being out of sync - //just return the same pointer. - //if you want to put a big lock in the entire function and keep allocated pointers in a map or something, feel free to do it - //as it should not really affect performance much (won't be called too often), as in far most cases the condition below will be false afterwards - - if (!_script_instance_bindings[p_script_language_index]) { - void *script_data = ScriptServer::get_language(p_script_language_index)->alloc_instance_binding_data(this); - if (script_data) { - instance_binding_count.increment(); - _script_instance_bindings[p_script_language_index] = script_data; +void *Object::get_instance_binding(void *p_token, const GDNativeInstanceBindingCallbacks *p_callbacks) { + void *binding = nullptr; + _instance_binding_mutex.lock(); + for (uint32_t i = 0; i < _instance_binding_count; i++) { + if (_instance_bindings[i].token == p_token) { + binding = _instance_bindings[i].binding; + break; } } + if (unlikely(!binding)) { + uint32_t current_size = next_power_of_2(_instance_binding_count); + uint32_t new_size = next_power_of_2(_instance_binding_count + 1); - return _script_instance_bindings[p_script_language_index]; -} + if (current_size == 0 || new_size > current_size) { + _instance_bindings = (InstanceBinding *)memrealloc(_instance_bindings, new_size * sizeof(InstanceBinding)); + } -bool Object::has_script_instance_binding(int p_script_language_index) { - return _script_instance_bindings[p_script_language_index] != nullptr; -} + _instance_bindings[_instance_binding_count].free_callback = p_callbacks->free_callback; + _instance_bindings[_instance_binding_count].reference_callback = p_callbacks->reference_callback; + _instance_bindings[_instance_binding_count].token = p_token; -void Object::set_script_instance_binding(int p_script_language_index, void *p_data) { -#ifdef DEBUG_ENABLED - CRASH_COND(_script_instance_bindings[p_script_language_index] != nullptr); -#endif - _script_instance_bindings[p_script_language_index] = p_data; + binding = p_callbacks->create_callback(p_token, this); + _instance_bindings[_instance_binding_count].binding = binding; + + _instance_binding_count++; + } + + _instance_binding_mutex.unlock(); + + return binding; } void Object::_construct_object(bool p_reference) { type_is_reference = p_reference; _instance_id = ObjectDB::add_instance(this); - memset(_script_instance_bindings, 0, sizeof(void *) * MAX_SCRIPT_INSTANCE_BINDINGS); ClassDB::instance_get_native_extension_data(&_extension, &_extension_instance); @@ -1864,12 +1863,13 @@ Object::~Object() { _instance_id = ObjectID(); _predelete_ok = 2; - if (!ScriptServer::are_languages_finished()) { - for (int i = 0; i < MAX_SCRIPT_INSTANCE_BINDINGS; i++) { - if (_script_instance_bindings[i]) { - ScriptServer::get_language(i)->free_instance_binding_data(_script_instance_bindings[i]); + if (_instance_bindings != nullptr) { + for (uint32_t i = 0; i < _instance_binding_count; i++) { + if (_instance_bindings[i].free_callback) { + _instance_bindings[i].free_callback(_instance_bindings[i].token, _instance_bindings[i].binding, this); } } + memfree(_instance_bindings); } } @@ -1887,7 +1887,6 @@ void ObjectDB::debug_objects(DebugFunc p_func) { for (uint32_t i = 0, count = slot_count; i < slot_max && count != 0; i++) { if (object_slots[i].validator) { p_func(object_slots[i].object); - count--; } } diff --git a/core/object/object.h b/core/object/object.h index e6eb6d1aaff..8389d80afca 100644 --- a/core/object/object.h +++ b/core/object/object.h @@ -480,10 +480,6 @@ public: }; private: - enum { - MAX_SCRIPT_INSTANCE_BINDINGS = 8 - }; - #ifdef DEBUG_ENABLED friend struct _ObjectDebugLock; #endif @@ -542,12 +538,35 @@ private: friend class RefCounted; bool type_is_reference = false; - SafeNumeric instance_binding_count; - void *_script_instance_bindings[MAX_SCRIPT_INSTANCE_BINDINGS]; + + std::mutex _instance_binding_mutex; + struct InstanceBinding { + void *binding; + void *token; + GDNativeInstanceBindingFreeCallback free_callback = nullptr; + GDNativeInstanceBindingReferenceCallback reference_callback = nullptr; + }; + InstanceBinding *_instance_bindings = nullptr; + uint32_t _instance_binding_count = 0; Object(bool p_reference); protected: + _FORCE_INLINE_ bool _instance_binding_reference(bool p_reference) { + bool can_die = true; + if (_instance_bindings) { + _instance_binding_mutex.lock(); + for (uint32_t i = 0; i < _instance_binding_count; i++) { + if (_instance_bindings[i].reference_callback) { + if (!_instance_bindings[i].reference_callback(_instance_bindings[i].token, _instance_bindings[i].binding, p_reference)) { + can_die = false; + } + } + } + _instance_binding_mutex.unlock(); + } + return can_die; + } friend class NativeExtensionMethodBind; _ALWAYS_INLINE_ const ObjectNativeExtension *_get_extension() const { return _extension; } _ALWAYS_INLINE_ GDExtensionClassInstancePtr _get_extension_instance() const { return _extension_instance; } @@ -785,9 +804,7 @@ public: #endif //used by script languages to store binding data - void *get_script_instance_binding(int p_script_language_index); - bool has_script_instance_binding(int p_script_language_index); - void set_script_instance_binding(int p_script_language_index, void *p_data); + void *get_instance_binding(void *p_token, const GDNativeInstanceBindingCallbacks *p_callbacks); void clear_internal_resource_paths(); diff --git a/core/object/ref_counted.cpp b/core/object/ref_counted.cpp index 9862624972f..2833f774dc5 100644 --- a/core/object/ref_counted.cpp +++ b/core/object/ref_counted.cpp @@ -65,13 +65,8 @@ bool RefCounted::reference() { if (_get_extension() && _get_extension()->reference) { _get_extension()->reference(_get_extension_instance()); } - if (instance_binding_count.get() > 0 && !ScriptServer::are_languages_finished()) { - for (int i = 0; i < MAX_SCRIPT_INSTANCE_BINDINGS; i++) { - if (_script_instance_bindings[i]) { - ScriptServer::get_language(i)->refcount_incremented_instance_binding(this); - } - } - } + + _instance_binding_reference(true); } return success; @@ -89,14 +84,8 @@ bool RefCounted::unreference() { if (_get_extension() && _get_extension()->unreference) { _get_extension()->unreference(_get_extension_instance()); } - if (instance_binding_count.get() > 0 && !ScriptServer::are_languages_finished()) { - for (int i = 0; i < MAX_SCRIPT_INSTANCE_BINDINGS; i++) { - if (_script_instance_bindings[i]) { - bool script_ret = ScriptServer::get_language(i)->refcount_decremented_instance_binding(this); - die = die && script_ret; - } - } - } + + die = die && _instance_binding_reference(false); } return die; diff --git a/modules/gdnative/nativescript/godot_nativescript.cpp b/modules/gdnative/nativescript/godot_nativescript.cpp index b10a7475687..70b14836bf6 100644 --- a/modules/gdnative/nativescript/godot_nativescript.cpp +++ b/modules/gdnative/nativescript/godot_nativescript.cpp @@ -332,7 +332,7 @@ void GDAPI godot_nativescript_unregister_instance_binding_data_functions(int p_i } void GDAPI *godot_nativescript_get_instance_binding_data(int p_idx, godot_object *p_object) { - return NativeScriptLanguage::get_singleton()->get_instance_binding_data(p_idx, (Object *)p_object); + return nullptr; } void GDAPI godot_nativescript_profiling_add_data(const char *p_signature, uint64_t p_time) { diff --git a/modules/gdnative/nativescript/nativescript.cpp b/modules/gdnative/nativescript/nativescript.cpp index 3695f6b9a31..d7943827c22 100644 --- a/modules/gdnative/nativescript/nativescript.cpp +++ b/modules/gdnative/nativescript/nativescript.cpp @@ -1258,6 +1258,8 @@ void NativeScriptLanguage::unregister_binding_functions(int p_idx) { } void *NativeScriptLanguage::get_instance_binding_data(int p_idx, Object *p_object) { + return nullptr; +#if 0 ERR_FAIL_INDEX_V(p_idx, binding_functions.size(), nullptr); ERR_FAIL_COND_V_MSG(!binding_functions[p_idx].first, nullptr, "Tried to get binding data for a nativescript binding that does not exist."); @@ -1287,9 +1289,12 @@ void *NativeScriptLanguage::get_instance_binding_data(int p_idx, Object *p_objec } return (*binding_data)[p_idx]; +#endif } void *NativeScriptLanguage::alloc_instance_binding_data(Object *p_object) { + return nullptr; +#if 0 Vector *binding_data = new Vector; binding_data->resize(binding_functions.size()); @@ -1301,9 +1306,11 @@ void *NativeScriptLanguage::alloc_instance_binding_data(Object *p_object) { binding_instances.insert(binding_data); return (void *)binding_data; +#endif } void NativeScriptLanguage::free_instance_binding_data(void *p_data) { +#if 0 if (!p_data) { return; } @@ -1323,9 +1330,11 @@ void NativeScriptLanguage::free_instance_binding_data(void *p_data) { binding_instances.erase(&binding_data); delete &binding_data; +#endif } void NativeScriptLanguage::refcount_incremented_instance_binding(Object *p_object) { +#if 0 void *data = p_object->get_script_instance_binding(lang_idx); if (!data) { @@ -1347,9 +1356,11 @@ void NativeScriptLanguage::refcount_incremented_instance_binding(Object *p_objec binding_functions[i].second.refcount_incremented_instance_binding(binding_data[i], p_object); } } +#endif } bool NativeScriptLanguage::refcount_decremented_instance_binding(Object *p_object) { +#if 0 void *data = p_object->get_script_instance_binding(lang_idx); if (!data) { @@ -1375,6 +1386,8 @@ bool NativeScriptLanguage::refcount_decremented_instance_binding(Object *p_objec } return can_die; +#endif + return false; } void NativeScriptLanguage::set_global_type_tag(int p_idx, StringName p_class_name, const void *p_type_tag) { diff --git a/modules/mono/csharp_script.cpp b/modules/mono/csharp_script.cpp index c48230f5243..be46e76d9ce 100644 --- a/modules/mono/csharp_script.cpp +++ b/modules/mono/csharp_script.cpp @@ -1510,6 +1510,7 @@ void CSharpLanguage::free_instance_binding_data(void *p_data) { } void CSharpLanguage::refcount_incremented_instance_binding(Object *p_object) { +#if 0 RefCounted *rc_owner = Object::cast_to(p_object); #ifdef DEBUG_ENABLED @@ -1544,9 +1545,11 @@ void CSharpLanguage::refcount_incremented_instance_binding(Object *p_object) { gchandle.release(); gchandle = strong_gchandle; } +#endif } bool CSharpLanguage::refcount_decremented_instance_binding(Object *p_object) { +#if 0 RefCounted *rc_owner = Object::cast_to(p_object); #ifdef DEBUG_ENABLED @@ -1586,6 +1589,8 @@ bool CSharpLanguage::refcount_decremented_instance_binding(Object *p_object) { } return refcount == 0; +#endif + return false; } CSharpInstance *CSharpInstance::create_for_managed_type(Object *p_owner, CSharpScript *p_script, const MonoGCHandleData &p_gchandle) { @@ -2264,8 +2269,10 @@ 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()); + + CRASH_COND(data == nullptr); CSharpScriptBinding &script_binding = ((Map::Element *)data)->get(); @@ -2283,6 +2290,7 @@ CSharpInstance::~CSharpInstance() { #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 } @@ -3101,7 +3109,7 @@ 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(static_cast(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()); @@ -3123,7 +3131,7 @@ CSharpInstance *CSharpScript::_create_instance(const Variant **p_args, int p_arg script_binding.inited = false; } } - +#endif CSharpInstance *instance = memnew(CSharpInstance(Ref(this))); instance->base_ref_counted = p_is_ref_counted; instance->owner = p_owner; diff --git a/modules/mono/glue/base_object_glue.cpp b/modules/mono/glue/base_object_glue.cpp index 2b87c2d9a4a..2d04cedb9b8 100644 --- a/modules/mono/glue/base_object_glue.cpp +++ b/modules/mono/glue/base_object_glue.cpp @@ -64,7 +64,7 @@ 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()); if (data) { @@ -76,6 +76,7 @@ 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) { @@ -84,7 +85,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(p_ptr)); #endif - +#if 0 RefCounted *rc = static_cast(p_ptr); if (rc->get_script_instance()) { @@ -124,6 +125,7 @@ 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 d7df18d5dab..d6545d50ece 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()') @@ -108,6 +108,7 @@ 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 0b9a577e012..080398c9976 100644 --- a/modules/mono/mono_gd/gd_mono_utils.cpp +++ b/modules/mono/mono_gd/gd_mono_utils.cpp @@ -54,6 +54,7 @@ namespace GDMonoUtils { MonoObject *unmanaged_get_managed(Object *unmanaged) { +#if 0 if (!unmanaged) { return nullptr; } @@ -120,6 +121,8 @@ MonoObject *unmanaged_get_managed(Object *unmanaged) { } return mono_object; +#endif + return nullptr; } void set_main_thread(MonoThread *p_thread) {