Skip to content

[Mono.Android] JNI handles are now in a "control block" #10179

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

Draft
wants to merge 6 commits into
base: main
Choose a base branch
from

Conversation

jonathanpeppers
Copy link
Member

Originally from: #10125
Context: dotnet/java-interop#1339

These are changes to Mono's GC bridge implementation, such that the same code can be used on both Mono and CoreCLR.

The base JavaObject type has changes such as:

--[NonSerialized] IntPtr                  handle;
--[NonSerialized] JniObjectReferenceType  handle_type;
--[NonSerialized] IntPtr                  weak_handle;
--[NonSerialized] int                     refs_added;
++unsafe  JniObjectReferenceControlBlock* jniObjectReferenceControlBlock;

And the new JniObjectReferenceControlBlock struct is defined as:

internal struct JniObjectReferenceControlBlock {
    public	IntPtr  handle;
    public  int     handle_type;
    public  IntPtr  weak_handle;
    public  int     refs_added;
}
  • Each JavaObject allocation now has a NativeMemory.AllocZeroed() call for this struct.

  • However, we only have to read a single field now using Mono's native embedding API:

--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"));
--info->weak_handle       = mono_class_get_field_from_name (info->klass, const_cast<char*> ("weak_handle"));
++info->jniObjectReferenceControlBlock    = mono_class_get_field_from_name (info->klass, const_cast<char*> ("jniObjectReferenceControlBlock"));

We are hoping this results in ~the same performance as before, with the flexibility to support multiple GC bridge implementations and runtimes.

Originally from: #10125
Context: dotnet/java-interop#1339

These are changes to Mono's GC bridge implementation, such that the
same code can be used on both Mono and CoreCLR.

The base `JavaObject` type has changes such as:

```diff
--[NonSerialized] IntPtr                  handle;
--[NonSerialized] JniObjectReferenceType  handle_type;
--[NonSerialized] IntPtr                  weak_handle;
--[NonSerialized] int                     refs_added;
++unsafe  JniObjectReferenceControlBlock* jniObjectReferenceControlBlock;
```

And the new `JniObjectReferenceControlBlock` struct is defined as:

    internal struct JniObjectReferenceControlBlock {
        public	IntPtr  handle;
        public  int     handle_type;
        public  IntPtr  weak_handle;
        public  int     refs_added;
    }

* Each `JavaObject` allocation now has a `NativeMemory.AllocZeroed()`
  call for this struct.

* However, we only have to read a single field now using Mono's native
  embedding API:

```diff
--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"));
--info->weak_handle       = mono_class_get_field_from_name (info->klass, const_cast<char*> ("weak_handle"));
++info->jniObjectReferenceControlBlock    = mono_class_get_field_from_name (info->klass, const_cast<char*> ("jniObjectReferenceControlBlock"));
```

We are hoping this results in ~the same performance as before, with
the flexibility to support multiple GC bridge implementations and
runtimes.

Co-authored-by: Marek Habersack <[email protected]>
@jonathanpeppers
Copy link
Member Author

This is close now, but crashes partway through test suite:

