Mono: Pending exceptions and cleanup

(cherry picked from commit 4739cb8c00)
This commit is contained in:
Ignacio Etcheverry 2018-06-26 21:03:42 +02:00 committed by Hein-Pieter van Braam
parent 291be24742
commit e1cf8dc2cb
23 changed files with 738 additions and 171 deletions

View file

@ -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
@ -127,6 +136,21 @@ def find_msbuild_windows():
if not mono_root:
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'),
'FscToolExe': os.path.join(mono_bin_dir, 'fsharpc.bat')
}
return (msbuild_mono, framework_path, mono_msbuild_env)
msbuild_tools_path = monoreg.find_msbuild_tools_path_reg()
if msbuild_tools_path:

View file

@ -5,6 +5,8 @@ import sys
import subprocess
from SCons.Script import BoolVariable, Dir, Environment, PathVariable, Variables
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')
@ -20,7 +22,7 @@ def find_file_in_dir(directory, files, prefix='', extension=''):
def can_build(platform):
if platform in ["javascript"]:
if platform in ['javascript']:
return False # Not yet supported
return True
@ -30,6 +32,27 @@ def is_enabled():
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):
from shutil import copyfile
@ -44,7 +67,7 @@ def copy_file(src_dir, dst_dir, name):
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))
@ -73,6 +96,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)
@ -135,7 +161,17 @@ def configure(env):
if os.getenv('MONO64_PREFIX'):
mono_root = os.getenv('MONO64_PREFIX')
# 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:
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)
@ -151,18 +187,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:
@ -178,11 +214,14 @@ def configure(env):
if mono_static:
raise RuntimeError('mono-static: Not supported with pkg-config. Specify a mono prefix manually')
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"]).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'))
@ -204,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

View file

@ -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<ScriptLanguage::StackInfo> 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<StackInfo>());
if (!gdmono->is_runtime_initialized() || !GDMono::get_singleton()->get_core_api_assembly() || !GDMonoUtils::mono_cache.corlib_cache_updated)
return Vector<StackInfo>();
@ -500,15 +502,15 @@ Vector<ScriptLanguage::StackInfo> CSharpLanguage::debug_get_current_stack_info()
#ifdef DEBUG_ENABLED
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);
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<StackInfo>();
}
@ -529,10 +531,10 @@ Vector<ScriptLanguage::StackInfo> 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<StackInfo>();
}
@ -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_();
}
}
}
@ -1077,11 +1079,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);
}
@ -1483,12 +1485,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);
}
@ -1537,11 +1539,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);
}
@ -1903,7 +1905,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);

View file

@ -47,11 +47,11 @@ String generate_core_api_project(const String &p_dir, const Vector<String> &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();
}
}

View file

@ -513,14 +513,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();
@ -535,14 +535,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();

View file

@ -40,14 +40,14 @@ void MonoDevelopInstance::execute(const Vector<String> &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();
}

View file

@ -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> MonoGCHandle::create_strong(MonoObject *p_object) {

View file

@ -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;

View file

@ -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);

View file

@ -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 <mono/metadata/exception.h>
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

View file

@ -33,11 +33,20 @@
#include <mono/jit/jit.h>
#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

View file

@ -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());
}

View file

@ -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);
}
}

View file

@ -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;

View file

@ -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);
}
}

View file

@ -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);

View file

@ -30,12 +30,15 @@
#include "gd_mono_utils.h"
#include <mono/metadata/exception.h>
#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"
@ -391,10 +394,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);
@ -404,29 +407,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<ScriptLanguage::StackInfo> si;
String exc_msg = "";
String exc_msg;
while (p_exc != NULL) {
GDMonoClass *st_klass = CACHED_CLASS(System_Diagnostics_StackTrace);
@ -435,24 +440,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<ScriptLanguage::StackInfo> _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]);
@ -460,10 +457,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__;
@ -475,4 +477,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

View file

@ -34,6 +34,8 @@
#include <mono/metadata/threads.h>
#include "../mono_gc_handle.h"
#include "../utils/macros.h"
#include "../utils/thread_local.h"
#include "gd_mono_header.h"
#include "object.h"
@ -181,10 +183,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
@ -203,4 +223,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

View file

@ -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();
}
}

View file

@ -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'])

View file

@ -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

View file

@ -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;
}

View file

@ -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