Skip to content

Commit 853fd1b

Browse files
committed
[NET6] Enable support for AssemblyLoadContext
Context: https://docs.microsoft.com/en-us/dotnet/api/system.runtime.loader.assemblyloadcontext?view=net-5.0 dotnet 6 removes support for AppDomains (technically, there still exists a single AppDomain, but creation of new ones is no longer possible), replacing them (in a way) with AssemblyLoadContext, which enables scoped loading of managed assemblies. Part of the change is the new set of MonoVM functions we need to call in order to load an assembly into either the default AssemblyLoadContext (early in the startup process) or into the application-created context later on during application run time. MonoVM also adds new preload hooks which work with the `ALC` instead of the older AppDomains. This commit adds support for `ALC` preload callbacks and the new assembly load functions.
1 parent d593b72 commit 853fd1b

File tree

6 files changed

+165
-77
lines changed

6 files changed

+165
-77
lines changed

src/monodroid/jni/embedded-assemblies.cc

Lines changed: 61 additions & 51 deletions
Original file line numberDiff line numberDiff line change
@@ -130,21 +130,45 @@ EmbeddedAssemblies::get_assembly_data (const MonoBundledAssembly *e, char*& asse
130130
}
131131
}
132132

133+
#if defined (NET6)
133134
MonoAssembly*
134-
#if defined (NET6) && defined (NET6_ALC_WORKS)
135-
EmbeddedAssemblies::open_from_bundles (MonoAssemblyName* aname, MonoAssemblyLoadContextGCHandle alc_gchandle, MonoError *error)
136-
#else // !(def NET6 && def NET6_ALC_WORKS)
137-
EmbeddedAssemblies::open_from_bundles (MonoAssemblyName* aname, bool ref_only)
138-
#endif // def NET6 && def NET6_ALC_WORKS
135+
EmbeddedAssemblies::open_from_bundles (MonoAssemblyName* aname, MonoAssemblyLoadContextGCHandle alc_gchandle, [[maybe_unused]] MonoError *error)
139136
{
140-
#if defined (NET6)
141-
// NET6 doesn't support reference-only loads, define the variable here to minimize ifdefs in the code below
142-
#if defined (NET6_ALC_WORKS)
143-
constexpr bool ref_only = false;
144-
#else // def NET6_ALC_WORKS
145-
ref_only = false;
146-
#endif // ndef NET6_ALC_WORKS
137+
auto loader = [&] (char *assembly_data, uint32_t assembly_data_size, const char *name) -> MonoImage* {
138+
return mono_image_open_from_data_alc (
139+
alc_gchandle,
140+
assembly_data,
141+
assembly_data_size,
142+
0 /* need_copy */,
143+
nullptr /* status */,
144+
name
145+
);
146+
};
147+
148+
return open_from_bundles (aname, loader, false /* ref_only */);
149+
}
147150
#endif // def NET6
151+
152+
MonoAssembly*
153+
EmbeddedAssemblies::open_from_bundles (MonoAssemblyName* aname, bool ref_only)
154+
{
155+
auto loader = [&] (char *assembly_data, uint32_t assembly_data_size, const char *name) -> MonoImage* {
156+
return mono_image_open_from_data_with_name (
157+
assembly_data,
158+
assembly_data_size,
159+
0,
160+
nullptr,
161+
ref_only,
162+
name
163+
);
164+
};
165+
166+
return open_from_bundles (aname, loader, ref_only);
167+
}
168+
169+
MonoAssembly*
170+
EmbeddedAssemblies::open_from_bundles (MonoAssemblyName* aname, std::function<MonoImage*(char*, uint32_t, const char*)> loader, bool ref_only)
171+
{
148172
const char *culture = mono_assembly_name_get_culture (aname);
149173
const char *asmname = mono_assembly_name_get_name (aname);
150174

@@ -185,19 +209,7 @@ EmbeddedAssemblies::open_from_bundles (MonoAssemblyName* aname, bool ref_only)
185209
}
186210

