Merge pull request #25574 from neikeq/ss
Mono: Lifetime fixes for CSharpInstance and instance binding data
This commit is contained in:
commit
919fa75803
7 changed files with 257 additions and 128 deletions
|
@ -1924,6 +1924,11 @@ void *Object::get_script_instance_binding(int p_script_language_index) {
|
|||
return _script_instance_bindings[p_script_language_index];
|
||||
}
|
||||
|
||||
bool Object::has_script_instance_binding(int p_script_language_index) {
|
||||
|
||||
return _script_instance_bindings[p_script_language_index] != NULL;
|
||||
}
|
||||
|
||||
Object::Object() {
|
||||
|
||||
_class_ptr = NULL;
|
||||
|
|
|
@ -729,6 +729,7 @@ public:
|
|||
|
||||
//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 clear_internal_resource_paths();
|
||||
|
||||
|
|
|
@ -139,14 +139,24 @@ void CSharpLanguage::finish() {
|
|||
}
|
||||
#endif
|
||||
|
||||
// Release gchandle bindings before finalizing mono runtime
|
||||
script_bindings.clear();
|
||||
// Make sure all script binding gchandles are released before finalizing GDMono
|
||||
for (Map<Object *, CSharpScriptBinding>::Element *E = script_bindings.front(); E; E = E->next()) {
|
||||
CSharpScriptBinding &script_binding = E->value();
|
||||
|
||||
if (script_binding.gchandle.is_valid()) {
|
||||
script_binding.gchandle->release();
|
||||
script_binding.inited = false;
|
||||
}
|
||||
}
|
||||
|
||||
if (gdmono) {
|
||||
memdelete(gdmono);
|
||||
gdmono = NULL;
|
||||
}
|
||||
|
||||
// Clear here, after finalizing all domains to make sure there is nothing else referencing the elements.
|
||||
script_bindings.clear();
|
||||
|
||||
finalizing = false;
|
||||
}
|
||||
|
||||
|
@ -1054,12 +1064,14 @@ CSharpLanguage::~CSharpLanguage() {
|
|||
singleton = NULL;
|
||||
}
|
||||
|
||||
void *CSharpLanguage::alloc_instance_binding_data(Object *p_object) {
|
||||
bool CSharpLanguage::setup_csharp_script_binding(CSharpScriptBinding &r_script_binding, Object *p_object) {
|
||||
|
||||
#ifdef DEBUG_ENABLED
|
||||
// I don't trust you
|
||||
if (p_object->get_script_instance())
|
||||
CRASH_COND(NULL != CAST_CSHARP_INSTANCE(p_object->get_script_instance()));
|
||||
if (p_object->get_script_instance()) {
|
||||
CSharpInstance *csharp_instance = CAST_CSHARP_INSTANCE(p_object->get_script_instance());
|
||||
CRASH_COND(csharp_instance != NULL && !csharp_instance->is_destructing_script_instance());
|
||||
}
|
||||
#endif
|
||||
|
||||
StringName type_name = p_object->get_class_name();
|
||||
|
@ -1068,29 +1080,21 @@ void *CSharpLanguage::alloc_instance_binding_data(Object *p_object) {
|
|||
const ClassDB::ClassInfo *classinfo = ClassDB::classes.getptr(type_name);
|
||||
while (classinfo && !classinfo->exposed)
|
||||
classinfo = classinfo->inherits_ptr;
|
||||
ERR_FAIL_NULL_V(classinfo, NULL);
|
||||
ERR_FAIL_NULL_V(classinfo, false);
|
||||
type_name = classinfo->name;
|
||||
|
||||
GDMonoClass *type_class = GDMonoUtils::type_get_proxy_class(type_name);
|
||||
|
||||
ERR_FAIL_NULL_V(type_class, NULL);
|
||||
ERR_FAIL_NULL_V(type_class, false);
|
||||
|
||||
MonoObject *mono_object = GDMonoUtils::create_managed_for_godot_object(type_class, type_name, p_object);
|
||||
|
||||
ERR_FAIL_NULL_V(mono_object, NULL);
|
||||
ERR_FAIL_NULL_V(mono_object, false);
|
||||
|
||||
CSharpScriptBinding script_binding;
|
||||
|
||||
script_binding.type_name = type_name;
|
||||
script_binding.wrapper_class = type_class; // cache
|
||||
script_binding.gchandle = MonoGCHandle::create_strong(mono_object);
|
||||
|
||||
void *data;
|
||||
|
||||
{
|
||||
SCOPED_MUTEX_LOCK(language_bind_mutex);
|
||||
data = (void *)script_bindings.insert(p_object, script_binding);
|
||||
}
|
||||
r_script_binding.inited = true;
|
||||
r_script_binding.type_name = type_name;
|
||||
r_script_binding.wrapper_class = type_class; // cache
|
||||
r_script_binding.gchandle = MonoGCHandle::create_strong(mono_object);
|
||||
|
||||
// Tie managed to unmanaged
|
||||
Reference *ref = Object::cast_to<Reference>(p_object);
|
||||
|
@ -1104,6 +1108,23 @@ void *CSharpLanguage::alloc_instance_binding_data(Object *p_object) {
|
|||
ref->reference();
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void *CSharpLanguage::alloc_instance_binding_data(Object *p_object) {
|
||||
|
||||
CSharpScriptBinding script_binding;
|
||||
|
||||
if (!setup_csharp_script_binding(script_binding, p_object))
|
||||
return NULL;
|
||||
|
||||
void *data;
|
||||
|
||||
{
|
||||
SCOPED_MUTEX_LOCK(language_bind_mutex);
|
||||
data = (void *)script_bindings.insert(p_object, script_binding);
|
||||
}
|
||||
|
||||
return data;
|
||||
}
|
||||
|
||||
|
@ -1125,10 +1146,15 @@ void CSharpLanguage::free_instance_binding_data(void *p_data) {
|
|||
|
||||
Map<Object *, CSharpScriptBinding>::Element *data = (Map<Object *, CSharpScriptBinding>::Element *)p_data;
|
||||
|
||||
// Set the native instance field to IntPtr.Zero, if not yet garbage collected
|
||||
MonoObject *mono_object = data->value().gchandle->get_target();
|
||||
if (mono_object) {
|
||||
CACHED_FIELD(GodotObject, ptr)->set_value_raw(mono_object, NULL);
|
||||
CSharpScriptBinding &script_binding = data->value();
|
||||
|
||||
if (script_binding.inited) {
|
||||
// Set the native instance field to IntPtr.Zero, if not yet garbage collected.
|
||||
// This is done to avoid trying to dispose the native instance from Dispose(bool).
|
||||
MonoObject *mono_object = script_binding.gchandle->get_target();
|
||||
if (mono_object) {
|
||||
CACHED_FIELD(GodotObject, ptr)->set_value_raw(mono_object, NULL);
|
||||
}
|
||||
}
|
||||
|
||||
script_bindings.erase(data);
|
||||
|
@ -1144,9 +1170,10 @@ void CSharpLanguage::refcount_incremented_instance_binding(Object *p_object) {
|
|||
#endif
|
||||
|
||||
void *data = p_object->get_script_instance_binding(get_language_index());
|
||||
if (!data)
|
||||
return;
|
||||
Ref<MonoGCHandle> &gchandle = ((Map<Object *, CSharpScriptBinding>::Element *)data)->get().gchandle;
|
||||
CRASH_COND(!data);
|
||||
|
||||
CSharpScriptBinding &script_binding = ((Map<Object *, CSharpScriptBinding>::Element *)data)->get();
|
||||
Ref<MonoGCHandle> &gchandle = script_binding.gchandle;
|
||||
|
||||
if (ref_owner->reference_get_count() > 1 && gchandle->is_weak()) { // The managed side also holds a reference, hence 1 instead of 0
|
||||
// The reference count was increased after the managed side was the only one referencing our owner.
|
||||
|
@ -1175,9 +1202,10 @@ bool CSharpLanguage::refcount_decremented_instance_binding(Object *p_object) {
|
|||
int refcount = ref_owner->reference_get_count();
|
||||
|
||||
void *data = p_object->get_script_instance_binding(get_language_index());
|
||||
if (!data)
|
||||
return refcount == 0;
|
||||
Ref<MonoGCHandle> &gchandle = ((Map<Object *, CSharpScriptBinding>::Element *)data)->get().gchandle;
|
||||
CRASH_COND(!data);
|
||||
|
||||
CSharpScriptBinding &script_binding = ((Map<Object *, CSharpScriptBinding>::Element *)data)->get();
|
||||
Ref<MonoGCHandle> &gchandle = script_binding.gchandle;
|
||||
|
||||
if (refcount == 1 && gchandle.is_valid() && !gchandle->is_weak()) { // The managed side also holds a reference, hence 1 instead of 0
|
||||
// If owner owner is no longer referenced by the unmanaged side,
|
||||
|
@ -1223,6 +1251,10 @@ MonoObject *CSharpInstance::get_mono_object() const {
|
|||
return gchandle->get_target();
|
||||
}
|
||||
|
||||
Object *CSharpInstance::get_owner() {
|
||||
return owner;
|
||||
}
|
||||
|
||||
bool CSharpInstance::set(const StringName &p_name, const Variant &p_value) {
|
||||
|
||||
ERR_FAIL_COND_V(!script.is_valid(), false);
|
||||
|
@ -1483,14 +1515,8 @@ bool CSharpInstance::_unreference_owner_unsafe() {
|
|||
// Unsafe refcount decrement. The managed instance also counts as a reference.
|
||||
// See: _reference_owner_unsafe()
|
||||
|
||||
bool die = static_cast<Reference *>(owner)->unreference();
|
||||
|
||||
if (die) {
|
||||
memdelete(owner);
|
||||
owner = NULL;
|
||||
}
|
||||
|
||||
return die;
|
||||
// Destroying the owner here means self destructing, so we defer the owner destruction to the caller.
|
||||
return static_cast<Reference *>(owner)->unreference();
|
||||
}
|
||||
|
||||
MonoObject *CSharpInstance::_internal_new_managed() {
|
||||
|
@ -1503,27 +1529,33 @@ MonoObject *CSharpInstance::_internal_new_managed() {
|
|||
ERR_FAIL_NULL_V(owner, NULL);
|
||||
ERR_FAIL_COND_V(script.is_null(), NULL);
|
||||
|
||||
if (base_ref)
|
||||
_reference_owner_unsafe();
|
||||
|
||||
MonoObject *mono_object = mono_object_new(SCRIPTS_DOMAIN, script->script_class->get_mono_ptr());
|
||||
|
||||
if (!mono_object) {
|
||||
// Important to clear this before destroying the script instance here
|
||||
script = Ref<CSharpScript>();
|
||||
owner->set_script_instance(NULL);
|
||||
owner = NULL;
|
||||
|
||||
bool die = _unreference_owner_unsafe();
|
||||
// Not ok for the owner to die here. If there is a situation where this can happen, it will be considered a bug.
|
||||
CRASH_COND(die == true);
|
||||
|
||||
ERR_EXPLAIN("Failed to allocate memory for the object");
|
||||
ERR_FAIL_V(NULL);
|
||||
}
|
||||
|
||||
// Tie managed to unmanaged
|
||||
gchandle = MonoGCHandle::create_strong(mono_object);
|
||||
|
||||
if (base_ref)
|
||||
_reference_owner_unsafe(); // Here, after assigning the gchandle (for the refcount_incremented callback)
|
||||
|
||||
CACHED_FIELD(GodotObject, ptr)->set_value_raw(mono_object, owner);
|
||||
|
||||
// Construct
|
||||
GDMonoMethod *ctor = script->script_class->get_method(CACHED_STRING_NAME(dotctor), 0);
|
||||
ctor->invoke_raw(mono_object, NULL);
|
||||
|
||||
// Tie managed to unmanaged
|
||||
gchandle = MonoGCHandle::create_strong(mono_object);
|
||||
|
||||
return mono_object;
|
||||
}
|
||||
|
||||
|
@ -1536,25 +1568,36 @@ void CSharpInstance::mono_object_disposed(MonoObject *p_obj) {
|
|||
CSharpLanguage::get_singleton()->release_script_gchandle(p_obj, gchandle);
|
||||
}
|
||||
|
||||
void CSharpInstance::mono_object_disposed_baseref(MonoObject *p_obj, bool p_is_finalizer, bool &r_owner_deleted) {
|
||||
void CSharpInstance::mono_object_disposed_baseref(MonoObject *p_obj, bool p_is_finalizer, bool &r_delete_owner, bool &r_remove_script_instance) {
|
||||
|
||||
#ifdef DEBUG_ENABLED
|
||||
CRASH_COND(!base_ref);
|
||||
CRASH_COND(gchandle.is_null());
|
||||
#endif
|
||||
|
||||
r_remove_script_instance = false;
|
||||
|
||||
if (_unreference_owner_unsafe()) {
|
||||
r_owner_deleted = true;
|
||||
// Safe to self destruct here with memdelete(owner), but it's deferred to the caller to prevent future mistakes.
|
||||
r_delete_owner = true;
|
||||
} else {
|
||||
r_owner_deleted = false;
|
||||
r_delete_owner = false;
|
||||
CSharpLanguage::get_singleton()->release_script_gchandle(p_obj, gchandle);
|
||||
if (p_is_finalizer && !GDMono::get_singleton()->is_finalizing_scripts_domain()) {
|
||||
// If the native instance is still alive, then it was
|
||||
// referenced from another thread before the finalizer could
|
||||
// unreference it and delete it, so we want to keep it.
|
||||
// GC.ReRegisterForFinalize(this) is not safe because the objects
|
||||
// referenced by this could have already been collected.
|
||||
// Instead we will create a new managed instance here.
|
||||
_internal_new_managed();
|
||||
|
||||
if (!p_is_finalizer) {
|
||||
// If the native instance is still alive and Dispose() was called
|
||||
// (instead of the finalizer), then we remove the script instance.
|
||||
r_remove_script_instance = true;
|
||||
} else if (!GDMono::get_singleton()->is_finalizing_scripts_domain()) {
|
||||
// If the native instance is still alive and this is called from the finalizer,
|
||||
// then it was referenced from another thread before the finalizer could
|
||||
// unreference and delete it, so we want to keep it.
|
||||
// GC.ReRegisterForFinalize(this) is not safe because the objects referenced by 'this'
|
||||
// could have already been collected. Instead we will create a new managed instance here.
|
||||
MonoObject *new_managed = _internal_new_managed();
|
||||
if (!new_managed) {
|
||||
r_remove_script_instance = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -1750,6 +1793,8 @@ CSharpInstance::CSharpInstance() :
|
|||
|
||||
CSharpInstance::~CSharpInstance() {
|
||||
|
||||
destructing_script_instance = true;
|
||||
|
||||
if (gchandle.is_valid()) {
|
||||
if (!predelete_notified && !ref_dying) {
|
||||
// This destructor is not called from the owners destructor.
|
||||
|
@ -1762,9 +1807,7 @@ CSharpInstance::~CSharpInstance() {
|
|||
|
||||
if (mono_object) {
|
||||
MonoException *exc = NULL;
|
||||
destructing_script_instance = true;
|
||||
GDMonoUtils::dispose(mono_object, &exc);
|
||||
destructing_script_instance = false;
|
||||
|
||||
if (exc) {
|
||||
GDMonoUtils::set_pending_exception(exc);
|
||||
|
@ -1772,11 +1815,23 @@ CSharpInstance::~CSharpInstance() {
|
|||
}
|
||||
}
|
||||
|
||||
gchandle->release(); // Make sure it's released
|
||||
gchandle->release(); // Make sure the gchandle is released
|
||||
}
|
||||
|
||||
if (base_ref && !ref_dying && owner) { // it may be called from the owner's destructor
|
||||
_unreference_owner_unsafe();
|
||||
// If not being called from the owner's destructor, and we still hold a reference to the owner
|
||||
if (base_ref && !ref_dying && owner && unsafe_referenced) {
|
||||
// The owner's script or script instance is being replaced (or removed)
|
||||
|
||||
// Transfer ownership to an "instance binding"
|
||||
|
||||
void *data = owner->get_script_instance_binding(CSharpLanguage::get_singleton()->get_language_index());
|
||||
CRASH_COND(data == NULL);
|
||||
|
||||
CSharpScriptBinding &script_binding = ((Map<Object *, CSharpScriptBinding>::Element *)data)->get();
|
||||
CRASH_COND(!script_binding.inited);
|
||||
|
||||
bool die = _unreference_owner_unsafe();
|
||||
CRASH_COND(die == true); // The "instance binding" should be holding a reference
|
||||
}
|
||||
|
||||
if (script.is_valid() && owner) {
|
||||
|
@ -2327,6 +2382,32 @@ StringName CSharpScript::get_instance_base_type() const {
|
|||
CSharpInstance *CSharpScript::_create_instance(const Variant **p_args, int p_argcount, Object *p_owner, bool p_isref, Variant::CallError &r_error) {
|
||||
|
||||
/* STEP 1, CREATE */
|
||||
Ref<Reference> ref;
|
||||
if (p_isref) {
|
||||
// Hold it alive. Important if we have to dispose a script instance binding before creating the CSharpInstance.
|
||||
ref = Ref<Reference>(static_cast<Reference *>(p_owner));
|
||||
}
|
||||
|
||||
// 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());
|
||||
CRASH_COND(data == NULL);
|
||||
|
||||
CSharpScriptBinding &script_binding = ((Map<Object *, CSharpScriptBinding>::Element *)data)->get();
|
||||
if (script_binding.inited && script_binding.gchandle.is_valid()) {
|
||||
MonoObject *mono_object = script_binding.gchandle->get_target();
|
||||
if (mono_object) {
|
||||
MonoException *exc = NULL;
|
||||
GDMonoUtils::dispose(mono_object, &exc);
|
||||
|
||||
if (exc) {
|
||||
GDMonoUtils::set_pending_exception(exc);
|
||||
}
|
||||
}
|
||||
|
||||
script_binding.inited = false;
|
||||
}
|
||||
}
|
||||
|
||||
CSharpInstance *instance = memnew(CSharpInstance);
|
||||
instance->base_ref = p_isref;
|
||||
|
@ -2334,16 +2415,20 @@ CSharpInstance *CSharpScript::_create_instance(const Variant **p_args, int p_arg
|
|||
instance->owner = p_owner;
|
||||
instance->owner->set_script_instance(instance);
|
||||
|
||||
if (instance->base_ref)
|
||||
instance->_reference_owner_unsafe();
|
||||
|
||||
/* STEP 2, INITIALIZE AND CONSTRUCT */
|
||||
|
||||
MonoObject *mono_object = mono_object_new(SCRIPTS_DOMAIN, script_class->get_mono_ptr());
|
||||
|
||||
if (!mono_object) {
|
||||
// Important to clear this before destroying the script instance here
|
||||
instance->script = Ref<CSharpScript>();
|
||||
instance->owner->set_script_instance(NULL);
|
||||
instance->owner = NULL;
|
||||
|
||||
bool die = instance->_unreference_owner_unsafe();
|
||||
// Not ok for the owner to die here. If there is a situation where this can happen, it will be considered a bug.
|
||||
CRASH_COND(die == true);
|
||||
|
||||
p_owner->set_script_instance(NULL);
|
||||
r_error.error = Variant::CallError::CALL_ERROR_INSTANCE_IS_NULL;
|
||||
ERR_EXPLAIN("Failed to allocate memory for the object");
|
||||
ERR_FAIL_V(NULL);
|
||||
|
@ -2352,6 +2437,9 @@ CSharpInstance *CSharpScript::_create_instance(const Variant **p_args, int p_arg
|
|||
// Tie managed to unmanaged
|
||||
instance->gchandle = MonoGCHandle::create_strong(mono_object);
|
||||
|
||||
if (instance->base_ref)
|
||||
instance->_reference_owner_unsafe(); // Here, after assigning the gchandle (for the refcount_incremented callback)
|
||||
|
||||
{
|
||||
SCOPED_MUTEX_LOCK(CSharpLanguage::get_singleton()->script_instances_mutex);
|
||||
instances.insert(instance->owner);
|
||||
|
|
|
@ -206,8 +206,15 @@ class CSharpInstance : public ScriptInstance {
|
|||
Ref<MonoGCHandle> gchandle;
|
||||
|
||||
bool _reference_owner_unsafe();
|
||||
|
||||
/*
|
||||
* If true is returned, the caller must memdelete the script instance's owner.
|
||||
*/
|
||||
bool _unreference_owner_unsafe();
|
||||
|
||||
/*
|
||||
* If NULL is returned, the caller must destroy the script instance by removing it from its owner.
|
||||
*/
|
||||
MonoObject *_internal_new_managed();
|
||||
|
||||
// Do not use unless you know what you are doing
|
||||
|
@ -223,6 +230,8 @@ public:
|
|||
|
||||
_FORCE_INLINE_ bool is_destructing_script_instance() { return destructing_script_instance; }
|
||||
|
||||
virtual Object *get_owner();
|
||||
|
||||
virtual bool set(const StringName &p_name, const Variant &p_value);
|
||||
virtual bool get(const StringName &p_name, Variant &r_ret) const;
|
||||
virtual void get_property_list(List<PropertyInfo> *p_properties) const;
|
||||
|
@ -235,7 +244,12 @@ public:
|
|||
virtual void call_multilevel_reversed(const StringName &p_method, const Variant **p_args, int p_argcount);
|
||||
|
||||
void mono_object_disposed(MonoObject *p_obj);
|
||||
void mono_object_disposed_baseref(MonoObject *p_obj, bool p_is_finalizer, bool &r_owner_deleted);
|
||||
|
||||
/*
|
||||
* If 'r_delete_owner' is set to true, the caller must memdelete the script instance's owner. Otherwise, if
|
||||
* 'r_remove_script_instance' is set to true, the caller must destroy the script instance by removing it from its owner.
|
||||
*/
|
||||
void mono_object_disposed_baseref(MonoObject *p_obj, bool p_is_finalizer, bool &r_delete_owner, bool &r_remove_script_instance);
|
||||
|
||||
virtual void refcount_incremented();
|
||||
virtual bool refcount_decremented();
|
||||
|
@ -255,6 +269,7 @@ public:
|
|||
};
|
||||
|
||||
struct CSharpScriptBinding {
|
||||
bool inited;
|
||||
StringName type_name;
|
||||
GDMonoClass *wrapper_class;
|
||||
Ref<MonoGCHandle> gchandle;
|
||||
|
@ -391,6 +406,8 @@ public:
|
|||
virtual void refcount_incremented_instance_binding(Object *p_object);
|
||||
virtual bool refcount_decremented_instance_binding(Object *p_object);
|
||||
|
||||
bool setup_csharp_script_binding(CSharpScriptBinding &r_script_binding, Object *p_object);
|
||||
|
||||
#ifdef DEBUG_ENABLED
|
||||
Vector<StackInfo> stack_trace_get_info(MonoObject *p_stack_trace);
|
||||
#endif
|
||||
|
|
|
@ -65,9 +65,12 @@ void godot_icall_Object_Disposed(MonoObject *p_obj, Object *p_ptr) {
|
|||
void *data = p_ptr->get_script_instance_binding(CSharpLanguage::get_singleton()->get_language_index());
|
||||
|
||||
if (data) {
|
||||
Ref<MonoGCHandle> &gchandle = ((Map<Object *, CSharpScriptBinding>::Element *)data)->get().gchandle;
|
||||
if (gchandle.is_valid()) {
|
||||
CSharpLanguage::release_script_gchandle(p_obj, gchandle);
|
||||
CSharpScriptBinding &script_binding = ((Map<Object *, CSharpScriptBinding>::Element *)data)->get();
|
||||
if (script_binding.inited) {
|
||||
Ref<MonoGCHandle> &gchandle = script_binding.gchandle;
|
||||
if (gchandle.is_valid()) {
|
||||
CSharpLanguage::release_script_gchandle(p_obj, gchandle);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -85,11 +88,14 @@ void godot_icall_Reference_Disposed(MonoObject *p_obj, Object *p_ptr, bool p_is_
|
|||
CSharpInstance *cs_instance = CAST_CSHARP_INSTANCE(ref->get_script_instance());
|
||||
if (cs_instance) {
|
||||
if (!cs_instance->is_destructing_script_instance()) {
|
||||
bool r_owner_deleted;
|
||||
cs_instance->mono_object_disposed_baseref(p_obj, p_is_finalizer, r_owner_deleted);
|
||||
if (!r_owner_deleted && !p_is_finalizer) {
|
||||
// If the native instance is still alive and Dispose() was called
|
||||
// (instead of the finalizer), then we remove the script instance.
|
||||
bool delete_owner;
|
||||
bool remove_script_instance;
|
||||
|
||||
cs_instance->mono_object_disposed_baseref(p_obj, p_is_finalizer, delete_owner, remove_script_instance);
|
||||
|
||||
if (delete_owner) {
|
||||
memdelete(ref);
|
||||
} else if (remove_script_instance) {
|
||||
ref->set_script_instance(NULL);
|
||||
}
|
||||
}
|
||||
|
@ -105,9 +111,12 @@ void godot_icall_Reference_Disposed(MonoObject *p_obj, Object *p_ptr, bool p_is_
|
|||
void *data = ref->get_script_instance_binding(CSharpLanguage::get_singleton()->get_language_index());
|
||||
|
||||
if (data) {
|
||||
Ref<MonoGCHandle> &gchandle = ((Map<Object *, CSharpScriptBinding>::Element *)data)->get().gchandle;
|
||||
if (gchandle.is_valid()) {
|
||||
CSharpLanguage::release_script_gchandle(p_obj, gchandle);
|
||||
CSharpScriptBinding &script_binding = ((Map<Object *, CSharpScriptBinding>::Element *)data)->get();
|
||||
if (script_binding.inited) {
|
||||
Ref<MonoGCHandle> &gchandle = script_binding.gchandle;
|
||||
if (gchandle.is_valid()) {
|
||||
CSharpLanguage::release_script_gchandle(p_obj, gchandle);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -138,7 +147,7 @@ MonoObject *godot_icall_Object_weakref(Object *p_obj) {
|
|||
wref->set_obj(p_obj);
|
||||
}
|
||||
|
||||
return GDMonoUtils::create_managed_for_godot_object(CACHED_CLASS(WeakRef), Reference::get_class_static(), Object::cast_to<Object>(wref.ptr()));
|
||||
return GDMonoUtils::unmanaged_get_managed(wref.ptr());
|
||||
}
|
||||
|
||||
Error godot_icall_SignalAwaiter_connect(Object *p_source, MonoString *p_signal, Object *p_target, MonoObject *p_awaiter) {
|
||||
|
|
|
@ -65,7 +65,7 @@ Ref<MonoGCHandle> MonoGCHandle::create_weak(MonoObject *p_object) {
|
|||
void MonoGCHandle::release() {
|
||||
|
||||
#ifdef DEBUG_ENABLED
|
||||
CRASH_COND(GDMono::get_singleton() == NULL);
|
||||
CRASH_COND(!released && GDMono::get_singleton() == NULL);
|
||||
#endif
|
||||
|
||||
if (!released && GDMono::get_singleton()->is_runtime_initialized()) {
|
||||
|
|
|
@ -265,61 +265,70 @@ void clear_cache() {
|
|||
}
|
||||
|
||||
MonoObject *unmanaged_get_managed(Object *unmanaged) {
|
||||
if (unmanaged) {
|
||||
if (unmanaged->get_script_instance()) {
|
||||
CSharpInstance *cs_instance = CAST_CSHARP_INSTANCE(unmanaged->get_script_instance());
|
||||
|
||||
if (cs_instance) {
|
||||
return cs_instance->get_mono_object();
|
||||
}
|
||||
}
|
||||
if (!unmanaged)
|
||||
return NULL;
|
||||
|
||||
// If the owner does not have a CSharpInstance...
|
||||
if (unmanaged->get_script_instance()) {
|
||||
CSharpInstance *cs_instance = CAST_CSHARP_INSTANCE(unmanaged->get_script_instance());
|
||||
|
||||
void *data = unmanaged->get_script_instance_binding(CSharpLanguage::get_singleton()->get_language_index());
|
||||
|
||||
if (data) {
|
||||
CSharpScriptBinding &script_binding = ((Map<Object *, CSharpScriptBinding>::Element *)data)->value();
|
||||
|
||||
Ref<MonoGCHandle> &gchandle = script_binding.gchandle;
|
||||
ERR_FAIL_COND_V(gchandle.is_null(), NULL);
|
||||
|
||||
MonoObject *target = gchandle->get_target();
|
||||
|
||||
if (target)
|
||||
return target;
|
||||
|
||||
CSharpLanguage::get_singleton()->release_script_gchandle(gchandle);
|
||||
|
||||
// Create a new one
|
||||
|
||||
#ifdef DEBUG_ENABLED
|
||||
CRASH_COND(script_binding.type_name == StringName());
|
||||
CRASH_COND(script_binding.wrapper_class == NULL);
|
||||
#endif
|
||||
|
||||
MonoObject *mono_object = GDMonoUtils::create_managed_for_godot_object(script_binding.wrapper_class, script_binding.type_name, unmanaged);
|
||||
ERR_FAIL_NULL_V(mono_object, NULL);
|
||||
|
||||
gchandle->set_handle(MonoGCHandle::new_strong_handle(mono_object), MonoGCHandle::STRONG_HANDLE);
|
||||
|
||||
// Tie managed to unmanaged
|
||||
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();
|
||||
}
|
||||
|
||||
return mono_object;
|
||||
if (cs_instance) {
|
||||
return cs_instance->get_mono_object();
|
||||
}
|
||||
}
|
||||
|
||||
return NULL;
|
||||
// If the owner does not have a CSharpInstance...
|
||||
|
||||
void *data = unmanaged->get_script_instance_binding(CSharpLanguage::get_singleton()->get_language_index());
|
||||
|
||||
if (!data)
|
||||
return 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);
|
||||
|
||||
if (!script_binding.inited)
|
||||
return NULL;
|
||||
}
|
||||
|
||||
Ref<MonoGCHandle> &gchandle = script_binding.gchandle;
|
||||
ERR_FAIL_COND_V(gchandle.is_null(), NULL);
|
||||
|
||||
MonoObject *target = gchandle->get_target();
|
||||
|
||||
if (target)
|
||||
return target;
|
||||
|
||||
CSharpLanguage::get_singleton()->release_script_gchandle(gchandle);
|
||||
|
||||
// Create a new one
|
||||
|
||||
#ifdef DEBUG_ENABLED
|
||||
CRASH_COND(script_binding.type_name == StringName());
|
||||
CRASH_COND(script_binding.wrapper_class == NULL);
|
||||
#endif
|
||||
|
||||
MonoObject *mono_object = GDMonoUtils::create_managed_for_godot_object(script_binding.wrapper_class, script_binding.type_name, unmanaged);
|
||||
ERR_FAIL_NULL_V(mono_object, NULL);
|
||||
|
||||
gchandle->set_handle(MonoGCHandle::new_strong_handle(mono_object), MonoGCHandle::STRONG_HANDLE);
|
||||
|
||||
// Tie managed to unmanaged
|
||||
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();
|
||||
}
|
||||
|
||||
return mono_object;
|
||||
}
|
||||
|
||||
void set_main_thread(MonoThread *p_thread) {
|
||||
|
|
Loading…
Reference in a new issue