06-09 19:47:52.059 13883 13904 I NUnit   : 	Passed
06-09 19:47:52.059 13883 13904 I NUnit   : SynchronizationContext_Post_DoesNotBlock 
06-09 19:47:52.079 13883 13904 I NUnit   : 	Passed
06-09 19:47:52.106 13883 13904 I NUnit   : Android.AppTests.ApplicationTest : 311.008 ms
06-09 19:47:52.115 13883 13904 F libc    : Fatal signal 11 (SIGSEGV), code 1 (SEGV_MAPERR), fault addr 0x2e56 in tid 13904 (Instrumentation), pid 13883 (droid.NET_Tests)
06-09 19:47:52.149 13908 13908 I crash_dump64: obtaining output fd from tombstoned, type: kDebuggerdTombstone
06-09 19:47:52.150  1840  1840 I /system/bin/tombstoned: received crash request for pid 13904
06-09 19:47:52.150 13908 13908 I crash_dump64: performing dump of process 13883 (target tid = 13904)
06-09 19:47:52.155 13908 13908 F DEBUG   : *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** ***
06-09 19:47:52.155 13908 13908 F DEBUG   : Build fingerprint: 'Android/sdk_phone_x86_64/generic_x86_64:10/QSR1.210820.001/7663313:userdebug/test-keys'
06-09 19:47:52.155 13908 13908 F DEBUG   : Revision: '0'
06-09 19:47:52.155 13908 13908 F DEBUG   : ABI: 'x86_64'
06-09 19:47:52.156 13908 13908 F DEBUG   : Timestamp: 2025-06-09 19:47:52+0000
06-09 19:47:52.156 13908 13908 F DEBUG   : pid: 13883, tid: 13904, name: Instrumentation  >>> Mono.Android.NET_Tests <<<
06-09 19:47:52.156 13908 13908 F DEBUG   : uid: 10116
06-09 19:47:52.156 13908 13908 F DEBUG   : signal 11 (SIGSEGV), code 1 (SEGV_MAPERR), fault addr 0x2e56
06-09 19:47:52.156 13908 13908 F DEBUG   :     rax 0000000000000000  rbx 0000719bb2676d98  rcx 0000719bb30b1574  rdx 0000719bad6d2e08
06-09 19:47:52.156 13908 13908 F DEBUG   :     r8  0000000000000002  r9  0000719bb3251874  r10 029afa1800000000  r11 0000000000000246
06-09 19:47:52.156 13908 13908 F DEBUG   :     r12 0000719bad6d2db0  r13 0000000000000000  r14 0000000000002e56  r15 0000719bb2676d98
06-09 19:47:52.156 13908 13908 F DEBUG   :     rdi 0000719bb2676d98  rsi 0000000000002e56
06-09 19:47:52.156 13908 13908 F DEBUG   :     rbp 0000719bad6d2eb0  rsp 0000719bad6d2d30  rip 0000719bb32c3623
06-09 19:47:52.160 11774 11774 I Zygote  : Process 12876 exited due to signal 9 (Killed)
06-09 19:47:52.186 11819 11865 I libprocessgroup: Successfully killed process cgroup uid 10052 pid 12876 in 178ms
06-09 19:47:52.314 13908 13908 F DEBUG   : 
06-09 19:47:52.314 13908 13908 F DEBUG   : backtrace:
06-09 19:47:52.314 13908 13908 F DEBUG   :       #00 pc 0000000000283623  /data/app/Mono.Android.NET_Tests-1NyeuxdOpbbgVP6gUyXaLg==/lib/x86_64/libmonosgen-2.0.so (BuildId: 088ffb4f24494b9211fd50df8ea276d10e37d3ad)
06-09 19:47:52.314 13908 13908 F DEBUG   :       #01 pc 00000000002835ee  /data/app/Mono.Android.NET_Tests-1NyeuxdOpbbgVP6gUyXaLg==/lib/x86_64/libmonosgen-2.0.so (mono_field_get_value+62) (BuildId: 088ffb4f24494b9211fd50df8ea276d10e37d3ad)
06-09 19:47:52.315 13908 13908 F DEBUG   :       #02 pc 00000000000d680c  /data/app/Mono.Android.NET_Tests-1NyeuxdOpbbgVP6gUyXaLg==/lib/x86_64/libmonodroid.so (xamarin::android::internal::OSBridge::gc_is_bridge_object(_MonoObject*)+92) (BuildId: 41cb33480f3cc8f594309d9a81f59c0c7e1e75f8)
06-09 19:47:52.315 13908 13908 F DEBUG   :       #03 pc 00000000002d51e9  /data/app/Mono.Android.NET_Tests-1NyeuxdOpbbgVP6gUyXaLg==/lib/x86_64/libmonosgen-2.0.so (BuildId: 088ffb4f24494b9211fd50df8ea276d10e37d3ad)
06-09 19:47:52.316 13908 13908 F DEBUG   :       #04 pc 00000000002db211  /data/app/Mono.Android.NET_Tests-1NyeuxdOpbbgVP6gUyXaLg==/lib/x86_64/libmonosgen-2.0.so (BuildId: 088ffb4f24494b9211fd50df8ea276d10e37d3ad)
06-09 19:47:52.316 13908 13908 F DEBUG   :       #05 pc 00000000002dbd32  /data/app/Mono.Android.NET_Tests-1NyeuxdOpbbgVP6gUyXaLg==/lib/x86_64/libmonosgen-2.0.so (BuildId: 088ffb4f24494b9211fd50df8ea276d10e37d3ad)
06-09 19:47:52.316 13908 13908 F DEBUG   :       #06 pc 00000000002da9bb  /data/app/Mono.Android.NET_Tests-1NyeuxdOpbbgVP6gUyXaLg==/lib/x86_64/libmonosgen-2.0.so (BuildId: 088ffb4f24494b9211fd50df8ea276d10e37d3ad)
06-09 19:47:52.316 13908 13908 F DEBUG   :       #07 pc 00000000002d67b5  /data/app/Mono.Android.NET_Tests-1NyeuxdOpbbgVP6gUyXaLg==/lib/x86_64/libmonosgen-2.0.so (BuildId: 088ffb4f24494b9211fd50df8ea276d10e37d3ad)
06-09 19:47:52.316 13908 13908 F DEBUG   :       #08 pc 00000000002d776e  /data/app/Mono.Android.NET_Tests-1NyeuxdOpbbgVP6gUyXaLg==/lib/x86_64/libmonosgen-2.0.so (BuildId: 088ffb4f24494b9211fd50df8ea276d10e37d3ad)
06-09 19:47:52.316 13908 13908 F DEBUG   :       #09 pc 00000000002c65bc  /data/app/Mono.Android.NET_Tests-1NyeuxdOpbbgVP6gUyXaLg==/lib/x86_64/libmonosgen-2.0.so (mono_gc_collect+44) (BuildId: 088ffb4f24494b9211fd50df8ea276d10e37d3ad)
06-09 19:47:52.316 13908 13908 F DEBUG   :       #10 pc 00000000001c9058  /data/app/Mono.Android.NET_Tests-1NyeuxdOpbbgVP6gUyXaLg==/lib/x86_64/libmonosgen-2.0.so (BuildId: 088ffb4f24494b9211fd50df8ea276d10e37d3ad)
06-09 19:47:52.316 13908 13908 F DEBUG   :       #11 pc 00000000001c782b  /data/app/Mono.Android.NET_Tests-1NyeuxdOpbbgVP6gUyXaLg==/lib/x86_64/libmonosgen-2.0.so (BuildId: 088ffb4f24494b9211fd50df8ea276d10e37d3ad)
06-09 19:47:52.316 13908 13908 F DEBUG   :       #12 pc 00000000001b9919  /data/app/Mono.Android.NET_Tests-1NyeuxdOpbbgVP6gUyXaLg==/lib/x86_64/libmonosgen-2.0.so (BuildId: 088ffb4f24494b9211fd50df8ea276d10e37d3ad)
06-09 19:47:52.317 13908 13908 F DEBUG   :       #13 pc 00000000001c9402  /data/app/Mono.Android.NET_Tests-1NyeuxdOpbbgVP6gUyXaLg==/lib/x86_64/libmonosgen-2.0.so (BuildId: 088ffb4f24494b9211fd50df8ea276d10e37d3ad)
06-09 19:47:52.317 13908 13908 F DEBUG   :       #14 pc 00000000001c9bb3  /data/app/Mono.Android.NET_Tests-1NyeuxdOpbbgVP6gUyXaLg==/lib/x86_64/libmonosgen-2.0.so (BuildId: 088ffb4f24494b9211fd50df8ea276d10e37d3ad)
06-09 19:47:52.317 13908 13908 F DEBUG   :       #15 pc 000000000001120c  <anonymous:62d25000>

Some of the configurations didn't crash.

grendello and others added 3 commits June 11, 2025 18:37
We were using it incorrectly - there's no need to fetch/set values of managed object fields.
The correct thing to do is to read/write them on the control block itself.
Conflicts:
	external/Java.Interop
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

2 participants