Fix/workaround for issue #21667

When a Reference managed instance is garbage collected and its finalizer is called, it could happen that the native instance is referenced once again before the finalizer can unreference and memdelete it. The workaround is to create a new managed instance when this happens (at least for now).
This commit is contained in:
Ignacio Etcheverry 2018-09-12 02:41:54 +02:00
parent 61426464ea
commit e558e1ec09
15 changed files with 512 additions and 196 deletions

View file

@ -48,6 +48,8 @@ class CSharpLanguage;
#ifdef NO_SAFE_CAST
template <typename TScriptInstance, typename TScriptLanguage>
TScriptInstance *cast_script_instance(ScriptInstance *p_inst) {
if (!p_inst)
return NULL;
return p_inst->get_language() == TScriptLanguage::get_singleton() ? static_cast<TScriptInstance *>(p_inst) : NULL;
}
#else
@ -177,14 +179,19 @@ class CSharpInstance : public ScriptInstance {
friend class CSharpScript;
friend class CSharpLanguage;
Object *owner;
Ref<CSharpScript> script;
Ref<MonoGCHandle> gchandle;
bool base_ref;
bool ref_dying;
bool unsafe_referenced;
void _reference_owner_unsafe();
void _unreference_owner_unsafe();
Ref<CSharpScript> script;
Ref<MonoGCHandle> gchandle;
bool _reference_owner_unsafe();
bool _unreference_owner_unsafe();
MonoObject *_internal_new_managed();
// Do not use unless you know what you are doing
friend void GDMonoInternals::tie_managed_to_unmanaged(MonoObject *, Object *);
@ -208,7 +215,8 @@ public:
virtual void call_multilevel(const StringName &p_method, const Variant **p_args, int p_argcount);
virtual void call_multilevel_reversed(const StringName &p_method, const Variant **p_args, int p_argcount);
void mono_object_disposed();
bool mono_object_disposed(MonoObject *p_obj);
bool mono_object_disposed_baseref(MonoObject *p_obj, bool p_is_finalizer, bool &r_owner_deleted);
virtual void refcount_incremented();
virtual bool refcount_decremented();
@ -217,7 +225,7 @@ public:
virtual MultiplayerAPI::RPCMode get_rset_mode(const StringName &p_variable) const;
virtual void notification(int p_notification);
void call_notification_no_check(MonoObject *p_mono_object, int p_notification);
void _call_notification(int p_notification);
virtual Ref<Script> get_script() const;
@ -227,6 +235,12 @@ public:
~CSharpInstance();
};
struct CSharpScriptBinding {
StringName type_name;
GDMonoClass *wrapper_class;
Ref<MonoGCHandle> gchandle;
};
class CSharpLanguage : public ScriptLanguage {
friend class CSharpScript;
@ -241,10 +255,11 @@ class CSharpLanguage : public ScriptLanguage {
Mutex *lock;
Mutex *script_bind_lock;
Mutex *script_gchandle_release_lock;
Map<Ref<CSharpScript>, Map<ObjectID, List<Pair<StringName, Variant> > > > to_reload;
Map<Object *, Ref<MonoGCHandle> > gchandle_bindings;
Map<Object *, CSharpScriptBinding> script_bindings;
struct StringNameCache {
@ -270,6 +285,9 @@ public:
_FORCE_INLINE_ static CSharpLanguage *get_singleton() { return singleton; }
static void release_script_gchandle(Ref<MonoGCHandle> &p_gchandle);
static void release_script_gchandle(MonoObject *p_pinned_expected_obj, Ref<MonoGCHandle> &p_gchandle);
bool debug_break(const String &p_error, bool p_allow_continue = true);
bool debug_break_parse(const String &p_file, int p_line, const String &p_error);