Skip to content

Commit ccdc24c

Browse files
[NativeAOT] fix default SynchronizationContext (#9883)
Context: #9846 Context: b6c22f3 Running `Mono.Android-Tests` under NativeAOT has the test failure: I NUnit : SynchronizationContext_Is_ThreadingSynchronizationContextCurrent E NUnit : [FAIL] E NUnit : : Expected: True E NUnit : But was: False E NUnit : at libMono.Android.NET-Tests!<BaseAddress>+0x1482f63 E NUnit : at System.Reflection.DynamicInvokeInfo.Invoke(Object, IntPtr, Object[], BinderBundle, Boolean) + 0xf3 `SynchronizationContext_Is_ThreadingSynchronizationContextCurrent()` failed because we never called `JNIEnvInit.SetSynchronizationContext()`. Call `JNIEnvInit.SetSynchronizationContext()`, and… F libc : Fatal signal 6 (SIGABRT), code -1 (SI_QUEUE) in tid 17119 (Thread-11), pid 17058 (droid.NET_Tests) I crash_dump64: obtaining output fd from tombstoned, type: kDebuggerdTombstoneProto I tombstoned: received crash request for pid 17119 I crash_dump64: performing dump of process 17058 (target tid = 17119) F DEBUG : *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** F DEBUG : Build fingerprint: 'google/panther/panther:14/AP2A.240905.003/12231197:user/release-keys' F DEBUG : Revision: 'MP1.0' F DEBUG : ABI: 'arm64' F DEBUG : Timestamp: 2025-03-07 15:48:39.677241696-0600 F DEBUG : Process uptime: 2s F DEBUG : Cmdline: Mono.Android.NET_Tests F DEBUG : pid: 17058, tid: 17119, name: Thread-11 >>> Mono.Android.NET_Tests <<< F DEBUG : uid: 10582 F DEBUG : tagged_addr_ctrl: 0000000000000001 (PR_TAGGED_ADDR_ENABLE) F DEBUG : signal 6 (SIGABRT), code -1 (SI_QUEUE), fault addr -------- F DEBUG : x0 0000000000000000 x1 00000000000042df x2 0000000000000006 x3 0000007c6b4838f0 F DEBUG : x4 0000000000000000 x5 0000000000000000 x6 0000000000000000 x7 0000007f36a58004 F DEBUG : x8 00000000000000f0 x9 0000007f18a6d350 x10 0000000000000001 x11 0000007f18abe170 F DEBUG : x12 0000000000000004 x13 51c34e921a529271 x14 0000000000000001 x15 0000000000000001 F DEBUG : x16 0000007f18b24fd0 x17 0000007f18b10560 x18 0000007c694dc000 x19 00000000000042a2 F DEBUG : x20 00000000000042df x21 00000000ffffffff x22 0000001a580093f8 x23 0000007c6b483e18 F DEBUG : x24 0000000000000010 x25 0000000000000014 x26 0000001a5bc27ab8 x27 0000001a5bc27b40 F DEBUG : x28 0000001a56005b38 x29 0000007c6b483970 F DEBUG : lr 0000007f18aa78b8 sp 0000007c6b4838d0 pc 0000007f18aa78e4 pst 0000000000001000 F DEBUG : 2 total frames F DEBUG : backtrace: F DEBUG : #00 pc 000000000005d8e4 /apex/com.android.runtime/lib64/bionic/libc.so (abort+164) (BuildId: 1d36f8ae6e0af6158793abea7d4f4f2b) F DEBUG : #1 pc 0000000000034828 /data/app/~~rqTsxkU5NjJYcDmnf-0haw==/Mono.Android.NET_Tests-fIi1xEY1rqvY2p4EDO81Ww==/split_config.arm64_v8a.apk (offset 0xa6c000) After lots of digging around, we discovered that the problem was that the `Android.App.Application` static constructor was running *too early*, which caused an `Android.Runtime.XAPeerMembers` instance to be constructed *before the runtime had finished initialization*. Which leads us to a digression: when is static constructor run? The answer to this depends upon the [`beforefieldinit` flag][0]: * When `beforefieldinit` is set, the runtime has lots of flexibility over when the static constructor is executed. The static constructor could be executed far earlier than one may normally expect. * When `beforefieldinit` *is not* set, the static constructor is executed "immediately" before the type is used. In C#, if a type does not contain a static constructor, then the type has `beforefieldinit` set: // has beforefieldinit partial class Application { static readonly JniPeerMembers _members = new XAPeerMembers ("android/app/Application", typeof (Application)); } If a type has a static constructor, then it does not have `beforefieldinit` set: // does not have beforefieldinit partial class Application { static readonly JniPeerMembers _members = new XAPeerMembers ("android/app/Application", typeof (Application)); static Application() { } } We hit this same scenario in b6c22f3 when using Mono's LLVM toolchain, and fixed that by using `[MethodImpl(MethodImplOptions.NoInlining)]` on `SetSynchronizationContext()`, in the hope that doing so would delay execution of the `Application` static constructor until the invocation of `SetSynchronizationContext()`. This worked for b6c22f3, but not with NativeAOT. To fix this: * Add an empty static constructor to `Android.App.Application` to remove the `beforefieldinit` flag from that type. * Call `JNIEnvInit.SetSynchronizationContext()` at startup for NativeAOT. With this change in place, no crashes occur at startup and the test passes. [0]: https://csharpindepth.com/Articles/BeforeFieldInit
1 parent fb4286d commit ccdc24c

File tree

2 files changed

+5
-2
lines changed

2 files changed

+5
-2
lines changed

src/Mono.Android/Android.App/Application.cs

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,10 @@ namespace Android.App {
88

99
partial class Application {
1010

11+
static Application () {
12+
// NOTE: do not remove, removes the beforefieldinit flag for JNIEnvInit.SetSynchronizationContext()
13+
}
14+
1115
static Context? _context;
1216
public static Context Context {
1317
get {

src/Mono.Android/Android.Runtime/JNIEnvInit.cs

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -86,6 +86,7 @@ internal static void InitializeJniRuntime (JniRuntime runtime)
8686
{
8787
androidRuntime = runtime;
8888
ValueManager = runtime.ValueManager;
89+
SetSynchronizationContext ();
8990
}
9091

9192
[UnmanagedCallersOnly]
@@ -136,8 +137,6 @@ internal static unsafe void Initialize (JnienvInitializeArgs* args)
136137
[DllImport ("xamarin-app")]
137138
static extern unsafe void xamarin_app_init (IntPtr env, delegate* unmanaged <int, int, int, IntPtr*, void> get_function_pointer);
138139

139-
// NOTE: prevents Android.App.Application static ctor from running
140-
[MethodImpl (MethodImplOptions.NoInlining)]
141140
static void SetSynchronizationContext () =>
142141
SynchronizationContext.SetSynchronizationContext (Android.App.Application.SynchronizationContext);
143142
}

0 commit comments

Comments
 (0)