Browse Source

Merge pull request #16987 from neikeq/pending-exceptions

Mono: Pending exceptions and cleanup
Ignacio Etcheverry 7 years ago
parent
commit
ac9e736b06

+ 13 - 3
modules/mono/SCsub

@@ -5,9 +5,11 @@ Import('env_modules')
 
 
 env_mono = env_modules.Clone()
 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):
 def make_cs_files_header(src, dst):
+    from compat import byte_to_str
+
     with open(dst, 'w') as header:
     with open(dst, 'w') as header:
         header.write('/* This is an automatically generated file; DO NOT EDIT! OK THX */\n')
         header.write('/* This is an automatically generated file; DO NOT EDIT! OK THX */\n')
         header.write('#ifndef _CS_FILES_DATA_H\n')
         header.write('#ifndef _CS_FILES_DATA_H\n')
@@ -75,6 +77,13 @@ else:
 if ARGUMENTS.get('yolo_copy', False):
 if ARGUMENTS.get('yolo_copy', False):
     env_mono.Append(CPPDEFINES=['YOLO_COPY'])
     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
 # Build GodotSharpTools solution
 
 
@@ -128,12 +137,13 @@ def find_msbuild_windows():
         raise RuntimeError('Cannot find mono root directory')
         raise RuntimeError('Cannot find mono root directory')
 
 
     framework_path = os.path.join(mono_root, 'lib', 'mono', '4.5')
     framework_path = os.path.join(mono_root, 'lib', 'mono', '4.5')
-
     mono_bin_dir = os.path.join(mono_root, 'bin')
     mono_bin_dir = os.path.join(mono_root, 'bin')
-
     msbuild_mono = os.path.join(mono_bin_dir, 'msbuild.bat')
     msbuild_mono = os.path.join(mono_bin_dir, 'msbuild.bat')
 
 
     if os.path.isfile(msbuild_mono):
     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 = {
         mono_msbuild_env = {
             'CscToolExe': os.path.join(mono_bin_dir, 'csc.bat'),
             'CscToolExe': os.path.join(mono_bin_dir, 'csc.bat'),
             'VbcToolExe': os.path.join(mono_bin_dir, 'vbc.bat'),
             'VbcToolExe': os.path.join(mono_bin_dir, 'vbc.bat'),

+ 75 - 34
modules/mono/config.py

@@ -4,23 +4,15 @@ import os
 import sys
 import sys
 import subprocess
 import subprocess
 
 
+from distutils.version import LooseVersion
 from SCons.Script import BoolVariable, Dir, Environment, File, PathVariable, SCons, Variables
 from SCons.Script import BoolVariable, Dir, Environment, File, PathVariable, SCons, Variables
 
 
 
 
 monoreg = imp.load_source('mono_reg_utils', 'modules/mono/mono_reg_utils.py')
 monoreg = imp.load_source('mono_reg_utils', 'modules/mono/mono_reg_utils.py')
 
 
 
 
-def find_file_in_dir(directory, files, prefix='', extension=''):
-    if not extension.startswith('.'):
-        extension = '.' + extension
-    for curfile in files:
-        if os.path.isfile(os.path.join(directory, prefix + curfile + extension)):
-            return curfile
-    return ''
-
-
 def can_build(env, platform):
 def can_build(env, platform):
-    if platform in ["javascript"]:
+    if platform in ['javascript']:
         return False # Not yet supported
         return False # Not yet supported
     return True
     return True
 
 
@@ -30,6 +22,27 @@ def is_enabled():
     return False
     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
+    for curfile in files:
+        if os.path.isfile(os.path.join(directory, prefix + curfile + extension)):
+            return curfile
+    return ''
+
+
 def copy_file(src_dir, dst_dir, name):
 def copy_file(src_dir, dst_dir, name):
     from shutil import copyfile
     from shutil import copyfile
 
 
@@ -55,7 +68,7 @@ def custom_path_is_dir_create(key, val, env):
 
 
 def configure(env):
 def configure(env):
     env.use_ptrcall = True
     env.use_ptrcall = True
-    env.add_module_version_string("mono")
+    env.add_module_version_string('mono')
 
 
     envvars = Variables()
     envvars = Variables()
     envvars.Add(BoolVariable('mono_static', 'Statically link mono', False))
     envvars.Add(BoolVariable('mono_static', 'Statically link mono', False))
@@ -84,6 +97,9 @@ def configure(env):
         if not mono_root:
         if not mono_root:
             raise RuntimeError('Mono installation directory not found')
             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')
         mono_lib_path = os.path.join(mono_root, 'lib')
 
 
         env.Append(LIBPATH=mono_lib_path)
         env.Append(LIBPATH=mono_lib_path)
@@ -149,20 +165,14 @@ def configure(env):
         # We can't use pkg-config to link mono statically,
         # We can't use pkg-config to link mono statically,
         # but we can still use it to find the mono root directory
         # but we can still use it to find the mono root directory
         if not mono_root and mono_static:
         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:
             if not mono_root:
                 raise RuntimeError('Building with mono_static=yes, but failed to find the mono prefix with pkg-config. Specify one manually')
                 raise RuntimeError('Building with mono_static=yes, but failed to find the mono prefix with pkg-config. Specify one manually')
 
 
         if mono_root:
         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')
             mono_lib_path = os.path.join(mono_root, 'lib')
 
 
             env.Append(LIBPATH=mono_lib_path)
             env.Append(LIBPATH=mono_lib_path)
@@ -178,18 +188,18 @@ def configure(env):
             if mono_static:
             if mono_static:
                 mono_lib_file = os.path.join(mono_lib_path, 'lib' + mono_lib + '.a')
                 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])
                     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'])
                     env.Append(LINKFLAGS=['-Wl,-whole-archive', mono_lib_file, '-Wl,-no-whole-archive'])
                 else:
                 else:
                     raise RuntimeError('mono-static: Not supported on this platform')
                     raise RuntimeError('mono-static: Not supported on this platform')
             else:
             else:
                 env.Append(LIBS=[mono_lib])
                 env.Append(LIBS=[mono_lib])
 
 