187211
get_assembly_data (e, assembly_data, assembly_data_size);
188-
189-
#if defined (NET6) && defined (NET6_ALC_WORKS)
190-
image = mono_image_open_from_data_alc (
191-
alc_gchandle,
192-
assembly_data,
193-
assembly_data_size,
194-
0 /* need_copy */,
195-
nullptr /* status */,
196-
name.get ()
197-
);
198-
#else // (def NET6 && def NET6_ALC_WORKS)
199-
image = mono_image_open_from_data_with_name (assembly_data, assembly_data_size, 0, nullptr, ref_only, name.get ());
200-
#endif // !(def NET6 && def NET6_ALC_WORKS)
212+
image = loader (assembly_data, assembly_data_size, name.get ());
201213
if (image == nullptr) {
202214
continue;
203215
}
@@ -215,50 +227,48 @@ EmbeddedAssemblies::open_from_bundles (MonoAssemblyName* aname, bool ref_only)
215227
log_info_nocheck (LOG_ASSEMBLY, "open_from_bundles: loaded assembly: %p\n", a);
216228
}
217229

218-
#if defined (NET6) && defined (NET6_ALC_WORKS)
219-
if (error != nullptr) {
220-
error->error_code = a == nullptr ? MONO_ERROR_NONE : MONO_ERROR_FILE_NOT_FOUND;
221-
}
222-
#endif // def NET6 && def NET6_ALC_WORKS
223230
return a;
224231
}
225232

226-
#if defined (NET6) && defined (NET6_ALC_WORKS)
233+
#if defined (NET6)
227234
MonoAssembly*
228235
EmbeddedAssemblies::open_from_bundles (MonoAssemblyLoadContextGCHandle alc_gchandle, MonoAssemblyName *aname, [[maybe_unused]] char **assemblies_path, [[maybe_unused]] void *user_data, MonoError *error)
229236
{
230-
log_warn (LOG_DEFAULT, __PRETTY_FUNCTION__);
231237
return embeddedAssemblies.open_from_bundles (aname, alc_gchandle, error);
232238
}
233-
#else // def NET6 && def NET6_ALC_WORKS
239+
#else // def NET6
234240
MonoAssembly*
235-
EmbeddedAssemblies::open_from_bundles_full (MonoAssemblyName *aname, [[maybe_unused]] char **assemblies_path, [[maybe_unused]] void *user_data)
241+
EmbeddedAssemblies::open_from_bundles_refonly (MonoAssemblyName *aname, [[maybe_unused]] char **assemblies_path, [[maybe_unused]] void *user_data)
236242
{
237-
return embeddedAssemblies.open_from_bundles (aname, false);
243+
return embeddedAssemblies.open_from_bundles (aname, true);
238244
}
245+
#endif // ndef NET6
239246

240247
MonoAssembly*
241-
EmbeddedAssemblies::open_from_bundles_refonly (MonoAssemblyName *aname, [[maybe_unused]] char **assemblies_path, [[maybe_unused]] void *user_data)
248+
EmbeddedAssemblies::open_from_bundles_full (MonoAssemblyName *aname, [[maybe_unused]] char **assemblies_path, [[maybe_unused]] void *user_data)
242249
{
243-
return embeddedAssemblies.open_from_bundles (aname, true);
250+
return embeddedAssemblies.open_from_bundles (aname, false);
244251
}
245-
#endif // !(def NET6 && def NET6_ALC_WORKS)
246252

247253
void
248-
EmbeddedAssemblies::install_preload_hooks ()
254+
EmbeddedAssemblies::install_preload_hooks (bool early)
249255
{
250-
#if defined (NET6) && defined (NET6_ALC_WORKS)
251-
mono_install_assembly_preload_hook_v3 (
252-
open_from_bundles,
253-
nullptr /* user_data */,
254-
0 /* append */
255-
);
256-
#else // def NET6 && def NET6_ALC_WORKS
257-
mono_install_assembly_preload_hook (open_from_bundles_full, nullptr);
258-
#if !defined (NET6) // Reference-only loads don't exist in NET6
259-
mono_install_assembly_refonly_preload_hook (open_from_bundles_refonly, nullptr);
260-
#endif // !def NET6
261-
#endif // !(def NET6 && def NET6_ALC_WORKS)
256+
if (early) {
257+
mono_install_assembly_preload_hook (open_from_bundles_full, nullptr);
258+
#if !defined (NET6)
259+
mono_install_assembly_refonly_preload_hook (open_from_bundles_refonly, nullptr);
260+
#endif // ndef NET6
261+
}
262+
263+
#if defined (NET6)
264+
if (!early) {
265+
mono_install_assembly_preload_hook_v3 (
266+
open_from_bundles,
267+
nullptr /* user_data */,
268+
0 /* append */
269+
);
270+
}
271+
#endif // ndef NET6
262272
}
263273

264274
template<typename Key, typename Entry, int (*compare)(const Key*, const Entry*), bool use_extra_size>

