Merge pull request #16927 from neikeq/rework-refcount-notify
Notify instance binding data api of refcount increment/decrement
This commit is contained in:
commit
8c435a343e
10 changed files with 129 additions and 23 deletions
|
@ -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
|
//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]) {
|
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];
|
return _script_instance_bindings[p_script_language_index];
|
||||||
|
@ -1931,6 +1935,7 @@ Object::Object() {
|
||||||
_instance_ID = ObjectDB::add_instance(this);
|
_instance_ID = ObjectDB::add_instance(this);
|
||||||
_can_translate = true;
|
_can_translate = true;
|
||||||
_is_queued_for_deletion = false;
|
_is_queued_for_deletion = false;
|
||||||
|
instance_binding_count = 0;
|
||||||
memset(_script_instance_bindings, 0, sizeof(void *) * MAX_SCRIPT_INSTANCE_BINDINGS);
|
memset(_script_instance_bindings, 0, sizeof(void *) * MAX_SCRIPT_INSTANCE_BINDINGS);
|
||||||
script_instance = NULL;
|
script_instance = NULL;
|
||||||
#ifdef TOOLS_ENABLED
|
#ifdef TOOLS_ENABLED
|
||||||
|
|
|
@ -490,10 +490,12 @@ private:
|
||||||
void _set_indexed_bind(const NodePath &p_name, const Variant &p_value);
|
void _set_indexed_bind(const NodePath &p_name, const Variant &p_value);
|
||||||
Variant _get_indexed_bind(const NodePath &p_name) const;
|
Variant _get_indexed_bind(const NodePath &p_name) const;
|
||||||
|
|
||||||
void *_script_instance_bindings[MAX_SCRIPT_INSTANCE_BINDINGS];
|
|
||||||
|
|
||||||
void property_list_changed_notify();
|
void property_list_changed_notify();
|
||||||
|
|
||||||
|
friend class Reference;
|
||||||
|
uint32_t instance_binding_count;
|
||||||
|
void *_script_instance_bindings[MAX_SCRIPT_INSTANCE_BINDINGS];
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
virtual void _initialize_classv() { initialize_class(); }
|
virtual void _initialize_classv() { initialize_class(); }
|
||||||
virtual bool _setv(const StringName &p_name, const Variant &p_property) { return false; };
|
virtual bool _setv(const StringName &p_name, const Variant &p_property) { return false; };
|
||||||
|
|
|
@ -66,8 +66,17 @@ int Reference::reference_get_count() const {
|
||||||
bool Reference::reference() {
|
bool Reference::reference() {
|
||||||
bool success = refcount.ref();
|
bool success = refcount.ref();
|
||||||
|
|
||||||
if (success && get_script_instance()) {
|
if (success && refcount.get() <= 2 /* higher is not relevant */) {
|
||||||
get_script_instance()->refcount_incremented();
|
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;
|
return success;
|
||||||
|
@ -77,9 +86,19 @@ bool Reference::unreference() {
|
||||||
|
|
||||||
bool die = refcount.unref();
|
bool die = refcount.unref();
|
||||||
|
|
||||||
if (get_script_instance()) {
|
if (refcount.get() <= 1 /* higher is not relevant */) {
|
||||||
bool script_ret = get_script_instance()->refcount_decremented();
|
if (get_script_instance()) {
|
||||||
die = die && script_ret;
|
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;
|
return die;
|
||||||
|
|
|
@ -311,6 +311,8 @@ public:
|
||||||
|
|
||||||
virtual void *alloc_instance_binding_data(Object *p_object) { return NULL; } //optional, not used by all languages
|
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 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();
|
virtual void frame();
|
||||||
|
|
||||||
|
|
|
@ -1013,6 +1013,69 @@ void CSharpLanguage::free_instance_binding_data(void *p_data) {
|
||||||
#endif
|
#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 *CSharpInstance::create_for_managed_type(Object *p_owner, CSharpScript *p_script, const Ref<MonoGCHandle> &p_gchandle) {
|
||||||
|
|
||||||
CSharpInstance *instance = memnew(CSharpInstance);
|
CSharpInstance *instance = memnew(CSharpInstance);
|
||||||
|
@ -1303,11 +1366,13 @@ void CSharpInstance::mono_object_disposed() {
|
||||||
|
|
||||||
void CSharpInstance::refcount_incremented() {
|
void CSharpInstance::refcount_incremented() {
|
||||||
|
|
||||||
|
#ifdef DEBUG_ENABLED
|
||||||
CRASH_COND(!base_ref);
|
CRASH_COND(!base_ref);
|
||||||
|
#endif
|
||||||
|
|
||||||
Reference *ref_owner = Object::cast_to<Reference>(owner);
|
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.
|
// 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,
|
// 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.
|
// 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.
|
// Release the current weak handle and replace it with a strong handle.
|
||||||
uint32_t strong_gchandle = MonoGCHandle::make_strong_handle(gchandle->get_target());
|
uint32_t strong_gchandle = MonoGCHandle::make_strong_handle(gchandle->get_target());
|
||||||
gchandle->release();
|
gchandle->release();
|
||||||
gchandle->set_handle(strong_gchandle);
|
gchandle->set_handle(strong_gchandle, MonoGCHandle::STRONG_HANDLE);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
bool CSharpInstance::refcount_decremented() {
|
bool CSharpInstance::refcount_decremented() {
|
||||||
|
|
||||||
|
#ifdef DEBUG_ENABLED
|
||||||
CRASH_COND(!base_ref);
|
CRASH_COND(!base_ref);
|
||||||
|
#endif
|
||||||
|
|
||||||
Reference *ref_owner = Object::cast_to<Reference>(owner);
|
Reference *ref_owner = Object::cast_to<Reference>(owner);
|
||||||
|
|
||||||
int refcount = ref_owner->reference_get_count();
|
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,
|
// If owner owner is no longer referenced by the unmanaged side,
|
||||||
// the managed instance takes responsibility of deleting the owner when GCed.
|
// the managed instance takes responsibility of deleting the owner when GCed.
|
||||||
|
|
||||||
// Release the current strong handle and replace it with a weak handle.
|
// Release the current strong handle and replace it with a weak handle.
|
||||||
uint32_t weak_gchandle = MonoGCHandle::make_weak_handle(gchandle->get_target());
|
uint32_t weak_gchandle = MonoGCHandle::make_weak_handle(gchandle->get_target());
|
||||||
gchandle->release();
|
gchandle->release();
|
||||||
gchandle->set_handle(weak_gchandle);
|
gchandle->set_handle(weak_gchandle, MonoGCHandle::WEAK_HANDLE);
|
||||||
|
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
|
@ -346,6 +346,8 @@ public:
|
||||||
// Don't use these. I'm watching you
|
// Don't use these. I'm watching you
|
||||||
virtual void *alloc_instance_binding_data(Object *p_object);
|
virtual void *alloc_instance_binding_data(Object *p_object);
|
||||||
virtual void free_instance_binding_data(void *p_data);
|
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
|
#ifdef DEBUG_ENABLED
|
||||||
Vector<StackInfo> stack_trace_get_info(MonoObject *p_stack_trace);
|
Vector<StackInfo> stack_trace_get_info(MonoObject *p_stack_trace);
|
||||||
|
|
|
@ -44,12 +44,12 @@ uint32_t MonoGCHandle::make_weak_handle(MonoObject *p_object) {
|
||||||
|
|
||||||
Ref<MonoGCHandle> MonoGCHandle::create_strong(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) {
|
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() {
|
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;
|
released = false;
|
||||||
|
weak = p_handle_type == WEAK_HANDLE;
|
||||||
handle = p_handle;
|
handle = p_handle;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -40,24 +40,33 @@ class MonoGCHandle : public Reference {
|
||||||
GDCLASS(MonoGCHandle, Reference)
|
GDCLASS(MonoGCHandle, Reference)
|
||||||
|
|
||||||
bool released;
|
bool released;
|
||||||
|
bool weak;
|
||||||
uint32_t handle;
|
uint32_t handle;
|
||||||
|
|
||||||
public:
|
public:
|
||||||
|
enum HandleType {
|
||||||
|
STRONG_HANDLE,
|
||||||
|
WEAK_HANDLE
|
||||||
|
};
|
||||||
|
|
||||||
static uint32_t make_strong_handle(MonoObject *p_object);
|
static uint32_t make_strong_handle(MonoObject *p_object);
|
||||||
static uint32_t make_weak_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_strong(MonoObject *p_object);
|
||||||
static Ref<MonoGCHandle> create_weak(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_ MonoObject *get_target() const { return released ? NULL : mono_gchandle_get_target(handle); }
|
||||||
|
|
||||||
_FORCE_INLINE_ void set_handle(uint32_t p_handle) {
|
_FORCE_INLINE_ void set_handle(uint32_t p_handle, HandleType p_handle_type) {
|
||||||
handle = p_handle;
|
|
||||||
released = false;
|
released = false;
|
||||||
|
weak = p_handle_type == WEAK_HANDLE;
|
||||||
|
handle = p_handle;
|
||||||
}
|
}
|
||||||
void release();
|
void release();
|
||||||
|
|
||||||
MonoGCHandle(uint32_t p_handle);
|
MonoGCHandle(uint32_t p_handle, HandleType p_handle_type);
|
||||||
~MonoGCHandle();
|
~MonoGCHandle();
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
@ -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_source, ERR_INVALID_DATA);
|
||||||
ERR_FAIL_NULL_V(p_target, 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(p_awaiter));
|
||||||
Ref<SignalAwaiterHandle> sa_con = memnew(SignalAwaiterHandle(awaiter_handle));
|
|
||||||
#ifdef DEBUG_ENABLED
|
#ifdef DEBUG_ENABLED
|
||||||
sa_con->set_connection_target(p_target);
|
sa_con->set_connection_target(p_target);
|
||||||
#endif
|
#endif
|
||||||
|
@ -119,8 +118,8 @@ void SignalAwaiterHandle::_bind_methods() {
|
||||||
ClassDB::bind_vararg_method(METHOD_FLAGS_DEFAULT, "_signal_callback", &SignalAwaiterHandle::_signal_callback, MethodInfo("_signal_callback"));
|
ClassDB::bind_vararg_method(METHOD_FLAGS_DEFAULT, "_signal_callback", &SignalAwaiterHandle::_signal_callback, MethodInfo("_signal_callback"));
|
||||||
}
|
}
|
||||||
|
|
||||||
SignalAwaiterHandle::SignalAwaiterHandle(uint32_t p_managed_handle) :
|
SignalAwaiterHandle::SignalAwaiterHandle(MonoObject *p_managed) :
|
||||||
MonoGCHandle(p_managed_handle) {
|
MonoGCHandle(MonoGCHandle::make_strong_handle(p_managed), STRONG_HANDLE) {
|
||||||
|
|
||||||
#ifdef DEBUG_ENABLED
|
#ifdef DEBUG_ENABLED
|
||||||
conn_target_id = 0;
|
conn_target_id = 0;
|
||||||
|
|
|
@ -64,7 +64,7 @@ public:
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
SignalAwaiterHandle(uint32_t p_managed_handle);
|
SignalAwaiterHandle(MonoObject *p_managed);
|
||||||
~SignalAwaiterHandle();
|
~SignalAwaiterHandle();
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
Loading…
Reference in a new issue