Browse Source

Merge pull request #24098 from neikeq/kk

Fix crash due to ~CSharpInstance() being called on freed instance
Ignacio Etcheverry 6 years ago
parent
commit
ca28c455bf

+ 6 - 3
modules/mono/csharp_script.cpp

@@ -993,7 +993,7 @@ void CSharpLanguage::set_language_index(int p_idx) {
 
 
 void CSharpLanguage::release_script_gchandle(Ref<MonoGCHandle> &p_gchandle) {
 void CSharpLanguage::release_script_gchandle(Ref<MonoGCHandle> &p_gchandle) {
 
 
-	if (!p_gchandle->is_released()) { // Do not locking unnecessarily
+	if (!p_gchandle->is_released()) { // Do not lock unnecessarily
 		SCOPED_MUTEX_LOCK(get_singleton()->script_gchandle_release_mutex);
 		SCOPED_MUTEX_LOCK(get_singleton()->script_gchandle_release_mutex);
 		p_gchandle->release();
 		p_gchandle->release();
 	}
 	}
@@ -1003,7 +1003,7 @@ void CSharpLanguage::release_script_gchandle(MonoObject *p_expected_obj, Ref<Mon
 
 
 	uint32_t pinned_gchandle = MonoGCHandle::new_strong_handle_pinned(p_expected_obj); // We might lock after this, so pin it
 	uint32_t pinned_gchandle = MonoGCHandle::new_strong_handle_pinned(p_expected_obj); // We might lock after this, so pin it
 
 
-	if (!p_gchandle->is_released()) { // Do not locking unnecessarily
+	if (!p_gchandle->is_released()) { // Do not lock unnecessarily
 		SCOPED_MUTEX_LOCK(get_singleton()->script_gchandle_release_mutex);
 		SCOPED_MUTEX_LOCK(get_singleton()->script_gchandle_release_mutex);
 
 
 		MonoObject *target = p_gchandle->get_target();
 		MonoObject *target = p_gchandle->get_target();
@@ -1754,7 +1754,8 @@ CSharpInstance::CSharpInstance() :
 		base_ref(false),
 		base_ref(false),
 		ref_dying(false),
 		ref_dying(false),
 		unsafe_referenced(false),
 		unsafe_referenced(false),
-		predelete_notified(false) {
+		predelete_notified(false),
+		destructing_script_instance(false) {
 }
 }
 
 
 CSharpInstance::~CSharpInstance() {
 CSharpInstance::~CSharpInstance() {
@@ -1771,7 +1772,9 @@ CSharpInstance::~CSharpInstance() {
 
 
 			if (mono_object) {
 			if (mono_object) {
 				MonoException *exc = NULL;
 				MonoException *exc = NULL;
+				destructing_script_instance = true;
 				GDMonoUtils::dispose(mono_object, &exc);
 				GDMonoUtils::dispose(mono_object, &exc);
+				destructing_script_instance = false;
 
 
 				if (exc) {
 				if (exc) {
 					GDMonoUtils::set_pending_exception(exc);
 					GDMonoUtils::set_pending_exception(exc);

+ 3 - 0
modules/mono/csharp_script.h

@@ -198,6 +198,7 @@ class CSharpInstance : public ScriptInstance {
 	bool ref_dying;
 	bool ref_dying;
 	bool unsafe_referenced;
 	bool unsafe_referenced;
 	bool predelete_notified;
 	bool predelete_notified;
+	bool destructing_script_instance;
 
 
 	Ref<CSharpScript> script;
 	Ref<CSharpScript> script;
 	Ref<MonoGCHandle> gchandle;
 	Ref<MonoGCHandle> gchandle;
@@ -218,6 +219,8 @@ class CSharpInstance : public ScriptInstance {
 public:
 public:
 	MonoObject *get_mono_object() const;
 	MonoObject *get_mono_object() const;
 
 
+	_FORCE_INLINE_ bool is_destructing_script_instance() { return destructing_script_instance; }
+
 	virtual bool set(const StringName &p_name, const Variant &p_value);
 	virtual bool set(const StringName &p_name, const Variant &p_value);
 	virtual bool get(const StringName &p_name, Variant &r_ret) const;
 	virtual bool get(const StringName &p_name, Variant &r_ret) const;
 	virtual void get_property_list(List<PropertyInfo> *p_properties) const;
 	virtual void get_property_list(List<PropertyInfo> *p_properties) const;

+ 12 - 8
modules/mono/glue/base_object_glue.cpp

@@ -54,8 +54,10 @@ void godot_icall_Object_Disposed(MonoObject *p_obj, Object *p_ptr) {
 	if (p_ptr->get_script_instance()) {
 	if (p_ptr->get_script_instance()) {
 		CSharpInstance *cs_instance = CAST_CSHARP_INSTANCE(p_ptr->get_script_instance());
 		CSharpInstance *cs_instance = CAST_CSHARP_INSTANCE(p_ptr->get_script_instance());
 		if (cs_instance) {
 		if (cs_instance) {
-			cs_instance->mono_object_disposed(p_obj);
-			p_ptr->set_script_instance(NULL);
+			if (!cs_instance->is_destructing_script_instance()) {
+				cs_instance->mono_object_disposed(p_obj);
+				p_ptr->set_script_instance(NULL);
+			}
 			return;
 			return;
 		}
 		}
 	}
 	}
@@ -82,12 +84,14 @@ void godot_icall_Reference_Disposed(MonoObject *p_obj, Object *p_ptr, bool p_is_
 	if (ref->get_script_instance()) {
 	if (ref->get_script_instance()) {
 		CSharpInstance *cs_instance = CAST_CSHARP_INSTANCE(ref->get_script_instance());
 		CSharpInstance *cs_instance = CAST_CSHARP_INSTANCE(ref->get_script_instance());
 		if (cs_instance) {
 		if (cs_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.
-				ref->set_script_instance(NULL);
+			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.
+					ref->set_script_instance(NULL);
+				}
 			}
 			}
 			return;
 			return;
 		}
 		}