diff options
Diffstat (limited to 'modules/mono/mono_gd/gd_mono.cpp')
-rw-r--r-- | modules/mono/mono_gd/gd_mono.cpp | 170 |
1 files changed, 97 insertions, 73 deletions
diff --git a/modules/mono/mono_gd/gd_mono.cpp b/modules/mono/mono_gd/gd_mono.cpp index fadac941e9..96d3f34039 100644 --- a/modules/mono/mono_gd/gd_mono.cpp +++ b/modules/mono/mono_gd/gd_mono.cpp @@ -42,6 +42,7 @@ #include "project_settings.h" #include "../csharp_script.h" +#include "../glue/cs_glue_version.gen.h" #include "../godotsharp_dirs.h" #include "../utils/path_utils.h" #include "gd_mono_class.h" @@ -177,6 +178,30 @@ void GDMono::initialize() { mono_set_dirs(assembly_dir.length() ? assembly_dir.get_data() : NULL, config_dir.length() ? config_dir.get_data() : NULL); +#elif OSX_ENABLED + mono_set_dirs(NULL, NULL); + + { + const char *assembly_rootdir = mono_assembly_getrootdir(); + const char *config_dir = mono_get_config_dir(); + + if (!assembly_rootdir || !config_dir || !DirAccess::exists(assembly_rootdir) || !DirAccess::exists(config_dir)) { + Vector<const char *> locations; + locations.push_back("/Library/Frameworks/Mono.framework/Versions/Current/"); + locations.push_back("/usr/local/var/homebrew/linked/mono/"); + + for (int i = 0; i < locations.size(); i++) { + String hint_assembly_rootdir = path_join(locations[i], "lib"); + String hint_mscorlib_path = path_join(hint_assembly_rootdir, "mono", "4.5", "mscorlib.dll"); + String hint_config_dir = path_join(locations[i], "etc"); + + if (FileAccess::exists(hint_mscorlib_path) && DirAccess::exists(hint_config_dir)) { + mono_set_dirs(hint_assembly_rootdir.utf8().get_data(), hint_config_dir.utf8().get_data()); + break; + } + } + } + } #else mono_set_dirs(NULL, NULL); #endif @@ -232,7 +257,7 @@ void GDMono::initialize() { _register_internal_calls(); // The following assemblies are not required at initialization -#ifndef MONO_GLUE_DISABLED +#ifdef MONO_GLUE_ENABLED if (_load_api_assemblies()) { if (!core_api_assembly_out_of_sync && !editor_api_assembly_out_of_sync && GDMonoUtils::mono_cache.godot_api_cache_updated) { // Everything is fine with the api assemblies, load the project assembly @@ -275,7 +300,7 @@ void GDMono::initialize() { print_verbose("Mono: INITIALIZED"); } -#ifndef MONO_GLUE_DISABLED +#ifdef MONO_GLUE_ENABLED namespace GodotSharpBindings { uint64_t get_core_api_hash(); @@ -283,14 +308,13 @@ uint64_t get_core_api_hash(); uint64_t get_editor_api_hash(); #endif // TOOLS_ENABLED uint32_t get_bindings_version(); -uint32_t get_cs_glue_version(); void register_generated_icalls(); } // namespace GodotSharpBindings #endif void GDMono::_register_internal_calls() { -#ifndef MONO_GLUE_DISABLED +#ifdef MONO_GLUE_ENABLED GodotSharpBindings::register_generated_icalls(); #endif @@ -304,7 +328,7 @@ void GDMono::_initialize_and_check_api_hashes() { api_core_hash = ClassDB::get_api_hash(ClassDB::API_CORE); -#ifndef MONO_GLUE_DISABLED +#ifdef MONO_GLUE_ENABLED if (api_core_hash != GodotSharpBindings::get_core_api_hash()) { ERR_PRINT("Mono: Core API hash mismatch!"); } @@ -313,7 +337,7 @@ void GDMono::_initialize_and_check_api_hashes() { #ifdef TOOLS_ENABLED api_editor_hash = ClassDB::get_api_hash(ClassDB::API_EDITOR); -#ifndef MONO_GLUE_DISABLED +#ifdef MONO_GLUE_ENABLED if (api_editor_hash != GodotSharpBindings::get_editor_api_hash()) { ERR_PRINT("Mono: Editor API hash mismatch!"); } @@ -431,11 +455,11 @@ bool GDMono::_load_core_api_assembly() { bool success = load_assembly(API_ASSEMBLY_NAME, &core_api_assembly); if (success) { -#ifndef MONO_GLUE_DISABLED +#ifdef MONO_GLUE_ENABLED APIAssembly::Version api_assembly_ver = APIAssembly::Version::get_from_loaded_assembly(core_api_assembly, APIAssembly::API_CORE); core_api_assembly_out_of_sync = GodotSharpBindings::get_core_api_hash() != api_assembly_ver.godot_api_hash || GodotSharpBindings::get_bindings_version() != api_assembly_ver.bindings_version || - GodotSharpBindings::get_cs_glue_version() != api_assembly_ver.cs_glue_version; + CS_GLUE_VERSION != api_assembly_ver.cs_glue_version; #endif GDMonoUtils::update_godot_api_cache(); } @@ -457,11 +481,11 @@ bool GDMono::_load_editor_api_assembly() { bool success = load_assembly(EDITOR_API_ASSEMBLY_NAME, &editor_api_assembly); if (success) { -#ifndef MONO_GLUE_DISABLED +#ifdef MONO_GLUE_ENABLED APIAssembly::Version api_assembly_ver = APIAssembly::Version::get_from_loaded_assembly(editor_api_assembly, APIAssembly::API_EDITOR); editor_api_assembly_out_of_sync = GodotSharpBindings::get_editor_api_hash() != api_assembly_ver.godot_api_hash || GodotSharpBindings::get_bindings_version() != api_assembly_ver.bindings_version || - GodotSharpBindings::get_cs_glue_version() != api_assembly_ver.cs_glue_version; + CS_GLUE_VERSION != api_assembly_ver.cs_glue_version; #endif } @@ -615,9 +639,7 @@ Error GDMono::_unload_scripts_domain() { mono_gc_collect(mono_gc_max_generation()); - finalizing_scripts_domain = true; mono_domain_finalize(scripts_domain, 2000); - finalizing_scripts_domain = false; mono_gc_collect(mono_gc_max_generation()); @@ -684,7 +706,7 @@ Error GDMono::reload_scripts_domain() { return err; } -#ifndef MONO_GLUE_DISABLED +#ifdef MONO_GLUE_ENABLED if (!_load_api_assemblies()) { return ERR_CANT_OPEN; } @@ -698,13 +720,15 @@ Error GDMono::reload_scripts_domain() { // so we invalidate the version in the metadata and unload the script domain. if (core_api_assembly_out_of_sync) { + ERR_PRINT("The loaded Core API assembly is out of sync"); metadata_set_api_assembly_invalidated(APIAssembly::API_CORE, true); } else if (!GDMonoUtils::mono_cache.godot_api_cache_updated) { - ERR_PRINT("Core API assembly is in sync, but the cache update failed"); + ERR_PRINT("The loaded Core API assembly is in sync, but the cache update failed"); metadata_set_api_assembly_invalidated(APIAssembly::API_CORE, true); } if (editor_api_assembly_out_of_sync) { + ERR_PRINT("The loaded Editor API assembly is out of sync"); metadata_set_api_assembly_invalidated(APIAssembly::API_EDITOR, true); } @@ -813,7 +837,6 @@ GDMono::GDMono() { gdmono_log = memnew(GDMonoLog); runtime_initialized = false; - finalizing_scripts_domain = false; root_domain = NULL; scripts_domain = NULL; @@ -842,7 +865,7 @@ GDMono::GDMono() { GDMono::~GDMono() { - if (runtime_initialized) { + if (is_runtime_initialized()) { if (scripts_domain) { @@ -867,8 +890,9 @@ GDMono::~GDMono() { print_verbose("Mono: Runtime cleanup..."); - runtime_initialized = false; mono_jit_cleanup(root_domain); + + runtime_initialized = false; } if (gdmono_log) @@ -879,33 +903,12 @@ GDMono::~GDMono() { _GodotSharp *_GodotSharp::singleton = NULL; -void _GodotSharp::_dispose_object(Object *p_object) { - - if (p_object->get_script_instance()) { - CSharpInstance *cs_instance = CAST_CSHARP_INSTANCE(p_object->get_script_instance()); - if (cs_instance) { - cs_instance->mono_object_disposed(); - return; - } - } - - // Unsafe refcount decrement. The managed instance also counts as a reference. - // See: CSharpLanguage::alloc_instance_binding_data(Object *p_object) - if (Object::cast_to<Reference>(p_object)->unreference()) { - memdelete(p_object); - } -} - void _GodotSharp::_dispose_callback() { #ifndef NO_THREADS queue_mutex->lock(); #endif - for (List<Object *>::Element *E = obj_delete_queue.front(); E; E = E->next()) { - _dispose_object(E->get()); - } - for (List<NodePath *>::Element *E = np_delete_queue.front(); E; E = E->next()) { memdelete(E->get()); } @@ -914,7 +917,6 @@ void _GodotSharp::_dispose_callback() { memdelete(E->get()); } - obj_delete_queue.clear(); np_delete_queue.clear(); rid_delete_queue.clear(); queue_empty = true; @@ -934,52 +936,69 @@ void _GodotSharp::detach_thread() { GDMonoUtils::detach_current_thread(); } -bool _GodotSharp::is_finalizing_domain() { +int32_t _GodotSharp::get_domain_id() { - return GDMono::get_singleton()->is_finalizing_scripts_domain(); + MonoDomain *domain = mono_domain_get(); + CRASH_COND(!domain); // User must check if runtime is initialized before calling this method + return mono_domain_get_id(domain); } -bool _GodotSharp::is_domain_loaded() { +int32_t _GodotSharp::get_scripts_domain_id() { - return GDMono::get_singleton()->get_scripts_domain() != NULL; + MonoDomain *domain = SCRIPTS_DOMAIN; + CRASH_COND(!domain); // User must check if scripts domain is loaded before calling this method + return mono_domain_get_id(domain); } -#define ENQUEUE_FOR_DISPOSAL(m_queue, m_inst) \ - m_queue.push_back(m_inst); \ - if (queue_empty) { \ - queue_empty = false; \ - if (!is_finalizing_domain()) { /* call_deferred may not be safe here */ \ - call_deferred("_dispose_callback"); \ - } \ - } +bool _GodotSharp::is_scripts_domain_loaded() { -void _GodotSharp::queue_dispose(MonoObject *p_mono_object, Object *p_object) { + return GDMono::get_singleton()->is_runtime_initialized() && SCRIPTS_DOMAIN != NULL; +} - if (GDMonoUtils::is_main_thread() && !GDMono::get_singleton()->is_finalizing_scripts_domain()) { - _dispose_object(p_object); - } else { -#ifndef NO_THREADS - queue_mutex->lock(); -#endif +bool _GodotSharp::_is_domain_finalizing_for_unload(int32_t p_domain_id) { - // This is our last chance to invoke notification predelete (this is being called from the finalizer) - // We must use the MonoObject* passed by the finalizer, because the weak GC handle target returns NULL at this point - CSharpInstance *si = CAST_CSHARP_INSTANCE(p_object->get_script_instance()); - if (si) { - si->call_notification_no_check(p_mono_object, Object::NOTIFICATION_PREDELETE); - } + return is_domain_finalizing_for_unload(p_domain_id); +} - ENQUEUE_FOR_DISPOSAL(obj_delete_queue, p_object); +bool _GodotSharp::is_domain_finalizing_for_unload() { -#ifndef NO_THREADS - queue_mutex->unlock(); -#endif - } + return is_domain_finalizing_for_unload(mono_domain_get()); +} + +bool _GodotSharp::is_domain_finalizing_for_unload(int32_t p_domain_id) { + + return is_domain_finalizing_for_unload(mono_domain_get_by_id(p_domain_id)); +} + +bool _GodotSharp::is_domain_finalizing_for_unload(MonoDomain *p_domain) { + + if (!p_domain) + return true; + return mono_domain_is_unloading(p_domain); +} + +bool _GodotSharp::is_runtime_shutting_down() { + + return mono_runtime_is_shutting_down(); } +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() && !GDMono::get_singleton()->is_finalizing_scripts_domain()) { + if (GDMonoUtils::is_main_thread() && !is_domain_finalizing_for_unload(SCRIPTS_DOMAIN)) { memdelete(p_node_path); } else { #ifndef NO_THREADS @@ -996,7 +1015,7 @@ void _GodotSharp::queue_dispose(NodePath *p_node_path) { void _GodotSharp::queue_dispose(RID *p_rid) { - if (GDMonoUtils::is_main_thread() && !GDMono::get_singleton()->is_finalizing_scripts_domain()) { + if (GDMonoUtils::is_main_thread() && !is_domain_finalizing_for_unload(SCRIPTS_DOMAIN)) { memdelete(p_rid); } else { #ifndef NO_THREADS @@ -1016,8 +1035,13 @@ void _GodotSharp::_bind_methods() { ClassDB::bind_method(D_METHOD("attach_thread"), &_GodotSharp::attach_thread); ClassDB::bind_method(D_METHOD("detach_thread"), &_GodotSharp::detach_thread); - ClassDB::bind_method(D_METHOD("is_finalizing_domain"), &_GodotSharp::is_finalizing_domain); - ClassDB::bind_method(D_METHOD("is_domain_loaded"), &_GodotSharp::is_domain_loaded); + ClassDB::bind_method(D_METHOD("get_domain_id"), &_GodotSharp::get_domain_id); + ClassDB::bind_method(D_METHOD("get_scripts_domain_id"), &_GodotSharp::get_scripts_domain_id); + ClassDB::bind_method(D_METHOD("is_scripts_domain_loaded"), &_GodotSharp::is_scripts_domain_loaded); + ClassDB::bind_method(D_METHOD("is_domain_finalizing_for_unload", "domain_id"), &_GodotSharp::_is_domain_finalizing_for_unload); + + 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); } |