-            if sys.platform == "darwin":
+            if sys.platform == 'darwin':
                 env.Append(LIBS=['iconv', 'pthread'])
                 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'])
                 env.Append(LIBS=['m', 'rt', 'dl', 'pthread'])
 
 
             if not mono_static:
             if not mono_static:
@@ -204,11 +214,14 @@ def configure(env):
         else:
         else:
             assert not mono_static
             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')
             env.ParseConfig('pkg-config monosgen-2 --cflags --libs')
 
 
             mono_lib_path = ''
             mono_lib_path = ''
             mono_so_name = ''
             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 = Environment()
             tmpenv.AppendENVPath('PKG_CONFIG_PATH', os.getenv('PKG_CONFIG_PATH'))
             tmpenv.AppendENVPath('PKG_CONFIG_PATH', os.getenv('PKG_CONFIG_PATH'))
@@ -230,13 +243,41 @@ def configure(env):
         env.Append(LINKFLAGS='-rdynamic')
         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

+ 23 - 21
modules/mono/csharp_script.cpp

@@ -49,6 +49,8 @@
 #include "mono_gd/gd_mono_class.h"
 #include "mono_gd/gd_mono_class.h"
 #include "mono_gd/gd_mono_marshal.h"
 #include "mono_gd/gd_mono_marshal.h"
 #include "signal_awaiter_utils.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)
 #define CACHED_STRING_NAME(m_var) (CSharpLanguage::get_singleton()->get_string_names().m_var)
 
 
