Kaynağa Gözat

Merge pull request #16927 from neikeq/rework-refcount-notify

Notify instance binding data api of refcount increment/decrement
Juan Linietsky 7 yıl önce
ebeveyn
işleme
8c435a343e

+ 6 - 1
core/object.cpp

@@ -1916,7 +1916,11 @@ void *Object::get_script_instance_binding(int p_script_language_index) {
 	//as it should not really affect performance much (won't be called too often), as in far most caes the condition below will be false afterwards
 
 	if (!_script_instance_bindings[p_script_language_index]) {
-		_script_instance_bindings[p_script_language_index] = ScriptServer::get_language(p_script_language_index)->alloc_instance_binding_data(this);
+		void *script_data = ScriptServer::get_language(p_script_language_index)->alloc_instance_binding_data(this);
+		if (script_data) {
+			atomic_increment(&instance_binding_count);
+			_script_instance_bindings[p_script_language_index] = script_data;
+		}
 	}
 
 	return _script_instance_bindings[p_script_language_index];
@@ -1931,6 +1935,7 @@ Object::Object() {
 	_instance_ID = ObjectDB::add_instance(this);
 	_can_translate = true;
 	_is_queued_for_deletion = false;
+	instance_binding_count = 0;
 	memset(_script_instance_bindings, 0, sizeof(void *) * MAX_SCRIPT_INSTANCE_BINDINGS);
 	script_instance = NULL;
 #ifdef TOOLS_ENABLED

+ 4 - 2
core/object.h

@@ -490,10 +490,12 @@ private:
 	void _set_indexed_bind(const NodePath &p_name, const Variant &p_value);
 	Variant _get_indexed_bind(const NodePath &p_name) const;
 
-	void *_script_instance_bindings[MAX_SCRIPT_INSTANCE_BINDINGS];
-
 	void property_list_changed_notify();
 
+	friend class Reference;
+	uint32_t instance_binding_count;
+	void *_script_instance_bindings[MAX_SCRIPT_INSTANCE_BINDINGS];
+
 protected:
 	virtual void _initialize_classv() { initialize_class(); }
 	virtual bool _setv(const StringName &p_name, const Variant &p_property) { return false; };

+ 24 - 5
core/reference.cpp

@@ -66,8 +66,17 @@ int Reference::reference_get_count() const {
 bool Reference::reference() {
 	bool success = refcount.ref();
 
-	if (success && get_script_instance()) {
-		get_script_instance()->refcount_incremented();
+	if (success && refcount.get() <= 2 /* higher is not relevant */) {
+		if (get_script_instance()) {
+			get_script_instance()->refcount_incremented();
+		}
+		if (instance_binding_count > 0) {
+			for (int i = 0; i < MAX_SCRIPT_INSTANCE_BINDINGS; i++) {
+				if (_script_instance_bindings[i]) {
+					ScriptServer::get_language(i)->refcount_incremented_instance_binding(this);
+				}
+			}
+		}
 	}
 
 	return success;
@@ -77,9 +86,19 @@ bool Reference::unreference() {
 
 	bool die = refcount.unref();
 
-	if (get_script_instance()) {
-		bool script_ret = get_script_instance()->refcount_decremented();
-		die = die && script_ret;
+	if (refcount.get() <= 1 /* higher is not relevant */) {
+		if (get_script_instance()) {
+			bool script_ret = get_script_instance()->refcount_decremented();
+			die = die && script_ret;
+		}
+		if (instance_binding_count > 0) {
+			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;
+				}
+			}
+		}
 	}
 
 	return die;

+ 2 - 0
core/script_language.h

@@ -311,6 +311,8 @@ public:
 
 	virtual void *alloc_instance_binding_data(Object *p_object) { return NULL; } //optional, not used by all languages
 	virtual void free_instance_binding_data(void *p_data) {} //optional, not used by all languages
+	virtual void refcount_incremented_instance_binding(Object *p_object) {} //optional, not used by all languages
+	virtual bool refcount_decremented_instance_binding(Object *p_object) { return true; } //return true if it can die //optional, not used by all languages
 
 	virtual void frame();
 

+ 71 - 4
modules/mono/csharp_script.cpp

@@ -1013,6 +1013,69 @@ void CSharpLanguage::free_instance_binding_data(void *p_data) {
 #endif
 }
 
+void CSharpLanguage::refcount_incremented_instance_binding(Object *p_object) {
+
+	Reference *ref_owner = Object::cast_to<Reference>(p_object);
+
+#ifdef DEBUG_ENABLED
+	CRASH_COND(!ref_owner);
+#endif
+
+	void *data = p_object->get_script_instance_binding(get_language_index());
+	if (!data)
+		return;
+	Ref<MonoGCHandle> &gchandle = ((Map<Object *, Ref<MonoGCHandle> >::Element *)data)->get();
+
+	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.
+		// 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; // Called after the managed side was collected, so nothing to do here
+
+		// Release the current weak handle and replace it with a strong handle.
+		uint32_t strong_gchandle = MonoGCHandle::make_strong_handle(target);
+		gchandle->release();
+		gchandle->set_handle(strong_gchandle, MonoGCHandle::STRONG_HANDLE);
+	}
+}
+
+bool CSharpLanguage::refcount_decremented_instance_binding(Object *p_object) {
+
+	Reference *ref_owner = Object::cast_to<Reference>(p_object);
+
+#ifdef DEBUG_ENABLED
+	CRASH_COND(!ref_owner);
+#endif
+
+	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 *, Ref<MonoGCHandle> >::Element *)data)->get();
+
+	if (refcount == 1 && !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,
+		// the managed instance takes responsibility of deleting the owner when GCed.
+
+		MonoObject *target = gchandle->get_target();
+		if (!target)
+			return refcount == 0; // Called after the managed side was collected, so nothing to do here
+
+		// Release the current strong handle and replace it with a weak handle.
+		uint32_t weak_gchandle = MonoGCHandle::make_weak_handle(target);
+		gchandle->release();
+		gchandle->set_handle(weak_gchandle, MonoGCHandle::WEAK_HANDLE);
+
+		return false;
+	}
+
+	return refcount == 0;
+}
+
 CSharpInstance *CSharpInstance::create_for_managed_type(Object *p_owner, CSharpScript *p_script, const Ref<MonoGCHandle> &p_gchandle) {
 
 	CSharpInstance *instance = memnew(CSharpInstance);
@@ -1303,11 +1366,13 @@ void CSharpInstance::mono_object_disposed() {
 
 void CSharpInstance::refcount_incremented() {
 
+#ifdef DEBUG_ENABLED
 	CRASH_COND(!base_ref);
+#endif
 
 	Reference *ref_owner = Object::cast_to<Reference>(owner);
 
-	if (ref_owner->reference_get_count() > 1) { // The managed side also holds a reference, hence 1 instead of 0
+	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.
 		// 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.
@@ -1315,26 +1380,28 @@ void CSharpInstance::refcount_incremented() {
 		// Release the current weak handle and replace it with a strong handle.
 		uint32_t strong_gchandle = MonoGCHandle::make_strong_handle(gchandle->get_target());
 		gchandle->release();
-		gchandle->set_handle(strong_gchandle);
+		gchandle->set_handle(strong_gchandle, MonoGCHandle::STRONG_HANDLE);
 	}
 }
 
 bool CSharpInstance::refcount_decremented() {
 
+#ifdef DEBUG_ENABLED
 	CRASH_COND(!base_ref);
+#endif
 
 	Reference *ref_owner = Object::cast_to<Reference>(owner);
 
 	int refcount = ref_owner->reference_get_count();
 
-	if (refcount == 1) { // The managed side also holds a reference, hence 1 instead of 0
+	if (refcount == 1 && !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,
 		// the managed instance takes responsibility of deleting the owner when GCed.
 
 		// Release the current strong handle and replace it with a weak handle.
 		uint32_t weak_gchandle = MonoGCHandle::make_weak_handle(gchandle->get_target());
 		gchandle->release();
-		gchandle->set_handle(weak_gchandle);
+		gchandle->set_handle(weak_gchandle, MonoGCHandle::WEAK_HANDLE);
 
 		return false;
 	}

+ 2 - 0
modules/mono/csharp_script.h

@@ -346,6 +346,8 @@ public:
 	// Don't use these. I'm watching you
 	virtual void *alloc_instance_binding_data(Object *p_object);
 	virtual void free_instance_binding_data(void *p_data);
+	virtual void refcount_incremented_instance_binding(Object *p_object);
+	virtual bool refcount_decremented_instance_binding(Object *p_object);
 
 #ifdef DEBUG_ENABLED
 	Vector<StackInfo> stack_trace_get_info(MonoObject *p_stack_trace);

+ 4 - 3
modules/mono/mono_gc_handle.cpp

@@ -44,12 +44,12 @@ uint32_t MonoGCHandle::make_weak_handle(MonoObject *p_object) {
 
 Ref<MonoGCHandle> MonoGCHandle::create_strong(MonoObject *p_object) {
 
-	return memnew(MonoGCHandle(make_strong_handle(p_object)));
+	return memnew(MonoGCHandle(make_strong_handle(p_object), STRONG_HANDLE));
 }
 
 Ref<MonoGCHandle> MonoGCHandle::create_weak(MonoObject *p_object) {
 
-	return memnew(MonoGCHandle(make_weak_handle(p_object)));
+	return memnew(MonoGCHandle(make_weak_handle(p_object), WEAK_HANDLE));
 }
 
 void MonoGCHandle::release() {
@@ -64,9 +64,10 @@ void MonoGCHandle::release() {
 	}
 }
 
-MonoGCHandle::MonoGCHandle(uint32_t p_handle) {
+MonoGCHandle::MonoGCHandle(uint32_t p_handle, HandleType p_handle_type) {
 
 	released = false;
+	weak = p_handle_type == WEAK_HANDLE;
 	handle = p_handle;
 }
 

+ 12 - 3
modules/mono/mono_gc_handle.h

@@ -40,24 +40,33 @@ class MonoGCHandle : public Reference {
 	GDCLASS(MonoGCHandle, Reference)
 
 	bool released;
+	bool weak;
 	uint32_t handle;
 
 public:
+	enum HandleType {
+		STRONG_HANDLE,
+		WEAK_HANDLE
+	};
+
 	static uint32_t make_strong_handle(MonoObject *p_object);
 	static uint32_t make_weak_handle(MonoObject *p_object);
 
 	static Ref<MonoGCHandle> create_strong(MonoObject *p_object);
 	static Ref<MonoGCHandle> create_weak(MonoObject *p_object);
 
+	_FORCE_INLINE_ bool is_weak() { return weak; }
+
 	_FORCE_INLINE_ MonoObject *get_target() const { return released ? NULL : mono_gchandle_get_target(handle); }
 
-	_FORCE_INLINE_ void set_handle(uint32_t p_handle) {
-		handle = p_handle;
+	_FORCE_INLINE_ void set_handle(uint32_t p_handle, HandleType p_handle_type) {
 		released = false;
+		weak = p_handle_type == WEAK_HANDLE;
+		handle = p_handle;
 	}
 	void release();
 
-	MonoGCHandle(uint32_t p_handle);
+	MonoGCHandle(uint32_t p_handle, HandleType p_handle_type);
 	~MonoGCHandle();
 };
 

+ 3 - 4
modules/mono/signal_awaiter_utils.cpp

@@ -42,8 +42,7 @@ Error connect_signal_awaiter(Object *p_source, const String &p_signal, Object *p
 	ERR_FAIL_NULL_V(p_source, ERR_INVALID_DATA);
 	ERR_FAIL_NULL_V(p_target, ERR_INVALID_DATA);
 
-	uint32_t awaiter_handle = MonoGCHandle::make_strong_handle(p_awaiter);
-	Ref<SignalAwaiterHandle> sa_con = memnew(SignalAwaiterHandle(awaiter_handle));
+	Ref<SignalAwaiterHandle> sa_con = memnew(SignalAwaiterHandle(p_awaiter));
 #ifdef DEBUG_ENABLED
 	sa_con->set_connection_target(p_target);
 #endif
@@ -119,8 +118,8 @@ void SignalAwaiterHandle::_bind_methods() {
 	ClassDB::bind_vararg_method(METHOD_FLAGS_DEFAULT, "_signal_callback", &SignalAwaiterHandle::_signal_callback, MethodInfo("_signal_callback"));
 }
 
-SignalAwaiterHandle::SignalAwaiterHandle(uint32_t p_managed_handle) :
-		MonoGCHandle(p_managed_handle) {
+SignalAwaiterHandle::SignalAwaiterHandle(MonoObject *p_managed) :
+		MonoGCHandle(MonoGCHandle::make_strong_handle(p_managed), STRONG_HANDLE) {
 
 #ifdef DEBUG_ENABLED
 	conn_target_id = 0;

+ 1 - 1
modules/mono/signal_awaiter_utils.h

@@ -64,7 +64,7 @@ public:
 	}
 #endif
 
-	SignalAwaiterHandle(uint32_t p_managed_handle);
+	SignalAwaiterHandle(MonoObject *p_managed);
 	~SignalAwaiterHandle();
 };