Skip to content

Commit 611fe49

Browse files
[Mono.Android] JNI handles are now in a "control block"
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]>
1 parent a0523ad commit 611fe49

File tree

4 files changed

+86
-57
lines changed

4 files changed

+86
-57
lines changed

src/native/mono/monodroid/monodroid-glue.cc

Lines changed: 5 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -793,22 +793,15 @@ MonodroidRuntime::create_domain (JNIEnv *env, jstring_array_wrapper &runtimeApks
793793
MonodroidRuntime::lookup_bridge_info (MonoClass *klass, const OSBridge::MonoJavaGCBridgeType *type, OSBridge::MonoJavaGCBridgeInfo *info) noexcept
794794
{
795795
info->klass = klass;
796-
info->handle = mono_class_get_field_from_name (info->klass, const_cast<char*> ("handle"));
797-
info->handle_type = mono_class_get_field_from_name (info->klass, const_cast<char*> ("handle_type"));
798-
info->refs_added = mono_class_get_field_from_name (info->klass, const_cast<char*> ("refs_added"));
799-
info->key_handle = mono_class_get_field_from_name (info->klass, const_cast<char*> ("key_handle"));
796+
info->jniObjectReferenceControlBlock = mono_class_get_field_from_name (info->klass, const_cast<char*>("jniObjectReferenceControlBlock"));
800797

801-
// key_handle is optional, as Java.Interop.JavaObject doesn't currently have it
802-
if (info->klass == nullptr || info->handle == nullptr || info->handle_type == nullptr || info->refs_added == nullptr) {
798+
if (info->klass == nullptr || info->jniObjectReferenceControlBlock == nullptr) {
803799
Helpers::abort_application (
804-
Util::monodroid_strdup_printf (
805-
"The type `%s.%s` is missing required instance fields! handle=%p handle_type=%p refs_added=%p key_handle=%p",
800+
std::format (
801+
"The type `{}.{} is missing required instance fields! jniObjectReferenceControlBlock={:p}",
806802
type->_namespace,
807803
type->_typename,
808-
info->handle,
809-
info->handle_type,
810-
info->refs_added,
811-
info->key_handle
804+
static_cast<void*>(info->jniObjectReferenceControlBlock)
812805
)
813806
);
814807
}

src/native/mono/monodroid/osbridge.cc

Lines changed: 70 additions & 40 deletions
Original file line numberDiff line numberDiff line change
@@ -42,9 +42,6 @@ const uint32_t OSBridge::NUM_GC_BRIDGE_TYPES = NUM_XA_GC_BRIDGE_TYPES + NUM_J
4242
OSBridge::MonoJavaGCBridgeInfo OSBridge::mono_java_gc_bridge_info [NUM_GC_BRIDGE_TYPES];
4343

4444
OSBridge::MonoJavaGCBridgeInfo OSBridge::empty_bridge_info = {
45-
nullptr,
46-
nullptr,
47-
nullptr,
4845
nullptr,
4946
nullptr
5047
};
@@ -76,9 +73,13 @@ OSBridge::clear_mono_java_gc_bridge_info ()
7673
for (uint32_t c = 0; c < NUM_GC_BRIDGE_TYPES; c++) {
7774
MonoJavaGCBridgeInfo *info = &mono_java_gc_bridge_info [c];
7875
info->klass = nullptr;
79-
info->handle = nullptr;
80-
info->handle_type = nullptr;
81-
info->refs_added = nullptr;
76+
auto control_block = reinterpret_cast<JniObjectReferenceControlBlock*>(info->jniObjectReferenceControlBlock);
77+
if (control_block == nullptr) [[unlikely]] {
78+
continue;
79+
}
80+
control_block->handle = nullptr;
81+
control_block->handle_type = 0;
82+
control_block->refs_added = 0;
8283
}
8384
}
8485

@@ -102,6 +103,19 @@ OSBridge::get_gc_bridge_index (MonoClass *klass)
102103
: -1;
103104
}
104105

106+
OSBridge::JniObjectReferenceControlBlock*
107+
OSBridge::get_gc_control_block_for_object (MonoObject *obj)
108+
{
109+
MonoJavaGCBridgeInfo *bridge_info = get_gc_bridge_info_for_object (obj);
110+
if (bridge_info == nullptr) {
111+
return nullptr;
112+
}
113+
114+
JniObjectReferenceControlBlock *control_block = nullptr;
115+
mono_field_get_value (obj, bridge_info->jniObjectReferenceControlBlock, &control_block);
116+
return control_block;
117+
}
118+
105119
OSBridge::MonoJavaGCBridgeInfo *
106120
OSBridge::get_gc_bridge_info_for_class (MonoClass *klass)
107121
{
@@ -459,11 +473,12 @@ OSBridge::take_global_ref_jni (JNIEnv *env, MonoObject *obj)
459473
jobject handle, weak;
460474
int type = JNIGlobalRefType;
461475

462-
MonoJavaGCBridgeInfo *bridge_info = get_gc_bridge_info_for_object (obj);
463-
if (bridge_info == nullptr)
476+
JniObjectReferenceControlBlock *control_block = get_gc_control_block_for_object (obj);
477+
if (control_block == nullptr) {
464478
return 0;
479+
}
465480

466-
mono_field_get_value (obj, bridge_info->handle, &weak);
481+
mono_field_get_value (obj, reinterpret_cast<MonoClassField*>(control_block->handle), &weak);
467482
handle = env->NewGlobalRef (weak);
468483
if (gref_log) {
469484
fprintf (gref_log, "*try_take_global obj=%p -> wref=%p handle=%p\n", obj, weak, handle);
@@ -476,8 +491,8 @@ OSBridge::take_global_ref_jni (JNIEnv *env, MonoObject *obj)
476491
" at [[gc:take_global_ref_jni]]", 0);
477492
} else if (Logger::gc_spew_enabled ()) [[unlikely]] {
478493
void *key_handle = nullptr;
479-
if (bridge_info->key_handle) {
480-
mono_field_get_value (obj, bridge_info->key_handle, &key_handle);
494+
if (control_block->weak_handle) {
495+
mono_field_get_value (obj, reinterpret_cast<MonoClassField*>(control_block->weak_handle), &key_handle);
481496
}
482497

483498
MonoClass *klass = mono_object_get_class (obj);
@@ -491,8 +506,8 @@ OSBridge::take_global_ref_jni (JNIEnv *env, MonoObject *obj)
491506
free (message);
492507
}
493508

494-
mono_field_set_value (obj, bridge_info->handle, &handle);
495-
mono_field_set_value (obj, bridge_info->handle_type, &type);
509+
mono_field_set_value (obj, reinterpret_cast<MonoClassField*>(control_block->handle), &handle);
510+
mono_field_set_value (obj, reinterpret_cast<MonoClassField*>(control_block->handle_type), &type);
496511

497512
_monodroid_weak_gref_delete (weak, get_object_ref_type (env, weak),
498513
"finalizer", gettid (), " at [[gc:take_global_ref_jni]]", 0);
@@ -507,11 +522,12 @@ OSBridge::take_weak_global_ref_jni (JNIEnv *env, MonoObject *obj)
507522
jobject handle, weak;
508523
int type = JNIWeakGlobalRefType;
509524

510-
MonoJavaGCBridgeInfo *bridge_info = get_gc_bridge_info_for_object (obj);
511-
if (bridge_info == nullptr)
525+
JniObjectReferenceControlBlock *control_block = get_gc_control_block_for_object (obj);
526+
if (control_block == nullptr) {
512527
return 0;
528+
}
513529

514-
mono_field_get_value (obj, bridge_info->handle, &handle);
530+
mono_field_get_value (obj, reinterpret_cast<MonoClassField*>(control_block->handle), &handle);
515531
if (gref_log) {
516532
fprintf (gref_log, "*take_weak obj=%p; handle=%p\n", obj, handle);
517533
fflush (gref_log);
@@ -522,8 +538,8 @@ OSBridge::take_weak_global_ref_jni (JNIEnv *env, MonoObject *obj)
522538
weak, get_object_ref_type (env, weak),
523539
"finalizer", gettid (), " at [[gc:take_weak_global_ref_jni]]", 0);
524540

525-
mono_field_set_value (obj, bridge_info->handle, &weak);
526-
mono_field_set_value (obj, bridge_info->handle_type, &type);
541+
mono_field_set_value (obj, reinterpret_cast<MonoClassField*>(control_block->handle), &weak);
542+
mono_field_set_value (obj, reinterpret_cast<MonoClassField*>(control_block->handle_type), &type);
527543

528544
_monodroid_gref_log_delete (handle, get_object_ref_type (env, handle),
529545
"finalizer", gettid (), " at [[gc:take_weak_global_ref_jni]]", 0);
@@ -558,13 +574,23 @@ OSBridge::gc_bridge_class_kind (MonoClass *klass)
558574
mono_bool
559575
OSBridge::gc_is_bridge_object (MonoObject *object)
560576
{
561-
void *handle;
577+
if (object == nullptr) [[unlikely]] {
578+
log_debug (LOG_GC, "gc_is_bridge_object was passed a NULL object pointer");
579+
return FALSE;
580+
}
562581

563-
MonoJavaGCBridgeInfo *bridge_info = get_gc_bridge_info_for_object (object);
564-
if (bridge_info == nullptr)
565-
return 0;
582+
JniObjectReferenceControlBlock *control_block = get_gc_control_block_for_object (object);
583+
if (control_block == nullptr) {
584+
return FALSE;
585+
}
586+
587+
if (control_block->handle == nullptr) {
588+
log_warn (LOG_GC, "gc_is_bridge_object: control block's handle is NULL");
589+
return FALSE;
590+
}
566591

567-
mono_field_get_value (object, bridge_info->handle, &handle);
592+
void *handle;
593+
mono_field_get_value (object, reinterpret_cast<MonoClassField*>(control_block->handle), &handle);
568594
if (handle == nullptr) {
569595
#if DEBUG
570596
MonoClass *mclass = mono_object_get_class (object);
@@ -574,10 +600,10 @@ OSBridge::gc_is_bridge_object (MonoObject *object)
574600
optional_string (mono_class_get_name (mclass))
575601
);
576602
#endif
577-
return 0;
603+
return FALSE;
578604
}
579605

580-
return 1;
606+
return TRUE;
581607
}
582608

583609
// Add a reference from an IGCUserPeer jobject to another jobject
@@ -604,10 +630,11 @@ mono_bool
604630
OSBridge::load_reference_target (OSBridge::AddReferenceTarget target, OSBridge::MonoJavaGCBridgeInfo** bridge_info, jobject *handle)
605631
{
606632
if (target.is_mono_object) {
607-
*bridge_info = get_gc_bridge_info_for_object (target.obj);
608-
if (!*bridge_info)
633+
JniObjectReferenceControlBlock *control_block = get_gc_control_block_for_object (target.obj);
634+
if (control_block == nullptr) {
609635
return FALSE;
610-
mono_field_get_value (target.obj, (*bridge_info)->handle, handle);
636+
}
637+
mono_field_get_value (target.obj, reinterpret_cast<MonoClassField*>(control_block->handle), handle);
611638
} else {
612639
*handle = target.jobj;
613640
}
@@ -649,7 +676,11 @@ OSBridge::add_reference (JNIEnv *env, OSBridge::AddReferenceTarget target, OSBri
649676
// Java temporaries do not need this because the entire GCUserPeer is discarded.
650677
if (success && target.is_mono_object) {
651678
int ref_val = 1;
652-
mono_field_set_value (target.obj, bridge_info->refs_added, &ref_val);
679+
JniObjectReferenceControlBlock *control_block = get_gc_control_block_for_object (target.obj);
680+
if (control_block == nullptr) {
681+
return FALSE;
682+
}
683+
mono_field_set_value (target.obj, reinterpret_cast<MonoClassField*>(control_block->refs_added), &ref_val);
653684
}
654685

655686
#if DEBUG
@@ -867,14 +898,13 @@ OSBridge::gc_cleanup_after_java_collection (JNIEnv *env, int num_sccs, MonoGCBri
867898
sccs [i]->is_alive = 0;
868899

869900
for (j = 0; j < sccs [i]->num_objs; j++) {
870-
MonoJavaGCBridgeInfo *bridge_info;
871-
872901
obj = sccs [i]->objs [j];
873902

874-
bridge_info = get_gc_bridge_info_for_object (obj);
875-
if (bridge_info == nullptr)
903+
JniObjectReferenceControlBlock *control_block = get_gc_control_block_for_object (obj);
904+
if (control_block == nullptr) {
876905
continue;
877-
mono_field_get_value (obj, bridge_info->handle, &jref);
906+
}
907+
mono_field_get_value (obj, reinterpret_cast<MonoClassField*>(control_block->handle), &jref);
878908
if (jref) {
879909
alive++;
880910
if (j > 0) {
@@ -884,7 +914,7 @@ OSBridge::gc_cleanup_after_java_collection (JNIEnv *env, int num_sccs, MonoGCBri
884914
);
885915
}
886916
sccs [i]->is_alive = 1;
887-
mono_field_get_value (obj, bridge_info->refs_added, &refs_added);
917+
mono_field_get_value (obj, (MonoClassField*) control_block->refs_added, &refs_added);
888918
if (refs_added) {
889919
jclass java_class = env->GetObjectClass (jref);
890920
clear_method_id = env->GetMethodID (java_class, "monodroidClearReferences", "()V");
@@ -951,13 +981,13 @@ OSBridge::gc_cross_references (int num_sccs, MonoGCBridgeSCC **sccs, int num_xre
951981
for (j = 0; j < sccs [i]->num_objs; ++j) {
952982
MonoObject *obj = sccs [i]->objs [j];
953983

954-
MonoJavaGCBridgeInfo *bridge_info = get_gc_bridge_info_for_object (obj);
984+
JniObjectReferenceControlBlock *control_block = get_gc_control_block_for_object (obj);
955985
jobject handle = 0;
956986
void *key_handle = nullptr;
957-
if (bridge_info != nullptr) {
958-
mono_field_get_value (obj, bridge_info->handle, &handle);
959-
if (bridge_info->key_handle != nullptr) {
960-
mono_field_get_value (obj, bridge_info->key_handle, &key_handle);
987+
if (control_block != nullptr) {
988+
mono_field_get_value (obj, reinterpret_cast<MonoClassField*>(control_block->handle), &handle);
989+
if (control_block->weak_handle != nullptr) {
990+
mono_field_get_value (obj, reinterpret_cast<MonoClassField*>(control_block->weak_handle), &key_handle);
961991
}
962992
}
963993
MonoClass *klass = mono_object_get_class (obj);

src/native/mono/monodroid/osbridge.hh

Lines changed: 10 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -37,10 +37,15 @@ namespace xamarin::android::internal
3737
struct MonoJavaGCBridgeInfo
3838
{
3939
MonoClass *klass;
40-
MonoClassField *handle;
41-
MonoClassField *handle_type;
42-
MonoClassField *refs_added;
43-
MonoClassField *key_handle;
40+
MonoClassField *jniObjectReferenceControlBlock;
41+
};
42+
43+
struct JniObjectReferenceControlBlock
44+
{
45+
jobject handle;
46+
int handle_type;
47+
jobject weak_handle;
48+
int refs_added;
4449
};
4550

4651
// add_reference can work with objects which are either MonoObjects with java peers, or raw jobjects
@@ -127,6 +132,7 @@ namespace xamarin::android::internal
127132

128133
private:
129134
int get_gc_bridge_index (MonoClass *klass);
135+
JniObjectReferenceControlBlock* get_gc_control_block_for_object (MonoObject *obj);
130136
MonoJavaGCBridgeInfo* get_gc_bridge_info_for_class (MonoClass *klass);
131137
MonoJavaGCBridgeInfo* get_gc_bridge_info_for_object (MonoObject *object);
132138
char get_object_ref_type (JNIEnv *env, void *handle);

0 commit comments

Comments
 (0)