@@ -474,7 +476,7 @@ String CSharpLanguage::_get_indentation() const {
 Vector<ScriptLanguage::StackInfo> CSharpLanguage::debug_get_current_stack_info() {
 Vector<ScriptLanguage::StackInfo> CSharpLanguage::debug_get_current_stack_info() {
 
 
 #ifdef DEBUG_ENABLED
 #ifdef DEBUG_ENABLED
-	// Printing an error here will result in endless recursion, so we must be careful
+	_TLS_RECURSION_GUARD_V_(Vector<StackInfo>());
 
 
 	if (!gdmono->is_runtime_initialized() || !GDMono::get_singleton()->get_core_api_assembly() || !GDMonoUtils::mono_cache.corlib_cache_updated)
 	if (!gdmono->is_runtime_initialized() || !GDMono::get_singleton()->get_core_api_assembly() || !GDMonoUtils::mono_cache.corlib_cache_updated)
 		return Vector<StackInfo>();
 		return Vector<StackInfo>();
@@ -498,15 +500,15 @@ Vector<ScriptLanguage::StackInfo> CSharpLanguage::debug_get_current_stack_info()
 #ifdef DEBUG_ENABLED
 #ifdef DEBUG_ENABLED
 Vector<ScriptLanguage::StackInfo> CSharpLanguage::stack_trace_get_info(MonoObject *p_stack_trace) {
 Vector<ScriptLanguage::StackInfo> 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<StackInfo>());
 
 
-	MonoObject *exc = NULL;
+	MonoException *exc = NULL;
 
 
 	GDMonoUtils::StackTrace_GetFrames st_get_frames = CACHED_METHOD_THUNK(System_Diagnostics_StackTrace, GetFrames);
 	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) {
 	if (exc) {
-		GDMonoUtils::print_unhandled_exception(exc, true /* fail silently to avoid endless recursion */);
+		GDMonoUtils::debug_print_unhandled_exception(exc);
 		return Vector<StackInfo>();
 		return Vector<StackInfo>();
 	}
 	}
 
 
@@ -527,10 +529,10 @@ Vector<ScriptLanguage::StackInfo> CSharpLanguage::stack_trace_get_info(MonoObjec
 		MonoString *file_name;
 		MonoString *file_name;
 		int file_line_num;
 		int file_line_num;
 		MonoString *method_decl;
 		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) {
 		if (exc) {
-			GDMonoUtils::print_unhandled_exception(exc, true /* fail silently to avoid endless recursion */);
+			GDMonoUtils::debug_print_unhandled_exception(exc);
 			return Vector<StackInfo>();
 			return Vector<StackInfo>();
 		}
 		}
 
 
@@ -559,12 +561,12 @@ void CSharpLanguage::frame() {
 
 
 			ERR_FAIL_NULL(thunk);
 			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_();
 			}
 			}
 		}
 		}
 	}
 	}
