From 4739cb8c0003a7c35220ce43d8263df617c6fbe0 Mon Sep 17 00:00:00 2001 From: Ignacio Etcheverry Date: Tue, 26 Jun 2018 21:03:42 +0200 Subject: [PATCH] Mono: Pending exceptions and cleanup --- modules/mono/SCsub | 16 +- modules/mono/config.py | 111 ++++++++---- modules/mono/csharp_script.cpp | 44 ++--- modules/mono/editor/csharp_project.cpp | 32 ++-- modules/mono/editor/godotsharp_builds.cpp | 20 +-- modules/mono/editor/monodevelop_instance.cpp | 16 +- modules/mono/mono_gc_handle.cpp | 7 +- modules/mono/mono_gd/gd_mono.cpp | 47 ++--- modules/mono/mono_gd/gd_mono.h | 2 + modules/mono/mono_gd/gd_mono_internals.cpp | 11 ++ modules/mono/mono_gd/gd_mono_internals.h | 11 +- modules/mono/mono_gd/gd_mono_marshal.cpp | 20 ++- modules/mono/mono_gd/gd_mono_method.cpp | 30 ++-- modules/mono/mono_gd/gd_mono_method.h | 6 +- modules/mono/mono_gd/gd_mono_property.cpp | 30 ++-- modules/mono/mono_gd/gd_mono_property.h | 6 +- modules/mono/mono_gd/gd_mono_utils.cpp | 86 +++++++--- modules/mono/mono_gd/gd_mono_utils.h | 33 +++- modules/mono/signal_awaiter_utils.cpp | 20 ++- modules/mono/tls_configure.py | 36 ++++ modules/mono/utils/macros.h | 59 +++++++ modules/mono/utils/thread_local.cpp | 99 +++++++++++ modules/mono/utils/thread_local.h | 171 +++++++++++++++++++ 23 files changed, 720 insertions(+), 193 deletions(-) create mode 100644 modules/mono/tls_configure.py create mode 100644 modules/mono/utils/macros.h create mode 100644 modules/mono/utils/thread_local.cpp create mode 100644 modules/mono/utils/thread_local.h diff --git a/modules/mono/SCsub b/modules/mono/SCsub index 03e187e5b08..89a3caf4e46 100644 --- a/modules/mono/SCsub +++ b/modules/mono/SCsub @@ -5,9 +5,11 @@ Import('env_modules') env_mono = env_modules.Clone() -from compat import byte_to_str +# TODO move functions to their own modules def make_cs_files_header(src, dst): + from compat import byte_to_str + with open(dst, 'w') as header: header.write('/* This is an automatically generated file; DO NOT EDIT! OK THX */\n') header.write('#ifndef _CS_FILES_DATA_H\n') @@ -75,6 +77,13 @@ else: if ARGUMENTS.get('yolo_copy', False): env_mono.Append(CPPDEFINES=['YOLO_COPY']) +# Configure TLS checks + +import tls_configure +conf = Configure(env_mono) +tls_configure.configure(conf) +env_mono = conf.Finish() + # Build GodotSharpTools solution @@ -128,12 +137,13 @@ def find_msbuild_windows(): raise RuntimeError('Cannot find mono root directory') framework_path = os.path.join(mono_root, 'lib', 'mono', '4.5') - mono_bin_dir = os.path.join(mono_root, 'bin') - msbuild_mono = os.path.join(mono_bin_dir, 'msbuild.bat') if os.path.isfile(msbuild_mono): + # The (Csc/Vbc/Fsc)ToolExe environment variables are required when + # building with Mono's MSBuild. They must point to the batch files + # in Mono's bin directory to make sure they are executed with Mono. mono_msbuild_env = { 'CscToolExe': os.path.join(mono_bin_dir, 'csc.bat'), 'VbcToolExe': os.path.join(mono_bin_dir, 'vbc.bat'), diff --git a/modules/mono/config.py b/modules/mono/config.py index ebf8512fb64..82a8234abb5 100644 --- a/modules/mono/config.py +++ b/modules/mono/config.py @@ -4,12 +4,36 @@ import os import sys import subprocess +from distutils.version import LooseVersion from SCons.Script import BoolVariable, Dir, Environment, File, PathVariable, SCons, Variables monoreg = imp.load_source('mono_reg_utils', 'modules/mono/mono_reg_utils.py') +def can_build(env, platform): + if platform in ['javascript']: + return False # Not yet supported + return True + + +def is_enabled(): + # The module is disabled by default. Use module_mono_enabled=yes to enable it. + return False + + +def get_doc_classes(): + return [ + '@C#', + 'CSharpScript', + 'GodotSharp', + ] + + +def get_doc_path(): + return 'doc_classes' + + def find_file_in_dir(directory, files, prefix='', extension=''): if not extension.startswith('.'): extension = '.' + extension @@ -19,17 +43,6 @@ def find_file_in_dir(directory, files, prefix='', extension=''): return '' -def can_build(env, platform): - if platform in ["javascript"]: - return False # Not yet supported - return True - - -def is_enabled(): - # The module is disabled by default. Use module_mono_enabled=yes to enable it. - return False - - def copy_file(src_dir, dst_dir, name): from shutil import copyfile @@ -55,7 +68,7 @@ def custom_path_is_dir_create(key, val, env): def configure(env): env.use_ptrcall = True - env.add_module_version_string("mono") + env.add_module_version_string('mono') envvars = Variables() envvars.Add(BoolVariable('mono_static', 'Statically link mono', False)) @@ -84,6 +97,9 @@ def configure(env): if not mono_root: raise RuntimeError('Mono installation directory not found') + mono_version = mono_root_try_find_mono_version(mono_root) + configure_for_mono_version(env, mono_version) + mono_lib_path = os.path.join(mono_root, 'lib') env.Append(LIBPATH=mono_lib_path) @@ -149,20 +165,14 @@ def configure(env): # We can't use pkg-config to link mono statically, # but we can still use it to find the mono root directory if not mono_root and mono_static: - def pkgconfig_try_find_mono_root(): - tmpenv = Environment() - tmpenv.AppendENVPath('PKG_CONFIG_PATH', os.getenv('PKG_CONFIG_PATH')) - tmpenv.ParseConfig('pkg-config monosgen-2 --libs-only-L') - for hint_dir in tmpenv['LIBPATH']: - name_found = find_file_in_dir(hint_dir, mono_lib_names, prefix='lib', extension=sharedlib_ext) - if name_found and os.path.isdir(os.path.join(hint_dir, '..', 'include', 'mono-2.0')): - return os.path.join(hint_dir, '..') - return '' - mono_root = pkgconfig_try_find_mono_root() + mono_root = pkgconfig_try_find_mono_root(mono_lib_names, sharedlib_ext) if not mono_root: raise RuntimeError('Building with mono_static=yes, but failed to find the mono prefix with pkg-config. Specify one manually') if mono_root: + mono_version = mono_root_try_find_mono_version(mono_root) + configure_for_mono_version(env, mono_version) + mono_lib_path = os.path.join(mono_root, 'lib') env.Append(LIBPATH=mono_lib_path) @@ -178,18 +188,18 @@ def configure(env): if mono_static: mono_lib_file = os.path.join(mono_lib_path, 'lib' + mono_lib + '.a') - if sys.platform == "darwin": + if sys.platform == 'darwin': env.Append(LINKFLAGS=['-Wl,-force_load,' + mono_lib_file]) - elif sys.platform == "linux" or sys.platform == "linux2": + elif sys.platform == 'linux' or sys.platform == 'linux2': env.Append(LINKFLAGS=['-Wl,-whole-archive', mono_lib_file, '-Wl,-no-whole-archive']) else: raise RuntimeError('mono-static: Not supported on this platform') else: env.Append(LIBS=[mono_lib]) - if sys.platform == "darwin": + if sys.platform == 'darwin': env.Append(LIBS=['iconv', 'pthread']) - elif sys.platform == "linux" or sys.platform == "linux2": + elif sys.platform == 'linux' or sys.platform == 'linux2': env.Append(LIBS=['m', 'rt', 'dl', 'pthread']) if not mono_static: @@ -204,11 +214,14 @@ def configure(env): else: assert not mono_static + mono_version = pkgconfig_try_find_mono_version() + configure_for_mono_version(env, mono_version) + env.ParseConfig('pkg-config monosgen-2 --cflags --libs') mono_lib_path = '' mono_so_name = '' - mono_prefix = subprocess.check_output(["pkg-config", "mono-2", "--variable=prefix"]).decode("utf8").strip() + mono_prefix = subprocess.check_output(['pkg-config', 'mono-2', '--variable=prefix']).decode('utf8').strip() tmpenv = Environment() tmpenv.AppendENVPath('PKG_CONFIG_PATH', os.getenv('PKG_CONFIG_PATH')) @@ -230,13 +243,41 @@ def configure(env): env.Append(LINKFLAGS='-rdynamic') -def get_doc_classes(): - return [ - "@C#", - "CSharpScript", - "GodotSharp", - ] +def configure_for_mono_version(env, mono_version): + if mono_version is None: + raise RuntimeError('Mono JIT compiler version not found') + print('Mono JIT compiler version: ' + str(mono_version)) + if mono_version >= LooseVersion("5.12.0"): + env.Append(CPPFLAGS=['-DHAS_PENDING_EXCEPTIONS']) -def get_doc_path(): - return "doc_classes" +def pkgconfig_try_find_mono_root(mono_lib_names, sharedlib_ext): + tmpenv = Environment() + tmpenv.AppendENVPath('PKG_CONFIG_PATH', os.getenv('PKG_CONFIG_PATH')) + tmpenv.ParseConfig('pkg-config monosgen-2 --libs-only-L') + for hint_dir in tmpenv['LIBPATH']: + name_found = find_file_in_dir(hint_dir, mono_lib_names, prefix='lib', extension=sharedlib_ext) + if name_found and os.path.isdir(os.path.join(hint_dir, '..', 'include', 'mono-2.0')): + return os.path.join(hint_dir, '..') + return '' + + +def pkgconfig_try_find_mono_version(): + lines = subprocess.check_output(['pkg-config', 'monosgen-2', '--modversion']).splitlines() + greater_version = None + for line in lines: + try: + version = LooseVersion(line) + if greater_version is None or version > greater_version: + greater_version = version + except ValueError: + pass + return greater_version + + +def mono_root_try_find_mono_version(mono_root): + first_line = subprocess.check_output([os.path.join(mono_root, 'bin', 'mono'), '--version']).splitlines()[0] + try: + return LooseVersion(first_line.split()[len('Mono JIT compiler version'.split())]) + except (ValueError, IndexError): + return None diff --git a/modules/mono/csharp_script.cpp b/modules/mono/csharp_script.cpp index 46c40b26900..dcac4527622 100644 --- a/modules/mono/csharp_script.cpp +++ b/modules/mono/csharp_script.cpp @@ -49,6 +49,8 @@ #include "mono_gd/gd_mono_class.h" #include "mono_gd/gd_mono_marshal.h" #include "signal_awaiter_utils.h" +#include "utils/macros.h" +#include "utils/thread_local.h" #define CACHED_STRING_NAME(m_var) (CSharpLanguage::get_singleton()->get_string_names().m_var) @@ -476,7 +478,7 @@ String CSharpLanguage::_get_indentation() const { Vector CSharpLanguage::debug_get_current_stack_info() { #ifdef DEBUG_ENABLED - // Printing an error here will result in endless recursion, so we must be careful + _TLS_RECURSION_GUARD_V_(Vector()); if (!gdmono->is_runtime_initialized() || !GDMono::get_singleton()->get_core_api_assembly() || !GDMonoUtils::mono_cache.corlib_cache_updated) return Vector(); @@ -500,15 +502,15 @@ Vector CSharpLanguage::debug_get_current_stack_info() #ifdef DEBUG_ENABLED Vector CSharpLanguage::stack_trace_get_info(MonoObject *p_stack_trace) { - // Printing an error here could result in endless recursion, so we must be careful + _TLS_RECURSION_GUARD_V_(Vector()); - MonoObject *exc = NULL; + MonoException *exc = NULL; GDMonoUtils::StackTrace_GetFrames st_get_frames = CACHED_METHOD_THUNK(System_Diagnostics_StackTrace, GetFrames); - MonoArray *frames = st_get_frames(p_stack_trace, &exc); + MonoArray *frames = st_get_frames(p_stack_trace, (MonoObject **)&exc); if (exc) { - GDMonoUtils::print_unhandled_exception(exc, true /* fail silently to avoid endless recursion */); + GDMonoUtils::debug_print_unhandled_exception(exc); return Vector(); } @@ -529,10 +531,10 @@ Vector CSharpLanguage::stack_trace_get_info(MonoObjec MonoString *file_name; int file_line_num; MonoString *method_decl; - get_sf_info(frame, &file_name, &file_line_num, &method_decl, &exc); + get_sf_info(frame, &file_name, &file_line_num, &method_decl, (MonoObject **)&exc); if (exc) { - GDMonoUtils::print_unhandled_exception(exc, true /* fail silently to avoid endless recursion */); + GDMonoUtils::debug_print_unhandled_exception(exc); return Vector(); } @@ -561,12 +563,12 @@ void CSharpLanguage::frame() { ERR_FAIL_NULL(thunk); - MonoObject *ex; - thunk(task_scheduler, &ex); + MonoException *exc = NULL; + thunk(task_scheduler, (MonoObject **)&exc); - if (ex) { - mono_print_unhandled_exception(ex); - ERR_FAIL(); + if (exc) { + GDMonoUtils::debug_unhandled_exception(exc); + _UNREACHABLE_(); } } } @@ -1078,11 +1080,11 @@ bool CSharpInstance::get(const StringName &p_name, Variant &r_ret) const { GDMonoProperty *property = top->get_property(p_name); if (property) { - MonoObject *exc = NULL; + MonoException *exc = NULL; MonoObject *value = property->get_value(mono_object, &exc); if (exc) { r_ret = Variant(); - GDMonoUtils::print_unhandled_exception(exc); + GDMonoUtils::set_pending_exception(exc); } else { r_ret = GDMonoMarshal::mono_object_to_variant(value); } @@ -1490,12 +1492,12 @@ bool CSharpScript::_update_exports() { CACHED_FIELD(GodotObject, ptr)->set_value_raw(tmp_object, tmp_object); // FIXME WTF is this workaround GDMonoMethod *ctor = script_class->get_method(CACHED_STRING_NAME(dotctor), 0); - MonoObject *ex = NULL; - ctor->invoke(tmp_object, NULL, &ex); + MonoException *exc = NULL; + ctor->invoke(tmp_object, NULL, &exc); - if (ex) { + if (exc) { ERR_PRINT("Exception thrown from constructor of temporary MonoObject:"); - mono_print_unhandled_exception(ex); + GDMonoUtils::debug_print_unhandled_exception(exc); tmp_object = NULL; ERR_FAIL_V(false); } @@ -1544,11 +1546,11 @@ bool CSharpScript::_update_exports() { exported_members_cache.push_front(prop_info); if (tmp_object) { - MonoObject *exc = NULL; + MonoException *exc = NULL; MonoObject *ret = property->get_value(tmp_object, &exc); if (exc) { exported_members_defval_cache[name] = Variant(); - GDMonoUtils::print_unhandled_exception(exc); + GDMonoUtils::debug_print_unhandled_exception(exc); } else { exported_members_defval_cache[name] = GDMonoMarshal::mono_object_to_variant(ret); } @@ -1918,7 +1920,7 @@ CSharpInstance *CSharpScript::_create_instance(const Variant **p_args, int p_arg // Construct GDMonoMethod *ctor = script_class->get_method(CACHED_STRING_NAME(dotctor), p_argcount); - ctor->invoke(mono_object, p_args, NULL); + ctor->invoke(mono_object, p_args); // Tie managed to unmanaged instance->gchandle = MonoGCHandle::create_strong(mono_object); diff --git a/modules/mono/editor/csharp_project.cpp b/modules/mono/editor/csharp_project.cpp index e4269b0aece..bc95607743c 100644 --- a/modules/mono/editor/csharp_project.cpp +++ b/modules/mono/editor/csharp_project.cpp @@ -47,11 +47,11 @@ String generate_core_api_project(const String &p_dir, const Vector &p_fi Variant dir = p_dir; Variant compile_items = p_files; const Variant *args[2] = { &dir, &compile_items }; - MonoObject *ex = NULL; - MonoObject *ret = klass->get_method("GenCoreApiProject", 2)->invoke(NULL, args, &ex); + MonoException *exc = NULL; + MonoObject *ret = klass->get_method("GenCoreApiProject", 2)->invoke(NULL, args, &exc); - if (ex) { - mono_print_unhandled_exception(ex); + if (exc) { + GDMonoUtils::debug_unhandled_exception(exc); ERR_FAIL_V(String()); } @@ -68,11 +68,11 @@ String generate_editor_api_project(const String &p_dir, const String &p_core_dll Variant core_dll_path = p_core_dll_path; Variant compile_items = p_files; const Variant *args[3] = { &dir, &core_dll_path, &compile_items }; - MonoObject *ex = NULL; - MonoObject *ret = klass->get_method("GenEditorApiProject", 3)->invoke(NULL, args, &ex); + MonoException *exc = NULL; + MonoObject *ret = klass->get_method("GenEditorApiProject", 3)->invoke(NULL, args, &exc); - if (ex) { - mono_print_unhandled_exception(ex); + if (exc) { + GDMonoUtils::debug_unhandled_exception(exc); ERR_FAIL_V(String()); } @@ -89,11 +89,11 @@ String generate_game_project(const String &p_dir, const String &p_name, const Ve Variant name = p_name; Variant compile_items = p_files; const Variant *args[3] = { &dir, &name, &compile_items }; - MonoObject *ex = NULL; - MonoObject *ret = klass->get_method("GenGameProject", 3)->invoke(NULL, args, &ex); + MonoException *exc = NULL; + MonoObject *ret = klass->get_method("GenGameProject", 3)->invoke(NULL, args, &exc); - if (ex) { - mono_print_unhandled_exception(ex); + if (exc) { + GDMonoUtils::debug_unhandled_exception(exc); ERR_FAIL_V(String()); } @@ -110,11 +110,11 @@ void add_item(const String &p_project_path, const String &p_item_type, const Str Variant item_type = p_item_type; Variant include = p_include; const Variant *args[3] = { &project_path, &item_type, &include }; - MonoObject *ex = NULL; - klass->get_method("AddItemToProjectChecked", 3)->invoke(NULL, args, &ex); + MonoException *exc = NULL; + klass->get_method("AddItemToProjectChecked", 3)->invoke(NULL, args, &exc); - if (ex) { - mono_print_unhandled_exception(ex); + if (exc) { + GDMonoUtils::debug_unhandled_exception(exc); ERR_FAIL(); } } diff --git a/modules/mono/editor/godotsharp_builds.cpp b/modules/mono/editor/godotsharp_builds.cpp index 43d58caad27..156a01ff5a0 100644 --- a/modules/mono/editor/godotsharp_builds.cpp +++ b/modules/mono/editor/godotsharp_builds.cpp @@ -512,14 +512,14 @@ void GodotSharpBuilds::BuildProcess::start(bool p_blocking) { const Variant *ctor_args[2] = { &solution, &config }; - MonoObject *ex = NULL; + MonoException *exc = NULL; GDMonoMethod *ctor = klass->get_method(".ctor", 2); - ctor->invoke(mono_object, ctor_args, &ex); + ctor->invoke(mono_object, ctor_args, &exc); - if (ex) { + if (exc) { exited = true; - GDMonoUtils::print_unhandled_exception(ex); - String message = "The build constructor threw an exception.\n" + GDMonoUtils::get_exception_name_and_message(ex); + GDMonoUtils::debug_unhandled_exception(exc); + String message = "The build constructor threw an exception.\n" + GDMonoUtils::get_exception_name_and_message(exc); build_tab->on_build_exec_failed(message); ERR_EXPLAIN(message); ERR_FAIL(); @@ -534,14 +534,14 @@ void GodotSharpBuilds::BuildProcess::start(bool p_blocking) { const Variant *args[3] = { &logger_assembly, &logger_output_dir, &custom_props }; - ex = NULL; + exc = NULL; GDMonoMethod *build_method = klass->get_method(p_blocking ? "Build" : "BuildAsync", 3); - build_method->invoke(mono_object, args, &ex); + build_method->invoke(mono_object, args, &exc); - if (ex) { + if (exc) { exited = true; - GDMonoUtils::print_unhandled_exception(ex); - String message = "The build method threw an exception.\n" + GDMonoUtils::get_exception_name_and_message(ex); + GDMonoUtils::debug_unhandled_exception(exc); + String message = "The build method threw an exception.\n" + GDMonoUtils::get_exception_name_and_message(exc); build_tab->on_build_exec_failed(message); ERR_EXPLAIN(message); ERR_FAIL(); diff --git a/modules/mono/editor/monodevelop_instance.cpp b/modules/mono/editor/monodevelop_instance.cpp index 48a285561d8..9f05711fd6c 100644 --- a/modules/mono/editor/monodevelop_instance.cpp +++ b/modules/mono/editor/monodevelop_instance.cpp @@ -40,14 +40,14 @@ void MonoDevelopInstance::execute(const Vector &p_files) { ERR_FAIL_NULL(execute_method); ERR_FAIL_COND(gc_handle.is_null()); - MonoObject *ex = NULL; + MonoException *exc = NULL; Variant files = p_files; const Variant *args[1] = { &files }; - execute_method->invoke(gc_handle->get_target(), args, &ex); + execute_method->invoke(gc_handle->get_target(), args, &exc); - if (ex) { - mono_print_unhandled_exception(ex); + if (exc) { + GDMonoUtils::debug_unhandled_exception(exc); ERR_FAIL(); } } @@ -68,14 +68,14 @@ MonoDevelopInstance::MonoDevelopInstance(const String &p_solution) { MonoObject *obj = mono_object_new(TOOLS_DOMAIN, klass->get_mono_ptr()); GDMonoMethod *ctor = klass->get_method(".ctor", 1); - MonoObject *ex = NULL; + MonoException *exc = NULL; Variant solution = p_solution; const Variant *args[1] = { &solution }; - ctor->invoke(obj, args, &ex); + ctor->invoke(obj, args, &exc); - if (ex) { - mono_print_unhandled_exception(ex); + if (exc) { + GDMonoUtils::debug_unhandled_exception(exc); ERR_FAIL(); } diff --git a/modules/mono/mono_gc_handle.cpp b/modules/mono/mono_gc_handle.cpp index 4e82bcd03e9..12109045e04 100644 --- a/modules/mono/mono_gc_handle.cpp +++ b/modules/mono/mono_gc_handle.cpp @@ -34,15 +34,12 @@ uint32_t MonoGCHandle::make_strong_handle(MonoObject *p_object) { - return mono_gchandle_new( - p_object, - false /* do not pin the object */ - ); + return mono_gchandle_new(p_object, /* pinned: */ false); } uint32_t MonoGCHandle::make_weak_handle(MonoObject *p_object) { - return mono_gchandle_new_weakref(p_object, false); + return mono_gchandle_new_weakref(p_object, /* track_resurrection: */ false); } Ref MonoGCHandle::create_strong(MonoObject *p_object) { diff --git a/modules/mono/mono_gd/gd_mono.cpp b/modules/mono/mono_gd/gd_mono.cpp index 0646580eaae..bc84f43b4f3 100644 --- a/modules/mono/mono_gd/gd_mono.cpp +++ b/modules/mono/mono_gd/gd_mono.cpp @@ -53,14 +53,6 @@ #include "main/main.h" #endif -void gdmono_unhandled_exception_hook(MonoObject *exc, void *user_data) { - - (void)user_data; // UNUSED - - GDMonoUtils::print_unhandled_exception(exc); - abort(); -} - #ifdef MONO_PRINT_HANDLER_ENABLED void gdmono_MonoPrintCallback(const char *string, mono_bool is_stdout) { @@ -197,6 +189,8 @@ void GDMono::initialize() { mono_config_parse(NULL); + mono_install_unhandled_exception_hook(&unhandled_exception_hook, NULL); + root_domain = mono_jit_init_version("GodotEngine.RootDomain", "v4.0.30319"); ERR_EXPLAIN("Mono: Failed to initialize runtime"); @@ -279,8 +273,6 @@ void GDMono::initialize() { OS::get_singleton()->print("Mono: Glue disabled, ignoring script assemblies\n"); #endif - mono_install_unhandled_exception_hook(gdmono_unhandled_exception_hook, NULL); - OS::get_singleton()->print("Mono: INITIALIZED\n"); } @@ -652,12 +644,12 @@ Error GDMono::_unload_scripts_domain() { _GodotSharp::get_singleton()->_dispose_callback(); - MonoObject *ex = NULL; - mono_domain_try_unload(domain, &ex); + MonoException *exc = NULL; + mono_domain_try_unload(domain, (MonoObject **)&exc); - if (ex) { - ERR_PRINT("Exception thrown when unloading scripts domain:"); - mono_print_unhandled_exception(ex); + if (exc) { + ERR_PRINT("Exception thrown when unloading scripts domain"); + GDMonoUtils::debug_unhandled_exception(exc); return FAILED; } @@ -763,12 +755,12 @@ Error GDMono::finalize_and_unload_domain(MonoDomain *p_domain) { _domain_assemblies_cleanup(mono_domain_get_id(p_domain)); - MonoObject *ex = NULL; - mono_domain_try_unload(p_domain, &ex); + MonoException *exc = NULL; + mono_domain_try_unload(p_domain, (MonoObject **)&exc); - if (ex) { - ERR_PRINTS("Exception thrown when unloading domain `" + domain_name + "`:"); - mono_print_unhandled_exception(ex); + if (exc) { + ERR_PRINTS("Exception thrown when unloading domain `" + domain_name + "`"); + GDMonoUtils::debug_unhandled_exception(exc); return FAILED; } @@ -811,6 +803,21 @@ void GDMono::_domain_assemblies_cleanup(uint32_t p_domain_id) { assemblies.erase(p_domain_id); } +void GDMono::unhandled_exception_hook(MonoObject *p_exc, void *) { + +// This method will be called by the runtime when a thrown exception is not handled. +// It won't be called when we manually treat a thrown exception as unhandled. +// We assume the exception was already printed before calling this hook. + +#ifdef DEBUG_ENABLED + GDMonoUtils::debug_send_unhandled_exception_error((MonoException *)p_exc); + if (ScriptDebugger::get_singleton()) + ScriptDebugger::get_singleton()->idle_poll(); +#endif + abort(); + _UNREACHABLE_(); +} + GDMono::GDMono() { singleton = this; diff --git a/modules/mono/mono_gd/gd_mono.h b/modules/mono/mono_gd/gd_mono.h index 5e011528709..e0ec6ced5e9 100644 --- a/modules/mono/mono_gd/gd_mono.h +++ b/modules/mono/mono_gd/gd_mono.h @@ -164,6 +164,8 @@ public: static GDMono *get_singleton() { return singleton; } + static void unhandled_exception_hook(MonoObject *p_exc, void *p_user_data); + // Do not use these, unless you know what you're doing void add_assembly(uint32_t p_domain_id, GDMonoAssembly *p_assembly); GDMonoAssembly **get_loaded_assembly(const String &p_name); diff --git a/modules/mono/mono_gd/gd_mono_internals.cpp b/modules/mono/mono_gd/gd_mono_internals.cpp index a1a79f957f2..505c030ca1f 100644 --- a/modules/mono/mono_gd/gd_mono_internals.cpp +++ b/modules/mono/mono_gd/gd_mono_internals.cpp @@ -32,8 +32,12 @@ #include "../csharp_script.h" #include "../mono_gc_handle.h" +#include "../utils/macros.h" +#include "../utils/thread_local.h" #include "gd_mono_utils.h" +#include + namespace GDMonoInternals { void tie_managed_to_unmanaged(MonoObject *managed, Object *unmanaged) { @@ -64,4 +68,11 @@ void tie_managed_to_unmanaged(MonoObject *managed, Object *unmanaged) { return; } + +void unhandled_exception(MonoException *p_exc) { + mono_unhandled_exception((MonoObject *)p_exc); // prints the exception as well + abort(); + _UNREACHABLE_(); +} + } // namespace GDMonoInternals diff --git a/modules/mono/mono_gd/gd_mono_internals.h b/modules/mono/mono_gd/gd_mono_internals.h index abec65e7d4c..50cadcedf69 100644 --- a/modules/mono/mono_gd/gd_mono_internals.h +++ b/modules/mono/mono_gd/gd_mono_internals.h @@ -33,11 +33,20 @@ #include +#include "../utils/macros.h" + #include "core/object.h" namespace GDMonoInternals { void tie_managed_to_unmanaged(MonoObject *managed, Object *unmanaged); -} + +/** + * Do not call this function directly. + * Use GDMonoUtils::debug_unhandled_exception(MonoException *) instead. + */ +_NO_RETURN_ void unhandled_exception(MonoException *p_exc); + +} // namespace GDMonoInternals #endif // GD_MONO_INTERNALS_H diff --git a/modules/mono/mono_gd/gd_mono_marshal.cpp b/modules/mono/mono_gd/gd_mono_marshal.cpp index c04fcca962a..31c5bbb2fb6 100644 --- a/modules/mono/mono_gd/gd_mono_marshal.cpp +++ b/modules/mono/mono_gd/gd_mono_marshal.cpp @@ -837,11 +837,13 @@ MonoObject *Dictionary_to_mono_object(const Dictionary &p_dict) { GDMonoUtils::MarshalUtils_ArraysToDict arrays_to_dict = CACHED_METHOD_THUNK(MarshalUtils, ArraysToDictionary); - MonoObject *ex = NULL; - MonoObject *ret = arrays_to_dict(keys, values, &ex); + MonoException *exc = NULL; + GD_MONO_BEGIN_RUNTIME_INVOKE; + MonoObject *ret = arrays_to_dict(keys, values, (MonoObject **)&exc); + GD_MONO_END_RUNTIME_INVOKE; - if (ex) { - mono_print_unhandled_exception(ex); + if (exc) { + GDMonoUtils::set_pending_exception(exc); ERR_FAIL_V(NULL); } @@ -858,11 +860,13 @@ Dictionary mono_object_to_Dictionary(MonoObject *p_dict) { MonoArray *keys = NULL; MonoArray *values = NULL; - MonoObject *ex = NULL; - dict_to_arrays(p_dict, &keys, &values, &ex); + MonoException *exc = NULL; + GD_MONO_BEGIN_RUNTIME_INVOKE; + dict_to_arrays(p_dict, &keys, &values, (MonoObject **)&exc); + GD_MONO_END_RUNTIME_INVOKE; - if (ex) { - mono_print_unhandled_exception(ex); + if (exc) { + GDMonoUtils::set_pending_exception(exc); ERR_FAIL_V(Dictionary()); } diff --git a/modules/mono/mono_gd/gd_mono_method.cpp b/modules/mono/mono_gd/gd_mono_method.cpp index ad529049456..c8df1038ce4 100644 --- a/modules/mono/mono_gd/gd_mono_method.cpp +++ b/modules/mono/mono_gd/gd_mono_method.cpp @@ -95,7 +95,7 @@ void *GDMonoMethod::get_thunk() { return mono_method_get_unmanaged_thunk(mono_method); } -MonoObject *GDMonoMethod::invoke(MonoObject *p_object, const Variant **p_params, MonoObject **r_exc) { +MonoObject *GDMonoMethod::invoke(MonoObject *p_object, const Variant **p_params, MonoException **r_exc) { if (get_return_type().type_encoding != MONO_TYPE_VOID || get_parameters_count() > 0) { MonoArray *params = mono_array_new(mono_domain_get(), CACHED_CLASS_RAW(MonoObject), get_parameters_count()); @@ -104,28 +104,32 @@ MonoObject *GDMonoMethod::invoke(MonoObject *p_object, const Variant **p_params, mono_array_set(params, MonoObject *, i, boxed_param); } - MonoObject *exc = NULL; - MonoObject *ret = mono_runtime_invoke_array(mono_method, p_object, params, &exc); + MonoException *exc = NULL; + GD_MONO_BEGIN_RUNTIME_INVOKE; + MonoObject *ret = mono_runtime_invoke_array(mono_method, p_object, params, (MonoObject **)&exc); + GD_MONO_END_RUNTIME_INVOKE; if (exc) { ret = NULL; if (r_exc) { *r_exc = exc; } else { - GDMonoUtils::print_unhandled_exception(exc); + GDMonoUtils::set_pending_exception(exc); } } return ret; } else { - MonoObject *exc = NULL; - mono_runtime_invoke(mono_method, p_object, NULL, &exc); + MonoException *exc = NULL; + GD_MONO_BEGIN_RUNTIME_INVOKE; + mono_runtime_invoke(mono_method, p_object, NULL, (MonoObject **)&exc); + GD_MONO_END_RUNTIME_INVOKE; if (exc) { if (r_exc) { *r_exc = exc; } else { - GDMonoUtils::print_unhandled_exception(exc); + GDMonoUtils::set_pending_exception(exc); } } @@ -133,21 +137,23 @@ MonoObject *GDMonoMethod::invoke(MonoObject *p_object, const Variant **p_params, } } -MonoObject *GDMonoMethod::invoke(MonoObject *p_object, MonoObject **r_exc) { +MonoObject *GDMonoMethod::invoke(MonoObject *p_object, MonoException **r_exc) { ERR_FAIL_COND_V(get_parameters_count() > 0, NULL); return invoke_raw(p_object, NULL, r_exc); } -MonoObject *GDMonoMethod::invoke_raw(MonoObject *p_object, void **p_params, MonoObject **r_exc) { - MonoObject *exc = NULL; - MonoObject *ret = mono_runtime_invoke(mono_method, p_object, p_params, &exc); +MonoObject *GDMonoMethod::invoke_raw(MonoObject *p_object, void **p_params, MonoException **r_exc) { + MonoException *exc = NULL; + GD_MONO_BEGIN_RUNTIME_INVOKE; + MonoObject *ret = mono_runtime_invoke(mono_method, p_object, p_params, (MonoObject **)&exc); + GD_MONO_END_RUNTIME_INVOKE; if (exc) { ret = NULL; if (r_exc) { *r_exc = exc; } else { - GDMonoUtils::print_unhandled_exception(exc); + GDMonoUtils::set_pending_exception(exc); } } diff --git a/modules/mono/mono_gd/gd_mono_method.h b/modules/mono/mono_gd/gd_mono_method.h index a173af83f46..444ec2a67d3 100644 --- a/modules/mono/mono_gd/gd_mono_method.h +++ b/modules/mono/mono_gd/gd_mono_method.h @@ -71,9 +71,9 @@ public: void *get_thunk(); - MonoObject *invoke(MonoObject *p_object, const Variant **p_params, MonoObject **r_exc = NULL); - MonoObject *invoke(MonoObject *p_object, MonoObject **r_exc = NULL); - MonoObject *invoke_raw(MonoObject *p_object, void **p_params, MonoObject **r_exc = NULL); + MonoObject *invoke(MonoObject *p_object, const Variant **p_params, MonoException **r_exc = NULL); + MonoObject *invoke(MonoObject *p_object, MonoException **r_exc = NULL); + MonoObject *invoke_raw(MonoObject *p_object, void **p_params, MonoException **r_exc = NULL); String get_full_name(bool p_signature = false) const; String get_full_name_no_class() const; diff --git a/modules/mono/mono_gd/gd_mono_property.cpp b/modules/mono/mono_gd/gd_mono_property.cpp index 0fe527b1994..1f837a2d781 100644 --- a/modules/mono/mono_gd/gd_mono_property.cpp +++ b/modules/mono/mono_gd/gd_mono_property.cpp @@ -138,47 +138,53 @@ bool GDMonoProperty::has_setter() { return mono_property_get_set_method(mono_property) != NULL; } -void GDMonoProperty::set_value(MonoObject *p_object, MonoObject *p_value, MonoObject **r_exc) { +void GDMonoProperty::set_value(MonoObject *p_object, MonoObject *p_value, MonoException **r_exc) { MonoMethod *prop_method = mono_property_get_set_method(mono_property); MonoArray *params = mono_array_new(mono_domain_get(), CACHED_CLASS_RAW(MonoObject), 1); mono_array_set(params, MonoObject *, 0, p_value); - MonoObject *exc = NULL; - mono_runtime_invoke_array(prop_method, p_object, params, &exc); + MonoException *exc = NULL; + GD_MONO_BEGIN_RUNTIME_INVOKE; + mono_runtime_invoke_array(prop_method, p_object, params, (MonoObject **)&exc); + GD_MONO_END_RUNTIME_INVOKE; if (exc) { if (r_exc) { *r_exc = exc; } else { - GDMonoUtils::print_unhandled_exception(exc); + GDMonoUtils::set_pending_exception(exc); } } } -void GDMonoProperty::set_value(MonoObject *p_object, void **p_params, MonoObject **r_exc) { - MonoObject *exc = NULL; - mono_property_set_value(mono_property, p_object, p_params, &exc); +void GDMonoProperty::set_value(MonoObject *p_object, void **p_params, MonoException **r_exc) { + MonoException *exc = NULL; + GD_MONO_BEGIN_RUNTIME_INVOKE; + mono_property_set_value(mono_property, p_object, p_params, (MonoObject **)&exc); + GD_MONO_END_RUNTIME_INVOKE; if (exc) { if (r_exc) { *r_exc = exc; } else { - GDMonoUtils::print_unhandled_exception(exc); + GDMonoUtils::set_pending_exception(exc); } } } -MonoObject *GDMonoProperty::get_value(MonoObject *p_object, MonoObject **r_exc) { - MonoObject *exc = NULL; - MonoObject *ret = mono_property_get_value(mono_property, p_object, NULL, &exc); +MonoObject *GDMonoProperty::get_value(MonoObject *p_object, MonoException **r_exc) { + MonoException *exc = NULL; + GD_MONO_BEGIN_RUNTIME_INVOKE; + MonoObject *ret = mono_property_get_value(mono_property, p_object, NULL, (MonoObject **)&exc); + GD_MONO_END_RUNTIME_INVOKE; if (exc) { ret = NULL; if (r_exc) { *r_exc = exc; } else { - GDMonoUtils::print_unhandled_exception(exc); + GDMonoUtils::set_pending_exception(exc); } } diff --git a/modules/mono/mono_gd/gd_mono_property.h b/modules/mono/mono_gd/gd_mono_property.h index 2a0065e8502..b3f1e2114a7 100644 --- a/modules/mono/mono_gd/gd_mono_property.h +++ b/modules/mono/mono_gd/gd_mono_property.h @@ -62,9 +62,9 @@ public: _FORCE_INLINE_ ManagedType get_type() const { return type; } - void set_value(MonoObject *p_object, MonoObject *p_value, MonoObject **r_exc = NULL); - void set_value(MonoObject *p_object, void **p_params, MonoObject **r_exc = NULL); - MonoObject *get_value(MonoObject *p_object, MonoObject **r_exc = NULL); + void set_value(MonoObject *p_object, MonoObject *p_value, MonoException **r_exc = NULL); + void set_value(MonoObject *p_object, void **p_params, MonoException **r_exc = NULL); + MonoObject *get_value(MonoObject *p_object, MonoException **r_exc = NULL); bool get_bool_value(MonoObject *p_object); int get_int_value(MonoObject *p_object); diff --git a/modules/mono/mono_gd/gd_mono_utils.cpp b/modules/mono/mono_gd/gd_mono_utils.cpp index fbdbeaa3f7e..a229552b76b 100644 --- a/modules/mono/mono_gd/gd_mono_utils.cpp +++ b/modules/mono/mono_gd/gd_mono_utils.cpp @@ -30,12 +30,15 @@ #include "gd_mono_utils.h" +#include + #include "os/dir_access.h" #include "os/os.h" #include "project_settings.h" #include "reference.h" #include "../csharp_script.h" +#include "../utils/macros.h" #include "gd_mono.h" #include "gd_mono_class.h" #include "gd_mono_marshal.h" @@ -397,10 +400,10 @@ MonoDomain *create_domain(const String &p_friendly_name) { return domain; } -String get_exception_name_and_message(MonoObject *p_ex) { +String get_exception_name_and_message(MonoException *p_ex) { String res; - MonoClass *klass = mono_object_get_class(p_ex); + MonoClass *klass = mono_object_get_class((MonoObject *)p_ex); MonoType *type = mono_class_get_type(klass); char *full_name = mono_type_full_name(type); @@ -410,29 +413,31 @@ String get_exception_name_and_message(MonoObject *p_ex) { res += ": "; MonoProperty *prop = mono_class_get_property_from_name(klass, "Message"); - MonoString *msg = (MonoString *)mono_property_get_value(prop, p_ex, NULL, NULL); + MonoString *msg = (MonoString *)mono_property_get_value(prop, (MonoObject *)p_ex, NULL, NULL); res += GDMonoMarshal::mono_string_to_godot(msg); return res; } -void print_unhandled_exception(MonoObject *p_exc) { - print_unhandled_exception(p_exc, false); +void debug_print_unhandled_exception(MonoException *p_exc) { + print_unhandled_exception(p_exc); + debug_send_unhandled_exception_error(p_exc); } -void print_unhandled_exception(MonoObject *p_exc, bool p_recursion_caution) { - mono_print_unhandled_exception(p_exc); +void debug_send_unhandled_exception_error(MonoException *p_exc) { #ifdef DEBUG_ENABLED if (!ScriptDebugger::get_singleton()) return; + _TLS_RECURSION_GUARD_; + ScriptLanguage::StackInfo separator; - separator.file = ""; + separator.file = String(); separator.func = "--- " + RTR("End of inner exception stack trace") + " ---"; separator.line = 0; Vector si; - String exc_msg = ""; + String exc_msg; while (p_exc != NULL) { GDMonoClass *st_klass = CACHED_CLASS(System_Diagnostics_StackTrace); @@ -441,24 +446,16 @@ void print_unhandled_exception(MonoObject *p_exc, bool p_recursion_caution) { MonoBoolean need_file_info = true; void *ctor_args[2] = { p_exc, &need_file_info }; - MonoObject *unexpected_exc = NULL; + MonoException *unexpected_exc = NULL; CACHED_METHOD(System_Diagnostics_StackTrace, ctor_Exception_bool)->invoke_raw(stack_trace, ctor_args, &unexpected_exc); - if (unexpected_exc != NULL) { - mono_print_unhandled_exception(unexpected_exc); - - if (p_recursion_caution) { - // Called from CSharpLanguage::get_current_stack_info, - // so printing an error here could result in endless recursion - OS::get_singleton()->printerr("Mono: Method GDMonoUtils::print_unhandled_exception failed"); - return; - } else { - ERR_FAIL(); - } + if (unexpected_exc) { + GDMonoInternals::unhandled_exception(unexpected_exc); + _UNREACHABLE_(); } Vector _si; - if (stack_trace != NULL && !p_recursion_caution) { + if (stack_trace != NULL) { _si = CSharpLanguage::get_singleton()->stack_trace_get_info(stack_trace); for (int i = _si.size() - 1; i >= 0; i--) si.insert(0, _si[i]); @@ -466,10 +463,15 @@ void print_unhandled_exception(MonoObject *p_exc, bool p_recursion_caution) { exc_msg += (exc_msg.length() > 0 ? " ---> " : "") + GDMonoUtils::get_exception_name_and_message(p_exc); - GDMonoProperty *p_prop = GDMono::get_singleton()->get_class(mono_object_get_class(p_exc))->get_property("InnerException"); - p_exc = p_prop != NULL ? p_prop->get_value(p_exc) : NULL; - if (p_exc != NULL) + GDMonoClass *exc_class = GDMono::get_singleton()->get_class(mono_get_exception_class()); + GDMonoProperty *inner_exc_prop = exc_class->get_property("InnerException"); + CRASH_COND(inner_exc_prop == NULL); + + MonoObject *inner_exc = inner_exc_prop->get_value((MonoObject *)p_exc); + if (inner_exc != NULL) si.insert(0, separator); + + p_exc = (MonoException *)inner_exc; } String file = si.size() ? si[0].file : __FILE__; @@ -481,4 +483,38 @@ void print_unhandled_exception(MonoObject *p_exc, bool p_recursion_caution) { #endif } +void debug_unhandled_exception(MonoException *p_exc) { +#ifdef DEBUG_ENABLED + GDMonoUtils::debug_send_unhandled_exception_error(p_exc); + if (ScriptDebugger::get_singleton()) + ScriptDebugger::get_singleton()->idle_poll(); +#endif + GDMonoInternals::unhandled_exception(p_exc); // prints the exception as well + _UNREACHABLE_(); +} + +void print_unhandled_exception(MonoException *p_exc) { + mono_print_unhandled_exception((MonoObject *)p_exc); +} + +void set_pending_exception(MonoException *p_exc) { +#ifdef HAS_PENDING_EXCEPTIONS + if (get_runtime_invoke_count() == 0) { + debug_unhandled_exception(p_exc); + _UNREACHABLE_(); + } + + if (!mono_runtime_set_pending_exception(p_exc, false)) { + ERR_PRINTS("Exception thrown from managed code, but it could not be set as pending:"); + GDMonoUtils::debug_print_unhandled_exception(p_exc); + } +#else + debug_unhandled_exception(p_exc); + _UNREACHABLE_(); +#endif +} + +_THREAD_LOCAL_(int) +current_invoke_count = 0; + } // namespace GDMonoUtils diff --git a/modules/mono/mono_gd/gd_mono_utils.h b/modules/mono/mono_gd/gd_mono_utils.h index fc13a00e85f..4f8e5932cd3 100644 --- a/modules/mono/mono_gd/gd_mono_utils.h +++ b/modules/mono/mono_gd/gd_mono_utils.h @@ -34,6 +34,8 @@ #include #include "../mono_gc_handle.h" +#include "../utils/macros.h" +#include "../utils/thread_local.h" #include "gd_mono_header.h" #include "object.h" @@ -184,10 +186,28 @@ MonoObject *create_managed_from(const RID &p_from); MonoDomain *create_domain(const String &p_friendly_name); -String get_exception_name_and_message(MonoObject *p_ex); +String get_exception_name_and_message(MonoException *p_ex); -void print_unhandled_exception(MonoObject *p_exc); -void print_unhandled_exception(MonoObject *p_exc, bool p_recursion_caution); +void debug_print_unhandled_exception(MonoException *p_exc); +void debug_send_unhandled_exception_error(MonoException *p_exc); +_NO_RETURN_ void debug_unhandled_exception(MonoException *p_exc); +void print_unhandled_exception(MonoException *p_exc); + +/** + * Sets the exception as pending. The exception will be thrown when returning to managed code. + * If no managed method is being invoked by the runtime, the exception will be treated as + * an unhandled exception and the method will not return. + */ +void set_pending_exception(MonoException *p_exc); + +extern _THREAD_LOCAL_(int) current_invoke_count; + +_FORCE_INLINE_ int get_runtime_invoke_count() { + return current_invoke_count; +} +_FORCE_INLINE_ int &get_runtime_invoke_count_ref() { + return current_invoke_count; +} } // namespace GDMonoUtils @@ -206,4 +226,11 @@ void print_unhandled_exception(MonoObject *p_exc, bool p_recursion_caution); #define REAL_T_MONOCLASS CACHED_CLASS_RAW(float) #endif +#define GD_MONO_BEGIN_RUNTIME_INVOKE \ + int &_runtime_invoke_count_ref = GDMonoUtils::get_runtime_invoke_count_ref(); \ + _runtime_invoke_count_ref += 1; + +#define GD_MONO_END_RUNTIME_INVOKE \ + _runtime_invoke_count_ref -= 1; + #endif // GD_MONOUTILS_H diff --git a/modules/mono/signal_awaiter_utils.cpp b/modules/mono/signal_awaiter_utils.cpp index b9d8520ac99..54720652fa7 100644 --- a/modules/mono/signal_awaiter_utils.cpp +++ b/modules/mono/signal_awaiter_utils.cpp @@ -101,11 +101,13 @@ Variant SignalAwaiterHandle::_signal_callback(const Variant **p_args, int p_argc GDMonoUtils::SignalAwaiter_SignalCallback thunk = CACHED_METHOD_THUNK(SignalAwaiter, SignalCallback); - MonoObject *ex = NULL; - thunk(get_target(), signal_args, &ex); + MonoException *exc = NULL; + GD_MONO_BEGIN_RUNTIME_INVOKE; + thunk(get_target(), signal_args, (MonoObject **)&exc); + GD_MONO_END_RUNTIME_INVOKE; - if (ex) { - mono_print_unhandled_exception(ex); + if (exc) { + GDMonoUtils::set_pending_exception(exc); ERR_FAIL_V(Variant()); } @@ -133,11 +135,13 @@ SignalAwaiterHandle::~SignalAwaiterHandle() { MonoObject *awaiter = get_target(); if (awaiter) { - MonoObject *ex = NULL; - thunk(awaiter, &ex); + MonoException *exc = NULL; + GD_MONO_BEGIN_RUNTIME_INVOKE; + thunk(awaiter, (MonoObject **)&exc); + GD_MONO_END_RUNTIME_INVOKE; - if (ex) { - mono_print_unhandled_exception(ex); + if (exc) { + GDMonoUtils::set_pending_exception(exc); ERR_FAIL_V(); } } diff --git a/modules/mono/tls_configure.py b/modules/mono/tls_configure.py new file mode 100644 index 00000000000..622280b00b6 --- /dev/null +++ b/modules/mono/tls_configure.py @@ -0,0 +1,36 @@ +from __future__ import print_function + +def supported(result): + return 'supported' if result else 'not supported' + + +def check_cxx11_thread_local(conf): + print('Checking for `thread_local` support...', end=" ") + result = conf.TryCompile('thread_local int foo = 0; int main() { return foo; }', '.cpp') + print(supported(result)) + return bool(result) + + +def check_declspec_thread(conf): + print('Checking for `__declspec(thread)` support...', end=" ") + result = conf.TryCompile('__declspec(thread) int foo = 0; int main() { return foo; }', '.cpp') + print(supported(result)) + return bool(result) + + +def check_gcc___thread(conf): + print('Checking for `__thread` support...', end=" ") + result = conf.TryCompile('__thread int foo = 0; int main() { return foo; }', '.cpp') + print(supported(result)) + return bool(result) + + +def configure(conf): + if check_cxx11_thread_local(conf): + conf.env.Append(CPPDEFINES=['HAVE_CXX11_THREAD_LOCAL']) + else: + if conf.env.msvc: + if check_declspec_thread(conf): + conf.env.Append(CPPDEFINES=['HAVE_DECLSPEC_THREAD']) + elif check_gcc___thread(conf): + conf.env.Append(CPPDEFINES=['HAVE_GCC___THREAD']) diff --git a/modules/mono/utils/macros.h b/modules/mono/utils/macros.h new file mode 100644 index 00000000000..337a86870ec --- /dev/null +++ b/modules/mono/utils/macros.h @@ -0,0 +1,59 @@ +/*************************************************************************/ +/* util_macros.h */ +/*************************************************************************/ +/* This file is part of: */ +/* GODOT ENGINE */ +/* https://godotengine.org */ +/*************************************************************************/ +/* Copyright (c) 2007-2018 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2018 Godot Engine contributors (cf. AUTHORS.md) */ +/* */ +/* Permission is hereby granted, free of charge, to any person obtaining */ +/* a copy of this software and associated documentation files (the */ +/* "Software"), to deal in the Software without restriction, including */ +/* without limitation the rights to use, copy, modify, merge, publish, */ +/* distribute, sublicense, and/or sell copies of the Software, and to */ +/* permit persons to whom the Software is furnished to do so, subject to */ +/* the following conditions: */ +/* */ +/* The above copyright notice and this permission notice shall be */ +/* included in all copies or substantial portions of the Software. */ +/* */ +/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */ +/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */ +/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/ +/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */ +/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */ +/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ +/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ +/*************************************************************************/ + +#ifndef UTIL_MACROS_H +#define UTIL_MACROS_H + +// noreturn + +#undef _NO_RETURN_ + +#ifdef __GNUC__ +#define _NO_RETURN_ __attribute__((noreturn)) +#elif _MSC_VER +#define _NO_RETURN_ __declspec(noreturn) +#else +#error Platform or compiler not supported +#endif + +// unreachable + +#if defined(_MSC_VER) +#define _UNREACHABLE_() __assume(0) +#elif defined(__GNUC__) && (__GNUC__ * 100 + __GNUC_MINOR__) >= 405 +#define _UNREACHABLE_() __builtin_unreachable() +#else +#define _UNREACHABLE_() \ + CRASH_NOW(); \ + do { \ + } while (true); +#endif + +#endif // UTIL_MACROS_H diff --git a/modules/mono/utils/thread_local.cpp b/modules/mono/utils/thread_local.cpp new file mode 100644 index 00000000000..6f8b0f90bc4 --- /dev/null +++ b/modules/mono/utils/thread_local.cpp @@ -0,0 +1,99 @@ +/*************************************************************************/ +/* thread_local.cpp */ +/*************************************************************************/ +/* This file is part of: */ +/* GODOT ENGINE */ +/* https://godotengine.org */ +/*************************************************************************/ +/* Copyright (c) 2007-2018 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2018 Godot Engine contributors (cf. AUTHORS.md) */ +/* */ +/* Permission is hereby granted, free of charge, to any person obtaining */ +/* a copy of this software and associated documentation files (the */ +/* "Software"), to deal in the Software without restriction, including */ +/* without limitation the rights to use, copy, modify, merge, publish, */ +/* distribute, sublicense, and/or sell copies of the Software, and to */ +/* permit persons to whom the Software is furnished to do so, subject to */ +/* the following conditions: */ +/* */ +/* The above copyright notice and this permission notice shall be */ +/* included in all copies or substantial portions of the Software. */ +/* */ +/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */ +/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */ +/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/ +/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */ +/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */ +/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ +/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ +/*************************************************************************/ + +#include "thread_local.h" + +#ifdef WINDOWS_ENABLED +#include +#else +#include +#endif + +#include "core/os/memory.h" +#include "core/print_string.h" + +struct ThreadLocalStorage::Impl { + +#ifdef WINDOWS_ENABLED + DWORD dwFlsIndex; +#else + pthread_key_t key; +#endif + + void *get_value() const { +#ifdef WINDOWS_ENABLED + return FlsGetValue(dwFlsIndex); +#else + return pthread_getspecific(key); +#endif + } + + void set_value(void *p_value) const { +#ifdef WINDOWS_ENABLED + FlsSetValue(dwFlsIndex, p_value); +#else + pthread_setspecific(key, p_value); +#endif + } + + Impl(void (*p_destr_callback_func)(void *)) { +#ifdef WINDOWS_ENABLED + dwFlsIndex = FlsAlloc(p_destr_callback_func); + ERR_FAIL_COND(dwFlsIndex == FLS_OUT_OF_INDEXES); +#else + pthread_key_create(&key, p_destr_callback_func); +#endif + } + + ~Impl() { +#ifdef WINDOWS_ENABLED + FlsFree(dwFlsIndex); +#else + pthread_key_delete(key); +#endif + } +}; + +void *ThreadLocalStorage::get_value() const { + return pimpl->get_value(); +} + +void ThreadLocalStorage::set_value(void *p_value) const { + pimpl->set_value(p_value); +} + +void ThreadLocalStorage::alloc(void (*p_destr_callback)(void *)) { + pimpl = memnew(ThreadLocalStorage::Impl(p_destr_callback)); +} + +void ThreadLocalStorage::free() { + memdelete(pimpl); + pimpl = NULL; +} diff --git a/modules/mono/utils/thread_local.h b/modules/mono/utils/thread_local.h new file mode 100644 index 00000000000..7ff10b4efca --- /dev/null +++ b/modules/mono/utils/thread_local.h @@ -0,0 +1,171 @@ +/*************************************************************************/ +/* thread_local.h */ +/*************************************************************************/ +/* This file is part of: */ +/* GODOT ENGINE */ +/* https://godotengine.org */ +/*************************************************************************/ +/* Copyright (c) 2007-2018 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2018 Godot Engine contributors (cf. AUTHORS.md) */ +/* */ +/* Permission is hereby granted, free of charge, to any person obtaining */ +/* a copy of this software and associated documentation files (the */ +/* "Software"), to deal in the Software without restriction, including */ +/* without limitation the rights to use, copy, modify, merge, publish, */ +/* distribute, sublicense, and/or sell copies of the Software, and to */ +/* permit persons to whom the Software is furnished to do so, subject to */ +/* the following conditions: */ +/* */ +/* The above copyright notice and this permission notice shall be */ +/* included in all copies or substantial portions of the Software. */ +/* */ +/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */ +/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */ +/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/ +/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */ +/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */ +/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ +/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ +/*************************************************************************/ + +#ifndef THREAD_LOCAL_H +#define THREAD_LOCAL_H + +#ifdef HAVE_CXX11_THREAD_LOCAL +#define _THREAD_LOCAL_(m_t) thread_local m_t +#else + +#if !defined(__GNUC__) && !defined(_MSC_VER) +#error Platform or compiler not supported +#endif + +#ifdef __GNUC__ + +#ifdef HAVE_GCC___THREAD +#define _THREAD_LOCAL_(m_t) __thread m_t +#else +#define USE_CUSTOM_THREAD_LOCAL +#endif + +#elif _MSC_VER + +#ifdef HAVE_DECLSPEC_THREAD +#define _THREAD_LOCAL_(m_t) __declspec(thread) m_t +#else +#define USE_CUSTOM_THREAD_LOCAL +#endif + +#endif // __GNUC__ _MSC_VER + +#endif // HAVE_CXX11_THREAD_LOCAL + +#ifdef USE_CUSTOM_THREAD_LOCAL +#define _THREAD_LOCAL_(m_t) ThreadLocal +#endif + +#include "core/typedefs.h" + +struct ThreadLocalStorage { + + void *get_value() const; + void set_value(void *p_value) const; + + void alloc(void (*p_dest_callback)(void *)); + void free(); + +private: + struct Impl; + Impl *pimpl; +}; + +template +class ThreadLocal { + + ThreadLocalStorage storage; + + T init_val; + +#ifdef WINDOWS_ENABLED +#define _CALLBACK_FUNC_ __stdcall +#else +#define _CALLBACK_FUNC_ +#endif + + static void _CALLBACK_FUNC_ destr_callback(void *tls_data) { + memdelete(static_cast(tls_data)); + } + +#undef _CALLBACK_FUNC_ + + T *_tls_get_value() const { + void *tls_data = storage.get_value(); + + if (tls_data) + return static_cast(tls_data); + + T *data = memnew(T(init_val)); + + storage.set_value(data); + + return data; + } + +public: + ThreadLocal() : + ThreadLocal(T()) {} + + ThreadLocal(const T &p_init_val) : + init_val(p_init_val) { + storage.alloc(&destr_callback); + } + + ThreadLocal(const ThreadLocal &other) : + ThreadLocal(*other._tls_get_value()) {} + + ~ThreadLocal() { + storage.free(); + } + + _FORCE_INLINE_ T *operator&() const { + return _tls_get_value(); + } + + _FORCE_INLINE_ operator T &() const { + return *_tls_get_value(); + } + + _FORCE_INLINE_ ThreadLocal &operator=(const T &val) { + T *ptr = _tls_get_value(); + *ptr = val; + return *this; + } +}; + +struct FlagScopeGuard { + + FlagScopeGuard(bool &p_flag) : + flag(p_flag) { + flag = !flag; + } + + ~FlagScopeGuard() { + flag = !flag; + } + +private: + bool &flag; +}; + +#define _TLS_RECURSION_GUARD_V_(m_ret) \ + static _THREAD_LOCAL_(bool) _recursion_flag_ = false; \ + if (_recursion_flag_) \ + return m_ret; \ + FlagScopeGuard _recursion_guard_(_recursion_flag_); + +#define _TLS_RECURSION_GUARD_ \ + static _THREAD_LOCAL_(bool) _recursion_flag_ = false; \ + if (_recursion_flag_) \ + return; \ + FlagScopeGuard _recursion_guard_(_recursion_flag_); + +#endif // THREAD_LOCAL_H