src/monodroid/jni/embedded-assemblies.hh

Lines changed: 10 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44

55
#include <cstring>
66
#include <limits>
7+
#include <functional>
78
#include <mono/metadata/object.h>
89
#include <mono/metadata/assembly.h>
910

@@ -50,7 +51,7 @@ namespace xamarin::android::internal {
5051
#endif
5152
const char* typemap_managed_to_java (MonoReflectionType *type, const uint8_t *mvid);
5253

53-
void install_preload_hooks ();
54+
void install_preload_hooks (bool early);
5455
MonoReflectionType* typemap_java_to_managed (MonoString *java_type);
5556

5657
/* returns current number of *all* assemblies found from all invocations */
@@ -93,11 +94,12 @@ namespace xamarin::android::internal {
9394
MonoReflectionType* typemap_java_to_managed (const char *java_type_name);
9495
size_t register_from (const char *apk_file, monodroid_should_register should_register);
9596
void gather_bundled_assemblies_from_apk (const char* apk, monodroid_should_register should_register);
96-
#if defined (NET6) && defined (NET6_ALC_WORKS)
97+
#if defined (NET6)
9798
MonoAssembly* open_from_bundles (MonoAssemblyName* aname, MonoAssemblyLoadContextGCHandle alc_gchandle, MonoError *error);
98-
#else // def NET6 && def NET6_ALC_WORKS
99+
#endif // def NET6
99100
MonoAssembly* open_from_bundles (MonoAssemblyName* aname, bool ref_only);
100-
#endif // !(def NET6 && def NET6_ALC_WORKS)
101+
MonoAssembly* open_from_bundles (MonoAssemblyName* aname, std::function<MonoImage*(char*, uint32_t, const char*)> loader, bool ref_only);
102+
101103
#if defined (DEBUG) || !defined (ANDROID)
102104
template<typename H>
103105
bool typemap_read_header (int dir_fd, const char *file_type, const char *dir_path, const char *file_path, uint32_t expected_magic, H &header, size_t &file_size, int &fd);
@@ -111,12 +113,12 @@ namespace xamarin::android::internal {
111113
bool register_debug_symbols_for_assembly (const char *entry_name, MonoBundledAssembly *assembly, const mono_byte *debug_contents, int debug_size);
112114

113115
static md_mmap_info md_mmap_apk_file (int fd, uint32_t offset, uint32_t size, const char* filename, const char* apk);
114-
#if defined (NET6) && defined (NET6_ALC_WORKS)
115-
static MonoAssembly* open_from_bundles (MonoAssemblyLoadContextGCHandle alc_gchandle, MonoAssemblyName *aname, char **assemblies_path, void *user_data, MonoError *error);
116-
#else // def NET6 && def NET6_ALC_WORKS
117116
static MonoAssembly* open_from_bundles_full (MonoAssemblyName *aname, char **assemblies_path, void *user_data);
117+
#if defined (NET6)
118+
static MonoAssembly* open_from_bundles (MonoAssemblyLoadContextGCHandle alc_gchandle, MonoAssemblyName *aname, char **assemblies_path, void *user_data, MonoError *error);
119+
#else // def NET6
118120
static MonoAssembly* open_from_bundles_refonly (MonoAssemblyName *aname, char **assemblies_path, void *user_data);
119-
#endif // !(def NET6 && def NET6_ALC_WORKS)
121+
#endif // ndef NET6
120122
static void get_assembly_data (const MonoBundledAssembly *e, char*& assembly_data, uint32_t& assembly_data_size);
121123

122124
void zip_load_entries (int fd, const char *apk_name, monodroid_should_register should_register);

src/monodroid/jni/monodroid-glue-internal.hh

Lines changed: 11 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -64,8 +64,11 @@ namespace xamarin::android::internal
6464
true
6565
>;
6666

67+
using load_assemblies_context_type = MonoAssemblyLoadContextGCHandle;
6768
static constexpr pinvoke_library_map::size_type LIBRARY_MAP_INITIAL_BUCKET_COUNT = 1;
68-
#endif // def NET6
69+
#else // def NET6
70+
using load_assemblies_context_type = MonoDomain*;
71+
#endif // ndef NET6
6972

7073
#if defined (DEBUG) && !defined (WINDOWS)
7174
struct RuntimeOptions {
@@ -222,7 +225,11 @@ namespace xamarin::android::internal
222225
void disable_external_signal_handlers ();
223226
void lookup_bridge_info (MonoDomain *domain, MonoImage *image, const OSBridge::MonoJavaGCBridgeType *type, OSBridge::MonoJavaGCBridgeInfo *info);
224227
void load_assembly (MonoDomain *domain, jstring_wrapper &assembly);
225-
void load_assemblies (MonoDomain *domain, bool preload, jstring_array_wrapper &assemblies);
228+
#if defined (NET6)
229+
void load_assembly (MonoAssemblyLoadContextGCHandle alc_handle, jstring_wrapper &assembly);
230+
#endif // ndef NET6
231+
void load_assemblies (load_assemblies_context_type ctx, bool preload, jstring_array_wrapper &assemblies);
232+
226233
void set_debug_options ();
227234
void parse_gdb_options ();
228235
void mono_runtime_init (dynamic_local_string<PROPERTY_VALUE_BUFFER_LEN>& runtime_args);
@@ -302,6 +309,8 @@ namespace xamarin::android::internal
302309
int current_context_id = -1;
303310

304311
#if defined (NET6)
312+
MonoAssemblyLoadContextGCHandle default_alc = nullptr;
313+
305314
static std::mutex pinvoke_map_write_lock;
306315
static pinvoke_api_map xa_pinvoke_map;
307316
static pinvoke_library_map other_pinvoke_map;

src/monodroid/jni/monodroid-glue.cc

Lines changed: 58 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -837,7 +837,7 @@ MonodroidRuntime::mono_runtime_init ([[maybe_unused]] dynamic_local_string<PROPE
837837
* Looking for assemblies from the update dir takes precedence over
838838
* everything else, and thus must go LAST.
839839
*/
840-
embeddedAssemblies.install_preload_hooks ();
840+
embeddedAssemblies.install_preload_hooks (true /* early */);
841841
#ifndef RELEASE
842842
mono_install_assembly_preload_hook (open_from_update_dir, nullptr);
843843
#endif
@@ -894,8 +894,11 @@ MonodroidRuntime::create_domain (JNIEnv *env, jstring_array_wrapper &runtimeApks
894894
}
895895

896896
MonoDomain *domain;
897+
#if !defined (NET6)
897898
if (is_root_domain) {
899+
#endif // ndef NET6
898900
domain = mono_jit_init_version (const_cast<char*> ("RootDomain"), const_cast<char*> ("mobile"));
901+
#if !defined (NET6)
899902
} else {
900903
MonoDomain* root_domain = mono_get_root_domain ();
901904

@@ -909,6 +912,7 @@ MonodroidRuntime::create_domain (JNIEnv *env, jstring_array_wrapper &runtimeApks
909912

910913
domain = utils.monodroid_create_appdomain (root_domain, domain_name.get (), /*shadow_copy:*/ 1, /*shadow_directory:*/ androidSystem.get_override_dir (0));
911914
}
915+
#endif // ndef NET6
912916

913917
if constexpr (is_running_on_desktop) {
914918
if (is_root_domain) {
@@ -1615,50 +1619,75 @@ MonodroidRuntime::disable_external_signal_handlers (void)
16151619
}
16161620
}
16171621

1622+
#if defined (NET6)
16181623
inline void
1619-
MonodroidRuntime::load_assembly (MonoDomain *domain, jstring_wrapper &assembly)
1624+
MonodroidRuntime::load_assembly (MonoAssemblyLoadContextGCHandle alc_handle, jstring_wrapper &assembly)
16201625
{
1626+
log_warn (LOG_ASSEMBLY, __PRETTY_FUNCTION__);
1627+
16211628
timing_period total_time;
16221629
if (XA_UNLIKELY (utils.should_log (LOG_TIMING)))
16231630
total_time.mark_start ();
16241631

16251632
const char *assm_name = assembly.get_cstr ();
1626-
MonoAssemblyName *aname;
1633+
log_warn (LOG_ASSEMBLY, " trying to load assembly: %s", assm_name);
1634+
MonoAssemblyName *aname = mono_assembly_name_new (assm_name);
1635+
log_warn (LOG_ASSEMBLY, " aname == %p", aname);
1636+
1637+
MonoImageOpenStatus open_status;
1638+
mono_assembly_load_full_alc (alc_handle, aname, nullptr /* basedir */, &open_status);
1639+
1640+
mono_assembly_name_free (aname);
16271641

1628-
aname = mono_assembly_name_new (assm_name);
1642+
if (XA_UNLIKELY (utils.should_log (LOG_TIMING))) {
1643+
total_time.mark_end ();
1644+
TIMING_LOG_INFO (total_time, "Assembly load (ALC): %s", assm_name);
1645+
}
1646+
}
1647+
#endif // NET6
1648+
1649+
inline void
1650+
MonodroidRuntime::load_assembly (MonoDomain *domain, jstring_wrapper &assembly)
1651+
{
1652+
timing_period total_time;
1653+
if (XA_UNLIKELY (utils.should_log (LOG_TIMING)))
1654+
total_time.mark_start ();
1655+
1656+
const char *assm_name = assembly.get_cstr ();
1657+
MonoAssemblyName *aname = mono_assembly_name_new (assm_name);
16291658

16301659
#ifndef ANDROID
16311660
if (designerAssemblies.has_assemblies () && designerAssemblies.try_load_assembly (domain, aname) != nullptr) {
16321661
log_debug (LOG_ASSEMBLY, "Dynamically opened assembly %s", mono_assembly_name_get_name (aname));
16331662
} else
16341663
#endif
1635-
if (domain != mono_domain_get ()) {
1636-
MonoDomain *current = mono_domain_get ();
1637-
mono_domain_set (domain, FALSE);
1638-
mono_assembly_load_full (aname, NULL, NULL, 0);
1639-
mono_domain_set (current, FALSE);
1640-
} else {
1641-
mono_assembly_load_full (aname, NULL, NULL, 0);
1642-
}
1664+
if (domain != mono_domain_get ()) {
1665+
MonoDomain *current = mono_domain_get ();
1666+
mono_domain_set (domain, FALSE);
1667+
mono_assembly_load_full (aname, NULL, NULL, 0);
1668+
mono_domain_set (current, FALSE);
1669+
} else {
1670+
mono_assembly_load_full (aname, NULL, NULL, 0);
1671+
}
16431672

16441673
mono_assembly_name_free (aname);
16451674

16461675
if (XA_UNLIKELY (utils.should_log (LOG_TIMING))) {
16471676
total_time.mark_end ();
1648-
TIMING_LOG_INFO (total_time, "Assembly load: %s preloaded", assm_name);
1677+
TIMING_LOG_INFO (total_time, "Assembly load (domain): %s", assm_name);
16491678
}
16501679
}
16511680

16521681
inline void
1653-
MonodroidRuntime::load_assemblies (MonoDomain *domain, bool preload, jstring_array_wrapper &assemblies)
1682+
MonodroidRuntime::load_assemblies (load_assemblies_context_type ctx, bool preload, jstring_array_wrapper &assemblies)
16541683
{
16551684
timing_period total_time;
16561685
if (XA_UNLIKELY (utils.should_log (LOG_TIMING)))
16571686
total_time.mark_start ();
16581687

16591688
for (size_t i = 0; i < assemblies.get_length (); ++i) {
16601689
jstring_wrapper &assembly = assemblies [i];
1661-
load_assembly (domain, assembly);
1690+
load_assembly (ctx, assembly);
16621691
// only load the first "main" assembly if we are not preloading.
16631692
if (!preload)
16641693
break;
@@ -1692,12 +1721,25 @@ MonodroidRuntime::create_and_initialize_domain (JNIEnv* env, jclass runtimeClass
16921721
}
16931722
}
16941723

1724+
#if defined (NET6)
1725+
default_alc = mono_alc_get_default_gchandle ();
1726+
abort_unless (default_alc != nullptr, "Default AssemblyLoadContext not found");
1727+
1728+
embeddedAssemblies.install_preload_hooks (false /* early */);
1729+
log_debug (LOG_ASSEMBLY, "ALC hooks installed");
1730+
#endif // def NET6
1731+
16951732
#ifndef ANDROID
16961733
if (assembliesBytes != nullptr)
16971734
designerAssemblies.add_or_update_from_java (domain, env, assemblies, assembliesBytes, assembliesPaths);
16981735
#endif
16991736
bool preload = (androidSystem.is_assembly_preload_enabled () || (is_running_on_desktop && force_preload_assemblies));
1700-
load_assemblies (domain, preload, assemblies);
1737+
1738+
#if defined (NET6)
1739+
load_assemblies (default_alc, preload, assemblies);
1740+
#else // def NET6
1741+
load_assemblies (domain, preload, assemblies);
1742+
#endif // ndef NET6
17011743
init_android_runtime (domain, env, runtimeClass, loader);
17021744

17031745
osBridge.add_monodroid_domain (domain);

0 commit comments

Comments
 (0)