@@ -1076,11 +1078,11 @@ bool CSharpInstance::get(const StringName &p_name, Variant &r_ret) const {
 		GDMonoProperty *property = top->get_property(p_name);
 		GDMonoProperty *property = top->get_property(p_name);
 
 
 		if (property) {
 		if (property) {
-			MonoObject *exc = NULL;
+			MonoException *exc = NULL;
 			MonoObject *value = property->get_value(mono_object, &exc);
 			MonoObject *value = property->get_value(mono_object, &exc);
 			if (exc) {
 			if (exc) {
 				r_ret = Variant();
 				r_ret = Variant();
-				GDMonoUtils::print_unhandled_exception(exc);
+				GDMonoUtils::set_pending_exception(exc);
 			} else {
 			} else {
 				r_ret = GDMonoMarshal::mono_object_to_variant(value);
 				r_ret = GDMonoMarshal::mono_object_to_variant(value);
 			}
 			}
@@ -1488,12 +1490,12 @@ bool CSharpScript::_update_exports() {
 			CACHED_FIELD(GodotObject, ptr)->set_value_raw(tmp_object, tmp_object); // FIXME WTF is this workaround
 			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);
 			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:");
 				ERR_PRINT("Exception thrown from constructor of temporary MonoObject:");
-				mono_print_unhandled_exception(ex);
+				GDMonoUtils::debug_print_unhandled_exception(exc);
 				tmp_object = NULL;
 				tmp_object = NULL;
 				ERR_FAIL_V(false);
 				ERR_FAIL_V(false);
 			}
 			}
@@ -1542,11 +1544,11 @@ bool CSharpScript::_update_exports() {
 						exported_members_cache.push_front(prop_info);
 						exported_members_cache.push_front(prop_info);
 
 
 						if (tmp_object) {
 						if (tmp_object) {
-							MonoObject *exc = NULL;
+							MonoException *exc = NULL;
 							MonoObject *ret = property->get_value(tmp_object, &exc);
 							MonoObject *ret = property->get_value(tmp_object, &exc);
 							if (exc) {
 							if (exc) {
 								exported_members_defval_cache[name] = Variant();
 								exported_members_defval_cache[name] = Variant();
-								GDMonoUtils::print_unhandled_exception(exc);
+								GDMonoUtils::debug_print_unhandled_exception(exc);
 							} else {
 							} else {
 								exported_members_defval_cache[name] = GDMonoMarshal::mono_object_to_variant(ret);
 								exported_members_defval_cache[name] = GDMonoMarshal::mono_object_to_variant(ret);
 							}
 							}
@@ -1916,7 +1918,7 @@ CSharpInstance *CSharpScript::_create_instance(const Variant **p_args, int p_arg
 
 
 	// Construct
 	// Construct
 	GDMonoMethod *ctor = script_class->get_method(CACHED_STRING_NAME(dotctor), p_argcount);
 	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
 	// Tie managed to unmanaged
 	instance->gchandle = MonoGCHandle::create_strong(mono_object);
 	instance->gchandle = MonoGCHandle::create_strong(mono_object);

+ 16 - 16
modules/mono/editor/csharp_project.cpp

@@ -47,11 +47,11 @@ String generate_core_api_project(const String &p_dir, const Vector<String> &p_fi
 	Variant dir = p_dir;
 	Variant dir = p_dir;
 	Variant compile_items = p_files;
 	Variant compile_items = p_files;
 	const Variant *args[2] = { &dir, &compile_items };
 	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());
 		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 core_dll_path = p_core_dll_path;
 	Variant compile_items = p_files;
 	Variant compile_items = p_files;
 	const Variant *args[3] = { &dir, &core_dll_path, &compile_items };
 	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());
 		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 name = p_name;
 	Variant compile_items = p_files;
 	Variant compile_items = p_files;
 	const Variant *args[3] = { &dir, &name, &compile_items };
 	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());
 		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 item_type = p_item_type;
 	Variant include = p_include;
 	Variant include = p_include;
 	const Variant *args[3] = { &project_path, &item_type, &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();
 		ERR_FAIL();
 	}
 	}
 }
 }

+ 10 - 10
modules/mono/editor/godotsharp_builds.cpp

@@ -512,14 +512,14 @@ void GodotSharpBuilds::BuildProcess::start(bool p_blocking) {
 
 
 	const Variant *ctor_args[2] = { &solution, &config };
 	const Variant *ctor_args[2] = { &solution, &config };
 
 
-	MonoObject *ex = NULL;
+	MonoException *exc = NULL;
 	GDMonoMethod *ctor = klass->get_method(".ctor", 2);
 	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;
 		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);
 		build_tab->on_build_exec_failed(message);
 		ERR_EXPLAIN(message);
 		ERR_EXPLAIN(message);
 		ERR_FAIL();
 		ERR_FAIL();
@@ -534,14 +534,14 @@ void GodotSharpBuilds::BuildProcess::start(bool p_blocking) {
 
 
 	const Variant *args[3] = { &logger_assembly, &logger_output_dir, &custom_props };
 	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);
 	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;
 		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);
 		build_tab->on_build_exec_failed(message);
 		ERR_EXPLAIN(message);
 		ERR_EXPLAIN(message);
 		ERR_FAIL();
 		ERR_FAIL();

