-
Notifications
You must be signed in to change notification settings - Fork 564
[NativeAOT] "Fix" AndroidRuntimeInternal.WaitForBridgeProcessing() #10128
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
[NativeAOT] "Fix" AndroidRuntimeInternal.WaitForBridgeProcessing() #10128
Conversation
Fixes: #10126 Context: 4b158ce Context: dotnet/java-interop@356485e Context: 32cff43 NativeAOT currently requires new binding assemblies, bindings which do not use `JNINativeWrapper.CreateDelegate()`, as one of the code paths within `JNINativeWrapper.CreateDelegate()` uses `System.Reflection.Emit`, which does not exist in NativeAOT. dotnet/java-interop@356485ee contains the `generator` changes needed to avoid `JNINativeWrapper.CreateDelegate()`. However, there is one situation in which that "requirement" is (kinda) *conditional*: commit 32cff43 added "builtin" JNI wrapper methods, avoiding the need for System.Reflection.Emit when overriding Java methods that match a builtin signature. This means existing binding assemblies can work, right? Kinda, yeah! So long as you *only* override methods which match a builtin signature. Which was enough to get MAUI going atop NativeAOT *without* rebuilding all of dotnet/android-libraries… However, for not-entirely-understood reasons, sometimes using a builtin JNI wrapper method would cause the app to crash: digest=============com.avalonia.safeareademo /apex/com.android.runtime/lib/bionic/libc.so (abort+) () XXlib/arm/libSafeAreaDemo.Android.so (SystemNative_Abort+) () XXlib/arm/libSafeAreaDemo.Android.so (S_P_CoreLib_Interop_Sys__Abort+) () XXlib/arm/libSafeAreaDemo.Android.so (S_P_CoreLib_System_RuntimeExceptionHelpers__FailFast+) () XXlib/arm/libSafeAreaDemo.Android.so (RuntimeFailFast+) () XXlib/arm/libSafeAreaDemo.Android.so (S_P_CoreLib_System_Runtime_EH__UnhandledExceptionFailFastViaClasslib+) () XXlib/arm/libSafeAreaDemo.Android.so (S_P_CoreLib_System_Runtime_EH__DispatchEx+) () XXlib/arm/libSafeAreaDemo.Android.so (RhThrowEx+) () XXlib/arm/libSafeAreaDemo.Android.so (RhpThrowEx+) () XXlib/arm/libSafeAreaDemo.Android.so (S_P_CoreLib_System_Runtime_CompilerServices_ClassConstructorRunner__EnsureClassConstructorRun+) () XXlib/arm/libSafeAreaDemo.Android.so (RhpCallCatchFunclet+) () XXlib/arm/libSafeAreaDemo.Android.so (S_P_CoreLib_System_Runtime_CompilerServices_ClassConstructorRunner__CheckStaticClassConstructionReturnNonGCStaticBase+) () XXlib/arm/libSafeAreaDemo.Android.so (Mono_Android_Runtime_Android_Runtime_AndroidRuntimeInternal__WaitForBridgeProcessing+) () XXlib/arm/libSafeAreaDemo.Android.so (Mono_Android_Android_Runtime_JNINativeWrapper__Wrap_JniMarshal_PPLL_L+) () XXlib/arm/libSafeAreaDemo.Android.so (Mono_Android__JniMarshal_PPLL_L__InvokeClosedStaticThunk+) () XXlib/arm/libSafeAreaDemo.Android.so (Internal_CompilerGenerated__Module___<ReverseDelegateStub>Mono_Android__JniMarshal_PPLL_L+) () /apex/com.android.art/lib/libart.so (art_quick_generic_jni_trampoline+) () The cause of this crash is that `JNIINativeWrapper.Wrap_JniMarshal_PPLL_L()` called `AndroidRuntimeInternal.WaitForBridgeProcessing()`, which in turn triggers the `AndroidRuntimeInternal` static constructor, which *throws* unless on MonoVM or CoreCLR, of which NativeAOT is neither. Fix this by updating `JNINativeWrapper.g.tt` to call `JNIEnv.ValueManager?.WaitForGCBridgeProcessing()` instead of `AndroidRuntimeInternal.WaitForBridgeProcessing()`. This allows NativeAOT to control what actually happens. Aside: this project includes [T4 Text Templates][0]. To regenerate the output files *without involving Visual Studio*, you can install the [`dotnet-t4`][1] tool: $ dotnet tool install --global dotnet-t4 then run it separately for each `.tt` file: $HOME/.dotnet/tools/t4 -o src/Mono.Android/Android.Runtime/JNINativeWrapper.g.cs \ src/Mono.Android/Android.Runtime/JNINativeWrapper.g.tt [0]: https://learn.microsoft.com/visualstudio/modeling/code-generation-and-t4-text-templates?view=vs-2022 [1]: https://www.nuget.org/packages/dotnet-t4/
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Pull Request Overview
This PR fixes a crash on NativeAOT by replacing calls to AndroidRuntimeInternal.WaitForBridgeProcessing() with JNIEnvInit.ValueManager?.WaitForGCBridgeProcessing(), ensuring that the proper bridge processing method is invoked for built-in JNI wrappers.
- Updated code in T4 template (JNINativeWrapper.g.tt)
- Updated multiple JNI wrapper methods in JNINativeWrapper.g.cs
- Modified JNIEnv.WaitForBridgeProcessing() to use the new API
Reviewed Changes
Copilot reviewed 3 out of 3 changed files in this pull request and generated no comments.
| File | Description |
|---|---|
| src/Mono.Android/Android.Runtime/JNINativeWrapper.g.tt | Replaced wait call to use JNIEnvInit.ValueManager?.WaitForGCBridgeProcessing() |
| src/Mono.Android/Android.Runtime/JNINativeWrapper.g.cs | Updated all JNI wrapper methods to call JNIEnvInit.ValueManager?.WaitForGCBridgeProcessing() |
| src/Mono.Android/Android.Runtime/JNIEnv.cs | Modified WaitForBridgeProcessing() to use the new call for NativeAOT support |
Comments suppressed due to low confidence (3)
src/Mono.Android/Android.Runtime/JNINativeWrapper.g.tt:271
- Ensure that JNIEnvInit.ValueManager is always properly initialized in contexts where WaitForGCBridgeProcessing() is required, as using the null-conditional operator may suppress necessary processing if ValueManager is null. Consider adding inline documentation to clarify this behavior.
JNIEnvInit.ValueManager?.WaitForGCBridgeProcessing ();
src/Mono.Android/Android.Runtime/JNINativeWrapper.g.cs:20
- Verify that the substitution of AndroidRuntimeInternal.WaitForBridgeProcessing() with JNIEnvInit.ValueManager?.WaitForGCBridgeProcessing() across all JNI wrapper methods meets the intended behavior in all runtime scenarios, particularly in NativeAOT where proper bridge processing is critical.
JNIEnvInit.ValueManager?.WaitForGCBridgeProcessing ();
src/Mono.Android/Android.Runtime/JNIEnv.cs:121
- Confirm that replacing the direct call with the null-conditional access does not unintentionally bypass necessary wait logic when ValueManager might be null. Inline comments or documentation could help explain this behavior for future maintainers.
JNIEnvInit.ValueManager?.WaitForGCBridgeProcessing ();
Fixes: #10126
Context: 4b158ce
Context: dotnet/java-interop@356485e
Context: 32cff43
NativeAOT currently requires new binding assemblies, bindings which do not use
JNINativeWrapper.CreateDelegate(), as one of the code paths withinJNINativeWrapper.CreateDelegate()usesSystem.Reflection.Emit, which does not exist in NativeAOT. dotnet/java-interop@356485ee contains thegeneratorchanges needed to avoidJNINativeWrapper.CreateDelegate().However, there is one situation in which that "requirement" is (kinda) conditional: commit 32cff43 added "builtin" JNI wrapper methods, avoiding the need for System.Reflection.Emit when overriding Java methods that match a builtin signature.
This means existing binding assemblies can work, right?
Kinda, yeah! So long as you only override methods which match a builtin signature. Which was enough to get MAUI going atop NativeAOT without rebuilding all of dotnet/android-libraries…
However, for not-entirely-understood reasons, sometimes using a builtin JNI wrapper method would cause the app to crash:
The cause of this crash is that
JNIINativeWrapper.Wrap_JniMarshal_PPLL_L()calledAndroidRuntimeInternal.WaitForBridgeProcessing(), which in turn triggers theAndroidRuntimeInternalstatic constructor, which throws unless on MonoVM or CoreCLR, of which NativeAOT is neither.Fix this by updating
JNINativeWrapper.g.ttto callJNIEnv.ValueManager?.WaitForGCBridgeProcessing()instead ofAndroidRuntimeInternal.WaitForBridgeProcessing(). This allows NativeAOT to control what actually happens.Aside: this project includes T4 Text Templates. To regenerate the output files without involving Visual Studio, you can install the
dotnet-t4tool:then run it separately for each
.ttfile: