Skip to content

Commit

Permalink
[One .NET] Remove as much AppDomain use as possible (#6071)
Browse files Browse the repository at this point in the history
Context: b7224e5

.NET 5+ no longer supports multiple AppDomains, there's always only a
single "root" AppDomain.  This allows us to drop bits of code which
deal with obtaining and setting the current AppDomain in various
contexts.

Removes almost all uses of AppDomains from the native Xamarin.Android
runtime for .NET 6+.  Unfortunately, it requires sometimes messy use
of the C++ preprocessor, but wherever it is possible and not overly
ugly, I try to separate the NET6 and "legacy" code completely, for
easier and less noisy future removal of the latter.
  • Loading branch information
grendello committed Jul 6, 2021
1 parent d80c9b1 commit 7282da9
Show file tree
Hide file tree
Showing 8 changed files with 149 additions and 39 deletions.
22 changes: 17 additions & 5 deletions src/monodroid/jni/monodroid-glue-internal.hh
Expand Up @@ -110,9 +110,6 @@ namespace xamarin::android::internal
};

private:
static constexpr char MONO_ANDROID_ASSEMBLY_NAME[] = "Mono.Android";
static constexpr char JAVA_INTEROP_ASSEMBLY_NAME[] = "Java.Interop";

static constexpr size_t SMALL_STRING_PARSE_BUFFER_LEN = 50;
static constexpr bool is_running_on_desktop =
#if ANDROID
Expand Down Expand Up @@ -176,7 +173,11 @@ namespace xamarin::android::internal
return counters;
}

#if defined (NET6)
void propagate_uncaught_exception (JNIEnv *env, jobject javaThread, jthrowable javaException);
#else // def NET6
void propagate_uncaught_exception (MonoDomain *domain, JNIEnv *env, jobject javaThread, jthrowable javaException);
#endif // ndef NET6

// The reason we don't use the C++ overload feature here is that there appears to be an issue in clang++ that
// comes with the Android NDK. The issue is that for calls like:
Expand Down Expand Up @@ -226,7 +227,12 @@ namespace xamarin::android::internal
void create_xdg_directory (jstring_wrapper& home, size_t home_len, const char *relativePath, size_t relative_path_len, const char *environmentVariableName);
void create_xdg_directories_and_environment (jstring_wrapper &homeDir);
void disable_external_signal_handlers ();
void lookup_bridge_info (MonoClass *klass, const OSBridge::MonoJavaGCBridgeType *type, OSBridge::MonoJavaGCBridgeInfo *info);
#if defined (NET6)
void lookup_bridge_info (MonoImage *image, const OSBridge::MonoJavaGCBridgeType *type, OSBridge::MonoJavaGCBridgeInfo *info);
#else // def NET6
void lookup_bridge_info (MonoDomain *domain, MonoImage *image, const OSBridge::MonoJavaGCBridgeType *type, OSBridge::MonoJavaGCBridgeInfo *info);
#endif // ndef NET6
void load_assembly (MonoDomain *domain, jstring_wrapper &assembly);
#if defined (NET6)
void load_assembly (MonoAssemblyLoadContextGCHandle alc_handle, jstring_wrapper &assembly);
Expand All @@ -236,10 +242,12 @@ namespace xamarin::android::internal
void set_debug_options ();
void parse_gdb_options ();
void mono_runtime_init (dynamic_local_string<PROPERTY_VALUE_BUFFER_LEN>& runtime_args);
#if !defined (NET6)
#if defined (NET6)
void init_android_runtime (JNIEnv *env, jclass runtimeClass, jobject loader);
#else //def NET6
void init_android_runtime (MonoDomain *domain, JNIEnv *env, jclass runtimeClass, jobject loader);
void setup_bundled_app (const char *dso_name);
#endif // ndef NET6
void init_android_runtime (MonoDomain *domain, JNIEnv *env, jclass runtimeClass, jobject loader);
void set_environment_variable_for_directory (const char *name, jstring_wrapper &value, bool createDirectory, mode_t mode);

void set_environment_variable_for_directory (const char *name, jstring_wrapper &value)
Expand All @@ -252,7 +260,11 @@ namespace xamarin::android::internal
set_environment_variable_for_directory (name, value, false, 0);
}

#if defined (NET6)
MonoClass* get_android_runtime_class ();
#else // def NET6
MonoClass* get_android_runtime_class (MonoDomain *domain);
#endif
MonoDomain* create_domain (JNIEnv *env, jstring_array_wrapper &runtimeApks, bool is_root_domain);
MonoDomain* create_and_initialize_domain (JNIEnv* env, jclass runtimeClass, jstring_array_wrapper &runtimeApks,
jstring_array_wrapper &assemblies, jobjectArray assembliesBytes, jstring_array_wrapper &assembliesPaths,
Expand Down
132 changes: 103 additions & 29 deletions src/monodroid/jni/monodroid-glue.cc
Expand Up @@ -958,10 +958,10 @@ MonodroidRuntime::LocalRefsAreIndirect (JNIEnv *env, jclass runtimeClass, int ve
return 1;
}

inline void
MonodroidRuntime::lookup_bridge_info (MonoDomain *domain, MonoImage *image, const OSBridge::MonoJavaGCBridgeType *type, OSBridge::MonoJavaGCBridgeInfo *info)
force_inline void
MonodroidRuntime::lookup_bridge_info (MonoClass *klass, const OSBridge::MonoJavaGCBridgeType *type, OSBridge::MonoJavaGCBridgeInfo *info)
{
info->klass = utils.monodroid_get_class_from_image (domain, image, type->_namespace, type->_typename);
info->klass = klass;
info->handle = mono_class_get_field_from_name (info->klass, const_cast<char*> ("handle"));
info->handle_type = mono_class_get_field_from_name (info->klass, const_cast<char*> ("handle_type"));
info->refs_added = mono_class_get_field_from_name (info->klass, const_cast<char*> ("refs_added"));
Expand All @@ -974,12 +974,38 @@ MonodroidRuntime::lookup_bridge_info (MonoDomain *domain, MonoImage *image, cons
info->handle_type,
info->refs_added,
info->weak_handle);
exit (FATAL_EXIT_MONO_MISSING_SYMBOLS);
abort ();
}
}

#if defined (NET6)
force_inline void
MonodroidRuntime::lookup_bridge_info (MonoImage *image, const OSBridge::MonoJavaGCBridgeType *type, OSBridge::MonoJavaGCBridgeInfo *info)
{
lookup_bridge_info (
mono_class_from_name (image, type->_namespace, type->_typename),
type,
info
);
}
#else // def NET6
force_inline void
MonodroidRuntime::lookup_bridge_info (MonoDomain *domain, MonoImage *image, const OSBridge::MonoJavaGCBridgeType *type, OSBridge::MonoJavaGCBridgeInfo *info)
{
lookup_bridge_info (
utils.monodroid_get_class_from_image (domain, image, type->_namespace, type->_typename),
type,
info
);
}
#endif // ndef NET6

void
MonodroidRuntime::init_android_runtime (MonoDomain *domain, JNIEnv *env, jclass runtimeClass, jobject loader)
MonodroidRuntime::init_android_runtime (
#if !defined (NET6)
MonoDomain *domain,
#endif // ndef NET6
JNIEnv *env, jclass runtimeClass, jobject loader)
{
mono_add_internal_call ("Java.Interop.TypeManager::monodroid_typemap_java_to_managed", reinterpret_cast<const void*>(typemap_java_to_managed));
mono_add_internal_call ("Android.Runtime.JNIEnv::monodroid_typemap_managed_to_java", reinterpret_cast<const void*>(typemap_managed_to_java));
Expand Down Expand Up @@ -1008,20 +1034,32 @@ MonodroidRuntime::init_android_runtime (MonoDomain *domain, JNIEnv *env, jclass

MonoAssembly *assm;
#if defined (NET6)
assm = utils.monodroid_load_assembly (default_alc, MONO_ANDROID_ASSEMBLY_NAME);
assm = utils.monodroid_load_assembly (default_alc, SharedConstants::MONO_ANDROID_ASSEMBLY_NAME);
#else // def NET6
assm = utils.monodroid_load_assembly (domain, MONO_ANDROID_ASSEMBLY_NAME);
assm = utils.monodroid_load_assembly (domain, SharedConstants::MONO_ANDROID_ASSEMBLY_NAME);
#endif // ndef NET6
MonoImage *image = mono_assembly_get_image (assm);

uint32_t i = 0;

for ( ; i < OSBridge::NUM_XA_GC_BRIDGE_TYPES; ++i) {
lookup_bridge_info (domain, image, &osBridge.get_java_gc_bridge_type (i), &osBridge.get_java_gc_bridge_info (i));
lookup_bridge_info (
#if !defined (NET6)
domain,
#endif // ndef NET6
image,
&osBridge.get_java_gc_bridge_type (i),
&osBridge.get_java_gc_bridge_info (i)
);
}

// TODO: try looking up the method by its token
MonoClass *runtime = utils.monodroid_get_class_from_image (domain, image, "Android.Runtime", "JNIEnv");
MonoClass *runtime;
#if defined (NET6)
runtime = mono_class_from_name (image, SharedConstants::ANDROID_RUNTIME_NS_NAME, SharedConstants::JNIENV_CLASS_NAME);
#else
runtime = utils.monodroid_get_class_from_image (domain, image, SharedConstants::ANDROID_RUNTIME_NS_NAME, SharedConstants::JNIENV_CLASS_NAME);
#endif
MonoMethod *method = mono_class_get_method_from_name (runtime, "Initialize", 1);

if (method == nullptr) {
Expand All @@ -1031,14 +1069,21 @@ MonodroidRuntime::init_android_runtime (MonoDomain *domain, JNIEnv *env, jclass

MonoAssembly *ji_assm;
#if defined (NET6)
ji_assm = utils.monodroid_load_assembly (default_alc, JAVA_INTEROP_ASSEMBLY_NAME);
ji_assm = utils.monodroid_load_assembly (default_alc, SharedConstants::JAVA_INTEROP_ASSEMBLY_NAME);
#else // def NET6
ji_assm = utils.monodroid_load_assembly (domain, JAVA_INTEROP_ASSEMBLY_NAME);
ji_assm = utils.monodroid_load_assembly (domain, SharedConstants::JAVA_INTEROP_ASSEMBLY_NAME);
#endif // ndef NET6

MonoImage *ji_image = mono_assembly_get_image (ji_assm);
for ( ; i < OSBridge::NUM_XA_GC_BRIDGE_TYPES + OSBridge::NUM_JI_GC_BRIDGE_TYPES; ++i) {
lookup_bridge_info (domain, ji_image, &osBridge.get_java_gc_bridge_type (i), &osBridge.get_java_gc_bridge_info (i));
lookup_bridge_info (
#if !defined (NET6)
domain,
#endif // ndef NET6
ji_image,
&osBridge.get_java_gc_bridge_type (i),
&osBridge.get_java_gc_bridge_info (i)
);
}

/* If running on desktop, we may be swapping in a new Mono.Android image when calling this
Expand Down Expand Up @@ -1076,41 +1121,61 @@ MonodroidRuntime::init_android_runtime (MonoDomain *domain, JNIEnv *env, jclass
void *args [] = {
&init,
};
#if defined (NET6)
mono_runtime_invoke (method, nullptr, args, nullptr);
#else // def NET6
utils.monodroid_runtime_invoke (domain, method, nullptr, args, nullptr);
#endif // ndef NET6

if (XA_UNLIKELY (utils.should_log (LOG_TIMING))) {
partial_time.mark_end ();
Timing::info (partial_time, "Runtime.init: end native-to-managed transition");
}
}

#if defined (NET6)
MonoClass*
MonodroidRuntime::get_android_runtime_class (MonoDomain *domain)
MonodroidRuntime::get_android_runtime_class ()
{
MonoAssembly *assm;
#if defined (NET6)
assm = utils.monodroid_load_assembly (default_alc, MONO_ANDROID_ASSEMBLY_NAME);
MonoAssembly *assm = utils.monodroid_load_assembly (default_alc, SharedConstants::MONO_ANDROID_ASSEMBLY_NAME);
MonoImage *image = mono_assembly_get_image (assm);
return mono_class_from_name (image, SharedConstants::ANDROID_RUNTIME_NS_NAME, SharedConstants::JNIENV_CLASS_NAME);
}
#else // def NET6
assm = utils.monodroid_load_assembly (domain, MONO_ANDROID_ASSEMBLY_NAME);
#endif // ndef NET6
MonoClass*
MonodroidRuntime::get_android_runtime_class (MonoDomain *domain)
{
MonoAssembly *assm = utils.monodroid_load_assembly (domain, SharedConstants::MONO_ANDROID_ASSEMBLY_NAME);
MonoImage *image = mono_assembly_get_image (assm);
MonoClass *runtime = utils.monodroid_get_class_from_image (domain, image, "Android.Runtime", "JNIEnv");

return runtime;
return utils.monodroid_get_class_from_image (domain, image, SharedConstants::ANDROID_RUNTIME_NS_NAME, SharedConstants::JNIENV_CLASS_NAME);
}
#endif // ndef NET6

inline void
MonodroidRuntime::propagate_uncaught_exception (MonoDomain *domain, JNIEnv *env, jobject javaThread, jthrowable javaException)
MonodroidRuntime::propagate_uncaught_exception (
#if !defined (NET6)
MonoDomain *domain,
#endif // ndef NET6
JNIEnv *env, jobject javaThread, jthrowable javaException)
{
MonoClass *runtime = get_android_runtime_class (domain);
MonoClass *runtime;
#if defined (NET6)
runtime = get_android_runtime_class ();
#else
runtime = get_android_runtime_class (domain);
#endif
MonoMethod *method = mono_class_get_method_from_name (runtime, "PropagateUncaughtException", 3);

void* args[] = {
&env,
&javaThread,
&javaException,
};
#if defined (NET6)
mono_runtime_invoke (method, nullptr, args, nullptr);
#else // def NET6
utils.monodroid_runtime_invoke (domain, method, nullptr, args, nullptr);
#endif // ndef NET6
}

#if DEBUG
Expand Down Expand Up @@ -1750,12 +1815,12 @@ MonodroidRuntime::create_and_initialize_domain (JNIEnv* env, jclass runtimeClass
bool preload = (androidSystem.is_assembly_preload_enabled () || (is_running_on_desktop && force_preload_assemblies));

#if defined (NET6)
load_assemblies (default_alc, preload, assemblies);
load_assemblies (default_alc, preload, assemblies);
init_android_runtime (env, runtimeClass, loader);
#else // def NET6
load_assemblies (domain, preload, assemblies);
#endif // ndef NET6
load_assemblies (domain, preload, assemblies);
init_android_runtime (domain, env, runtimeClass, loader);

#endif // ndef NET6
osBridge.add_monodroid_domain (domain);

return domain;
Expand Down Expand Up @@ -2246,17 +2311,22 @@ MonodroidRuntime::Java_mono_android_Runtime_register (JNIEnv *env, jstring manag
&methods_len,
};

MonoMethod *register_jni_natives = registerType;
#if !defined (NET6)
MonoDomain *domain = mono_domain_get ();
mono_jit_thread_attach (domain);
// Refresh current domain as it might have been modified by the above call
domain = mono_domain_get ();

MonoMethod *register_jni_natives = registerType;
if constexpr (is_running_on_desktop) {
MonoClass *runtime = utils.monodroid_get_class_from_name (domain, MONO_ANDROID_ASSEMBLY_NAME, "Android.Runtime", "JNIEnv");
MonoClass *runtime = utils.monodroid_get_class_from_name (domain, SharedConstants::MONO_ANDROID_ASSEMBLY_NAME, SharedConstants::ANDROID_RUNTIME_NS_NAME, SharedConstants::JNIENV_CLASS_NAME);
register_jni_natives = mono_class_get_method_from_name (runtime, "RegisterJniNatives", 5);
}

utils.monodroid_runtime_invoke (domain, register_jni_natives, nullptr, args, nullptr);
#else // ndef NET6
mono_runtime_invoke (register_jni_natives, nullptr, args, nullptr);
#endif // def NET6

env->ReleaseStringChars (methods, methods_ptr);
env->ReleaseStringChars (managedType, managedType_ptr);
Expand Down Expand Up @@ -2318,6 +2388,10 @@ get_jnienv (void)
JNIEXPORT void
JNICALL Java_mono_android_Runtime_propagateUncaughtException (JNIEnv *env, [[maybe_unused]] jclass klass, jobject javaThread, jthrowable javaException)
{
#if defined (NET6)
monodroidRuntime.propagate_uncaught_exception (env, javaThread, javaException);
#else // def NET6
MonoDomain *domain = mono_domain_get ();
monodroidRuntime.propagate_uncaught_exception (domain, env, javaThread, javaException);
#endif // ndef NET6
}
4 changes: 3 additions & 1 deletion src/monodroid/jni/osbridge.cc
Expand Up @@ -1116,7 +1116,7 @@ OSBridge::add_monodroid_domain (MonoDomain *domain)
* use GC API to allocate memory and thus can't be called from within the GC callback as it causes a deadlock
* (the routine allocating the memory waits for the GC round to complete first)
*/
MonoClass *jnienv = utils.monodroid_get_class_from_name (domain, "Mono.Android", "Android.Runtime", "JNIEnv");;
MonoClass *jnienv = utils.monodroid_get_class_from_name (domain, SharedConstants::MONO_ANDROID_ASSEMBLY_NAME, SharedConstants::ANDROID_RUNTIME_NS_NAME, SharedConstants::JNIENV_CLASS_NAME);;
node->domain = domain;
node->bridge_processing_field = mono_class_get_field_from_name (jnienv, const_cast<char*> ("BridgeProcessing"));
node->jnienv_vtable = mono_class_vtable (domain, jnienv);
Expand All @@ -1125,6 +1125,7 @@ OSBridge::add_monodroid_domain (MonoDomain *domain)
domains_list = node;
}

#if !defined (NET6) && !defined (ANDROID)
void
OSBridge::remove_monodroid_domain (MonoDomain *domain)
{
Expand Down Expand Up @@ -1164,3 +1165,4 @@ OSBridge::on_destroy_contexts ()
if (!domains_list)
osBridge.clear_mono_java_gc_bridge_info ();
}
#endif // ndef NET6 && ndef ANDROID
2 changes: 2 additions & 0 deletions src/monodroid/jni/osbridge.hh
Expand Up @@ -117,7 +117,9 @@ namespace xamarin::android::internal
void initialize_on_onload (JavaVM *vm, JNIEnv *env);
void initialize_on_runtime_init (JNIEnv *env, jclass runtimeClass);
void add_monodroid_domain (MonoDomain *domain);
#if !defined (NET6)
void remove_monodroid_domain (MonoDomain *domain);
#endif // ndef NET6
void on_destroy_contexts ();

private:
Expand Down
7 changes: 7 additions & 0 deletions src/monodroid/jni/shared-constants.hh
Expand Up @@ -15,6 +15,13 @@ namespace xamarin::android::internal
class SharedConstants
{
public:
static constexpr char MONO_ANDROID_ASSEMBLY_NAME[] = "Mono.Android";
static constexpr char JAVA_INTEROP_ASSEMBLY_NAME[] = "Java.Interop";

static constexpr char ANDROID_RUNTIME_NS_NAME[] = "Android.Runtime";
static constexpr char JNIENV_CLASS_NAME[] = "JNIEnv";
static constexpr char ANDROID_ENVIRONMENT_CLASS_NAME[] = "AndroidEnvironment";

#if defined (NET6)
static constexpr char RUNTIME_CONFIG_BLOB_NAME[] = "rc.bin";
#endif // def NET6
Expand Down
4 changes: 2 additions & 2 deletions src/monodroid/jni/timezones.cc
Expand Up @@ -33,9 +33,9 @@ init ()
if (AndroidEnvironment_NotifyTimeZoneChanged)
return;

Mono_Android_dll = utils.monodroid_load_assembly (mono_domain_get (), "Mono.Android");
Mono_Android_dll = utils.monodroid_load_assembly (mono_domain_get (), SharedConstants::MONO_ANDROID_ASSEMBLY_NAME);
Mono_Android_image = mono_assembly_get_image (Mono_Android_dll);
AndroidEnvironment = mono_class_from_name (Mono_Android_image, "Android.Runtime", "AndroidEnvironment");
AndroidEnvironment = mono_class_from_name (Mono_Android_image, SharedConstants::ANDROID_RUNTIME_NS_NAME, SharedConstants::ANDROID_ENVIRONMENT_CLASS_NAME);
AndroidEnvironment_NotifyTimeZoneChanged = mono_class_get_method_from_name (AndroidEnvironment, "NotifyTimeZoneChanged", 0);

if (AndroidEnvironment_NotifyTimeZoneChanged == nullptr) {
Expand Down

0 comments on commit 7282da9

Please sign in to comment.