+ 8 - 8
modules/mono/editor/monodevelop_instance.cpp

@@ -40,14 +40,14 @@ void MonoDevelopInstance::execute(const Vector<String> &p_files) {
 	ERR_FAIL_NULL(execute_method);
 	ERR_FAIL_NULL(execute_method);
 	ERR_FAIL_COND(gc_handle.is_null());
 	ERR_FAIL_COND(gc_handle.is_null());
 
 
-	MonoObject *ex = NULL;
+	MonoException *exc = NULL;
 
 
 	Variant files = p_files;
 	Variant files = p_files;
 	const Variant *args[1] = { &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();
 		ERR_FAIL();
 	}
 	}
 }
 }
@@ -68,14 +68,14 @@ MonoDevelopInstance::MonoDevelopInstance(const String &p_solution) {
 	MonoObject *obj = mono_object_new(TOOLS_DOMAIN, klass->get_mono_ptr());
 	MonoObject *obj = mono_object_new(TOOLS_DOMAIN, klass->get_mono_ptr());
 
 
 	GDMonoMethod *ctor = klass->get_method(".ctor", 1);
 	GDMonoMethod *ctor = klass->get_method(".ctor", 1);
-	MonoObject *ex = NULL;
+	MonoException *exc = NULL;
 
 
 	Variant solution = p_solution;
 	Variant solution = p_solution;
 	const Variant *args[1] = { &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();
 		ERR_FAIL();
 	}
 	}
 
 

+ 2 - 5
modules/mono/mono_gc_handle.cpp

