Mono: Fix crash on NodePath/RID disposal during Godot shutdown

This commit is contained in:
Ignacio Etcheverry 2018-10-06 23:00:18 +02:00
parent a41cf404a9
commit b63e518ce9
4 changed files with 17 additions and 81 deletions

View file

@ -40,7 +40,7 @@ NodePath *godot_icall_NodePath_Ctor(MonoString *p_path) {
void godot_icall_NodePath_Dtor(NodePath *p_ptr) {
ERR_FAIL_NULL(p_ptr);
_GodotSharp::get_singleton()->queue_dispose(p_ptr);
memdelete(p_ptr);
}
MonoString *godot_icall_NodePath_operator_String(NodePath *p_np) {

View file

@ -45,7 +45,7 @@ RID *godot_icall_RID_Ctor(Object *p_from) {
void godot_icall_RID_Dtor(RID *p_ptr) {
ERR_FAIL_NULL(p_ptr);
_GodotSharp::get_singleton()->queue_dispose(p_ptr);
memdelete(p_ptr);
}
uint32_t godot_icall_RID_get_id(RID *p_ptr) {

View file

@ -671,14 +671,18 @@ Error GDMono::_unload_scripts_domain() {
print_verbose("Mono: Unloading scripts domain...");
_GodotSharp::get_singleton()->_dispose_callback();
if (mono_domain_get() != root_domain)
mono_domain_set(root_domain, true);
mono_gc_collect(mono_gc_max_generation());
mono_domain_finalize(scripts_domain, 2000);
finalizing_scripts_domain = true;
if (!mono_domain_finalize(scripts_domain, 2000)) {
ERR_PRINT("Mono: Domain finalization timeout");
}
finalizing_scripts_domain = false;
mono_gc_collect(mono_gc_max_generation());
@ -696,8 +700,6 @@ Error GDMono::_unload_scripts_domain() {
MonoDomain *domain = scripts_domain;
scripts_domain = NULL;
_GodotSharp::get_singleton()->_dispose_callback();
MonoException *exc = NULL;
mono_domain_try_unload(domain, (MonoObject **)&exc);
@ -800,7 +802,9 @@ Error GDMono::finalize_and_unload_domain(MonoDomain *p_domain) {
mono_domain_set(root_domain, true);
mono_gc_collect(mono_gc_max_generation());
mono_domain_finalize(p_domain, 2000);
if (!mono_domain_finalize(p_domain, 2000)) {
ERR_PRINT("Mono: Domain finalization timeout");
}
mono_gc_collect(mono_gc_max_generation());
_domain_assemblies_cleanup(mono_domain_get_id(p_domain));
@ -875,6 +879,7 @@ GDMono::GDMono() {
gdmono_log = memnew(GDMonoLog);
runtime_initialized = false;
finalizing_scripts_domain = false;
root_domain = NULL;
scripts_domain = NULL;
@ -941,29 +946,6 @@ GDMono::~GDMono() {
_GodotSharp *_GodotSharp::singleton = NULL;
void _GodotSharp::_dispose_callback() {
#ifndef NO_THREADS
queue_mutex->lock();
#endif
for (List<NodePath *>::Element *E = np_delete_queue.front(); E; E = E->next()) {
memdelete(E->get());
}
for (List<RID *>::Element *E = rid_delete_queue.front(); E; E = E->next()) {
memdelete(E->get());
}
np_delete_queue.clear();
rid_delete_queue.clear();
queue_empty = true;
#ifndef NO_THREADS
queue_mutex->unlock();
#endif
}
void _GodotSharp::attach_thread() {
GDMonoUtils::attach_current_thread();
@ -1012,6 +994,8 @@ bool _GodotSharp::is_domain_finalizing_for_unload(MonoDomain *p_domain) {
if (!p_domain)
return true;
if (p_domain == SCRIPTS_DOMAIN && GDMono::get_singleton()->is_finalizing_scripts_domain())
return true;
return mono_domain_is_unloading(p_domain);
}
@ -1025,49 +1009,6 @@ bool _GodotSharp::is_runtime_initialized() {
return GDMono::get_singleton()->is_runtime_initialized();
}
#define ENQUEUE_FOR_DISPOSAL(m_queue, m_inst) \
m_queue.push_back(m_inst); \
if (queue_empty) { \
queue_empty = false; \
if (!is_domain_finalizing_for_unload(SCRIPTS_DOMAIN)) { /* call_deferred may not be safe here */ \
call_deferred("_dispose_callback"); \
} \
}
void _GodotSharp::queue_dispose(NodePath *p_node_path) {
if (GDMonoUtils::is_main_thread() && !is_domain_finalizing_for_unload(SCRIPTS_DOMAIN)) {
memdelete(p_node_path);
} else {
#ifndef NO_THREADS
queue_mutex->lock();
#endif
ENQUEUE_FOR_DISPOSAL(np_delete_queue, p_node_path);
#ifndef NO_THREADS
queue_mutex->unlock();
#endif
}
}
void _GodotSharp::queue_dispose(RID *p_rid) {
if (GDMonoUtils::is_main_thread() && !is_domain_finalizing_for_unload(SCRIPTS_DOMAIN)) {
memdelete(p_rid);
} else {
#ifndef NO_THREADS
queue_mutex->lock();
#endif
ENQUEUE_FOR_DISPOSAL(rid_delete_queue, p_rid);
#ifndef NO_THREADS
queue_mutex->unlock();
#endif
}
}
void _GodotSharp::_bind_methods() {
ClassDB::bind_method(D_METHOD("attach_thread"), &_GodotSharp::attach_thread);
@ -1080,8 +1021,6 @@ void _GodotSharp::_bind_methods() {
ClassDB::bind_method(D_METHOD("is_runtime_shutting_down"), &_GodotSharp::is_runtime_shutting_down);
ClassDB::bind_method(D_METHOD("is_runtime_initialized"), &_GodotSharp::is_runtime_initialized);
ClassDB::bind_method(D_METHOD("_dispose_callback"), &_GodotSharp::_dispose_callback);
}
_GodotSharp::_GodotSharp() {

View file

@ -172,6 +172,8 @@ public:
_FORCE_INLINE_ bool is_runtime_initialized() const { return runtime_initialized && !mono_runtime_is_shutting_down() /* stays true after shutdown finished */; }
_FORCE_INLINE_ bool is_finalizing_scripts_domain() { return finalizing_scripts_domain; }
_FORCE_INLINE_ MonoDomain *get_scripts_domain() { return scripts_domain; }
#ifdef TOOLS_ENABLED
_FORCE_INLINE_ MonoDomain *get_tools_domain() { return tools_domain; }
@ -260,8 +262,6 @@ class _GodotSharp : public Object {
friend class GDMono;
void _dispose_callback();
bool _is_domain_finalizing_for_unload(int32_t p_domain_id);
List<NodePath *> np_delete_queue;
@ -295,9 +295,6 @@ public:
bool is_runtime_shutting_down();
bool is_runtime_initialized();
void queue_dispose(NodePath *p_node_path);
void queue_dispose(RID *p_rid);
_GodotSharp();
~_GodotSharp();
};