summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--core/object.cpp7
-rw-r--r--core/object.h6
-rw-r--r--core/reference.cpp29
-rw-r--r--core/script_language.h2
-rw-r--r--modules/mono/csharp_script.cpp75
-rw-r--r--modules/mono/csharp_script.h2
-rw-r--r--modules/mono/mono_gc_handle.cpp7
-rw-r--r--modules/mono/mono_gc_handle.h15
-rw-r--r--modules/mono/signal_awaiter_utils.cpp7
-rw-r--r--modules/mono/signal_awaiter_utils.h2
10 files changed, 129 insertions, 23 deletions
diff --git a/core/object.cpp b/core/object.cpp
index aaa37e6cf2..307142b43b 100644
--- a/core/object.cpp
+++ b/core/object.cpp
@@ -1825,7 +1825,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];
@@ -1840,6 +1844,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
diff --git a/core/object.h b/core/object.h
index 8306b5a356..e0a6b3d9e5 100644
--- a/core/object.h
+++ b/core/object.h
@@ -484,10 +484,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; };
diff --git a/core/reference.cpp b/core/reference.cpp
index c33a7c683c..6e1520d81d 100644
--- a/core/reference.cpp
+++ b/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;
diff --git a/core/script_language.h b/core/script_language.h
index d1da0a3b72..4c0acc406d 100644
--- a/core/script_language.h
+++ b/core/script_language.h
@@ -284,6 +284,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();
diff --git a/modules/mono/csharp_script.cpp b/modules/mono/csharp_script.cpp
index fb45136575..1901b2cae8 100644
--- a/modules/mono/csharp_script.cpp
+++ b/modules/mono/csharp_script.cpp
@@ -955,6 +955,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);
@@ -1245,11 +1308,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.
@@ -1257,26 +1322,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;
}
diff --git a/modules/mono/csharp_script.h b/modules/mono/csharp_script.h
index ffb1d2e0f4..b4fecfa912 100644
--- a/modules/mono/csharp_script.h
+++ b/modules/mono/csharp_script.h
@@ -345,6 +345,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);
diff --git a/modules/mono/mono_gc_handle.cpp b/modules/mono/mono_gc_handle.cpp
index 4e82bcd03e..91a60fb716 100644
--- a/modules/mono/mono_gc_handle.cpp
+++ b/modules/mono/mono_gc_handle.cpp
@@ -47,12 +47,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() {
@@ -67,9 +67,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;
}
diff --git a/modules/mono/mono_gc_handle.h b/modules/mono/mono_gc_handle.h
index 9cb3ef0fbb..7eeaba30e0 100644
--- a/modules/mono/mono_gc_handle.h
+++ b/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();
};
diff --git a/modules/mono/signal_awaiter_utils.cpp b/modules/mono/signal_awaiter_utils.cpp
index b9d8520ac9..4a7f574bdc 100644
--- a/modules/mono/signal_awaiter_utils.cpp
+++ b/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
@@ -117,8 +116,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;
diff --git a/modules/mono/signal_awaiter_utils.h b/modules/mono/signal_awaiter_utils.h
index a6a205ff8d..1920432709 100644
--- a/modules/mono/signal_awaiter_utils.h
+++ b/modules/mono/signal_awaiter_utils.h
@@ -64,7 +64,7 @@ public:
}
#endif
- SignalAwaiterHandle(uint32_t p_managed_handle);
+ SignalAwaiterHandle(MonoObject *p_managed);
~SignalAwaiterHandle();
};