@@ -34,15 +34,12 @@
 
 
 uint32_t MonoGCHandle::make_strong_handle(MonoObject *p_object) {
 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) {
 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> MonoGCHandle::create_strong(MonoObject *p_object) {
 Ref<MonoGCHandle> MonoGCHandle::create_strong(MonoObject *p_object) {

+ 27 - 20
modules/mono/mono_gd/gd_mono.cpp

@@ -53,14 +53,6 @@
 #include "main/main.h"
 #include "main/main.h"
 #endif
 #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
 #ifdef MONO_PRINT_HANDLER_ENABLED
 void gdmono_MonoPrintCallback(const char *string, mono_bool is_stdout) {
 void gdmono_MonoPrintCallback(const char *string, mono_bool is_stdout) {
 
 
@@ -197,6 +189,8 @@ void GDMono::initialize() {
 
 
 	mono_config_parse(NULL);
 	mono_config_parse(NULL);
 
 
+	mono_install_unhandled_exception_hook(&unhandled_exception_hook, NULL);
+
 	root_domain = mono_jit_init_version("GodotEngine.RootDomain", "v4.0.30319");
 	root_domain = mono_jit_init_version("GodotEngine.RootDomain", "v4.0.30319");
 
 
 	ERR_EXPLAIN("Mono: Failed to initialize runtime");
 	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");
 		OS::get_singleton()->print("Mono: Glue disabled, ignoring script assemblies\n");
 #endif
 #endif
 
 
-	mono_install_unhandled_exception_hook(gdmono_unhandled_exception_hook, NULL);
-
 	OS::get_singleton()->print("Mono: INITIALIZED\n");
 	OS::get_singleton()->print("Mono: INITIALIZED\n");
 }
 }
 
 
@@ -652,12 +644,12 @@ Error GDMono::_unload_scripts_domain() {
 
 
 	_GodotSharp::get_singleton()->_dispose_callback();
 	_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;
 		return FAILED;
 	}
 	}
 
 
@@ -763,12 +755,12 @@ Error GDMono::finalize_and_unload_domain(MonoDomain *p_domain) {
 
 
 	_domain_assemblies_cleanup(mono_domain_get_id(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;
 		return FAILED;
 	}
 	}
 
 
@@ -811,6 +803,21 @@ void GDMono::_domain_assemblies_cleanup(uint32_t p_domain_id) {
 	assemblies.erase(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() {
 GDMono::GDMono() {
 
 
 	singleton = this;
 	singleton = this;

+ 2 - 0
modules/mono/mono_gd/gd_mono.h

@@ -164,6 +164,8 @@ public:
 
 
 	static GDMono *get_singleton() { return singleton; }
 	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
 	// Do not use these, unless you know what you're doing
 	void add_assembly(uint32_t p_domain_id, GDMonoAssembly *p_assembly);
 	void add_assembly(uint32_t p_domain_id, GDMonoAssembly *p_assembly);
 	GDMonoAssembly **get_loaded_assembly(const String &p_name);
 	GDMonoAssembly **get_loaded_assembly(const String &p_name);

+ 11 - 0
modules/mono/mono_gd/gd_mono_internals.cpp

@@ -32,8 +32,12 @@
 
 
 #include "../csharp_script.h"
 #include "../csharp_script.h"
 #include "../mono_gc_handle.h"
 #include "../mono_gc_handle.h"
+#include "../utils/macros.h"
+#include "../utils/thread_local.h"
 #include "gd_mono_utils.h"
 #include "gd_mono_utils.h"
 
 
+#include <mono/metadata/exception.h>
+
 namespace GDMonoInternals {
 namespace GDMonoInternals {
 
 
 void tie_managed_to_unmanaged(MonoObject *managed, Object *unmanaged) {
 void tie_managed_to_unmanaged(MonoObject *managed, Object *unmanaged) {
@@ -64,4 +68,11 @@ void tie_managed_to_unmanaged(MonoObject *managed, Object *unmanaged) {
 
 
 	return;
 	return;
 }
 }
+
+void unhandled_exception(MonoException *p_exc) {
+	mono_unhandled_exception((MonoObject *)p_exc); // prints the exception as well
+	abort();
+	_UNREACHABLE_();
+}
+
 } // namespace GDMonoInternals
 } // namespace GDMonoInternals

+ 10 - 1
modules/mono/mono_gd/gd_mono_internals.h

@@ -33,11 +33,20 @@
 
 
 #include <mono/jit/jit.h>
 #include <mono/jit/jit.h>
 
 
+#include "../utils/macros.h"
+
 #include "core/object.h"
 #include "core/object.h"
 
 
 namespace GDMonoInternals {
 namespace GDMonoInternals {
 
 
 void tie_managed_to_unmanaged(MonoObject *managed, Object *unmanaged);
 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
 #endif // GD_MONO_INTERNALS_H

+ 12 - 8
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);
 	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);
 		ERR_FAIL_V(NULL);
 	}
 	}
 
 
@@ -858,11 +860,13 @@ Dictionary mono_object_to_Dictionary(MonoObject *p_dict) {
 
 
 	MonoArray *keys = NULL;
 	MonoArray *keys = NULL;
 	MonoArray *values = 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());
 		ERR_FAIL_V(Dictionary());
 	}
 	}
 
 

+ 18 - 12
modules/mono/mono_gd/gd_mono_method.cpp

@@ -95,7 +95,7 @@ void *GDMonoMethod::get_thunk() {
 	return mono_method_get_unmanaged_thunk(mono_method);
 	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) {
 	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());
 		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);
 			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) {
 		if (exc) {
 			ret = NULL;
 			ret = NULL;
 			if (r_exc) {
 			if (r_exc) {
 				*r_exc = exc;
 				*r_exc = exc;
 			} else {
 			} else {
-				GDMonoUtils::print_unhandled_exception(exc);
+				GDMonoUtils::set_pending_exception(exc);
 			}
 			}
 		}
 		}
 
 
 		return ret;
 		return ret;
 	} else {
 	} 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 (exc) {
 			if (r_exc) {
 			if (r_exc) {
 				*r_exc = exc;
 				*r_exc = exc;
 			} else {
 			} 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);
 	ERR_FAIL_COND_V(get_parameters_count() > 0, NULL);
 	return invoke_raw(p_object, NULL, r_exc);
 	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) {
 	if (exc) {
 		ret = NULL;
 		ret = NULL;
 		if (r_exc) {
 		if (r_exc) {
 			*r_exc = exc;
 			*r_exc = exc;
 		} else {
 		} else {
-			GDMonoUtils::print_unhandled_exception(exc);
+			GDMonoUtils::set_pending_exception(exc);
 		}
 		}
 	}
 	}
 
 

+ 3 - 3
modules/mono/mono_gd/gd_mono_method.h

@@ -71,9 +71,9 @@ public:
 
 
 	void *get_thunk();
 	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(bool p_signature = false) const;
 	String get_full_name_no_class() const;
 	String get_full_name_no_class() const;

+ 18 - 12
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;
 	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);
 	MonoMethod *prop_method = mono_property_get_set_method(mono_property);
 
 
 	MonoArray *params = mono_array_new(mono_domain_get(), CACHED_CLASS_RAW(MonoObject), 1);
 	MonoArray *params = mono_array_new(mono_domain_get(), CACHED_CLASS_RAW(MonoObject), 1);
 	mono_array_set(params, MonoObject *, 0, p_value);
 	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 (exc) {
 		if (r_exc) {
 		if (r_exc) {
 			*r_exc = exc;
 			*r_exc = exc;
 		} else {
 		} 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 (exc) {
 		if (r_exc) {
 		if (r_exc) {
 			*r_exc = exc;
 			*r_exc = exc;
 		} else {
 		} 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) {
 	if (exc) {
 		ret = NULL;
 		ret = NULL;
 		if (r_exc) {
 		if (r_exc) {
 			*r_exc = exc;
 			*r_exc = exc;
 		} else {
 		} else {
-			GDMonoUtils::print_unhandled_exception(exc);
+			GDMonoUtils::set_pending_exception(exc);
 		}
 		}
 	}
 	}
 
 

+ 3 - 3
modules/mono/mono_gd/gd_mono_property.h

@@ -62,9 +62,9 @@ public:
 
 
 	_FORCE_INLINE_ ManagedType get_type() const { return type; }
 	_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);
 	bool get_bool_value(MonoObject *p_object);
 	int get_int_value(MonoObject *p_object);
 	int get_int_value(MonoObject *p_object);

+ 61 - 25
modules/mono/mono_gd/gd_mono_utils.cpp

@@ -30,12 +30,15 @@
 
 
 #include "gd_mono_utils.h"
 #include "gd_mono_utils.h"
 
 
+#include <mono/metadata/exception.h>
+
 #include "os/dir_access.h"
 #include "os/dir_access.h"
 #include "os/os.h"
 #include "os/os.h"
 #include "project_settings.h"
 #include "project_settings.h"
 #include "reference.h"
 #include "reference.h"
 
 
 #include "../csharp_script.h"
 #include "../csharp_script.h"
+#include "../utils/macros.h"
 #include "gd_mono.h"
 #include "gd_mono.h"
 #include "gd_mono_class.h"
 #include "gd_mono_class.h"
 #include "gd_mono_marshal.h"
 #include "gd_mono_marshal.h"
@@ -397,10 +400,10 @@ MonoDomain *create_domain(const String &p_friendly_name) {
 	return domain;
 	return domain;
 }
 }
 
 
-String get_exception_name_and_message(MonoObject *p_ex) {
+String get_exception_name_and_message(MonoException *p_ex) {
 	String res;
 	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);
 	MonoType *type = mono_class_get_type(klass);
 
 
 	char *full_name = mono_type_full_name(type);
 	char *full_name = mono_type_full_name(type);
@@ -410,29 +413,31 @@ String get_exception_name_and_message(MonoObject *p_ex) {
 	res += ": ";
 	res += ": ";
 
 
 	MonoProperty *prop = mono_class_get_property_from_name(klass, "Message");
 	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);
 	res += GDMonoMarshal::mono_string_to_godot(msg);
 
 
 	return res;
 	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
 #ifdef DEBUG_ENABLED
 	if (!ScriptDebugger::get_singleton())
 	if (!ScriptDebugger::get_singleton())
 		return;
 		return;
 
 
+	_TLS_RECURSION_GUARD_;
+
 	ScriptLanguage::StackInfo separator;
 	ScriptLanguage::StackInfo separator;
-	separator.file = "";
+	separator.file = String();
 	separator.func = "--- " + RTR("End of inner exception stack trace") + " ---";
 	separator.func = "--- " + RTR("End of inner exception stack trace") + " ---";
 	separator.line = 0;
 	separator.line = 0;
 
 
 	Vector<ScriptLanguage::StackInfo> si;
 	Vector<ScriptLanguage::StackInfo> si;
-	String exc_msg = "";
+	String exc_msg;
 
 
 	while (p_exc != NULL) {
 	while (p_exc != NULL) {
 		GDMonoClass *st_klass = CACHED_CLASS(System_Diagnostics_StackTrace);
 		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;
 		MonoBoolean need_file_info = true;
 		void *ctor_args[2] = { p_exc, &need_file_info };
 		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);
 		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<ScriptLanguage::StackInfo> _si;
 		Vector<ScriptLanguage::StackInfo> _si;
-		if (stack_trace != NULL && !p_recursion_caution) {
+		if (stack_trace != NULL) {
 			_si = CSharpLanguage::get_singleton()->stack_trace_get_info(stack_trace);
 			_si = CSharpLanguage::get_singleton()->stack_trace_get_info(stack_trace);
 			for (int i = _si.size() - 1; i >= 0; i--)
 			for (int i = _si.size() - 1; i >= 0; i--)
 				si.insert(0, _si[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);
 		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);
 			si.insert(0, separator);
+
+		p_exc = (MonoException *)inner_exc;
 	}
 	}
 
 
 	String file = si.size() ? si[0].file : __FILE__;
 	String file = si.size() ? si[0].file : __FILE__;
@@ -481,4 +483,38 @@ void print_unhandled_exception(MonoObject *p_exc, bool p_recursion_caution) {
 #endif
 #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
 } // namespace GDMonoUtils

+ 30 - 3
modules/mono/mono_gd/gd_mono_utils.h

@@ -34,6 +34,8 @@
 #include <mono/metadata/threads.h>
 #include <mono/metadata/threads.h>
 
 
 #include "../mono_gc_handle.h"
 #include "../mono_gc_handle.h"
+#include "../utils/macros.h"
+#include "../utils/thread_local.h"
 #include "gd_mono_header.h"
 #include "gd_mono_header.h"
 
 
 #include "object.h"
 #include "object.h"
@@ -184,10 +186,28 @@ MonoObject *create_managed_from(const RID &p_from);
 
 
 MonoDomain *create_domain(const String &p_friendly_name);
 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
 } // 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)
 #define REAL_T_MONOCLASS CACHED_CLASS_RAW(float)
 #endif
 #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
 #endif // GD_MONOUTILS_H

+ 12 - 8
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);
 	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());
 		ERR_FAIL_V(Variant());
 	}
 	}
 
 
@@ -133,11 +135,13 @@ SignalAwaiterHandle::~SignalAwaiterHandle() {
 		MonoObject *awaiter = get_target();
 		MonoObject *awaiter = get_target();
 
 
 		if (awaiter) {
 		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();
 				ERR_FAIL_V();
 			}
 			}
 		}
 		}

+ 36 - 0
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'])

+ 59 - 0
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

+ 99 - 0
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 <windows.h>
+#else
+#include <pthread.h>
+#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;
+}

+ 171 - 0
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<m_t>
+#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 <typename T>
+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<T *>(tls_data));
+	}
+
+#undef _CALLBACK_FUNC_
+
+	T *_tls_get_value() const {
+		void *tls_data = storage.get_value();
+
+		if (tls_data)
+			return static_cast<T *>(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