diff --git a/src/Java.Interop/Java.Interop/IJavaPeerable.cs b/src/Java.Interop/Java.Interop/IJavaPeerable.cs index 612744b10..a1888e7e8 100644 --- a/src/Java.Interop/Java.Interop/IJavaPeerable.cs +++ b/src/Java.Interop/Java.Interop/IJavaPeerable.cs @@ -37,6 +37,8 @@ public interface IJavaPeerable : IDisposable void Disposed (); /// void Finalized (); + + IntPtr JniObjectReferenceControlBlock => IntPtr.Zero; } } diff --git a/src/Java.Interop/Java.Interop/JavaException.cs b/src/Java.Interop/Java.Interop/JavaException.cs index b07735b5a..23280e05c 100644 --- a/src/Java.Interop/Java.Interop/JavaException.cs +++ b/src/Java.Interop/Java.Interop/JavaException.cs @@ -5,7 +5,7 @@ namespace Java.Interop { [JniTypeSignature (JniTypeName, GenerateJavaPeer=false)] - unsafe public class JavaException : Exception, IJavaPeerable + unsafe public partial class JavaException : Exception, IJavaPeerable { internal const string JniTypeName = "java/lang/Throwable"; readonly static JniPeerMembers _members = new JniPeerMembers (JniTypeName, typeof (JavaException)); @@ -18,13 +18,7 @@ unsafe public class JavaException : Exception, IJavaPeerable JniObjectReference reference; #endif // FEATURE_JNIOBJECTREFERENCE_SAFEHANDLES #if FEATURE_JNIOBJECTREFERENCE_INTPTRS - IntPtr handle; - JniObjectReferenceType handle_type; - #pragma warning disable 0169 - // Used by JavaInteropGCBridge - IntPtr weak_handle; - int refs_added; - #pragma warning restore 0169 + unsafe JniObjectReferenceControlBlock* jniObjectReferenceControlBlock; #endif // FEATURE_JNIOBJECTREFERENCE_INTPTRS protected static readonly JniObjectReference* InvalidJniObjectReference = null; @@ -106,7 +100,11 @@ public JniObjectReference PeerReference { return reference; #endif // FEATURE_JNIOBJECTREFERENCE_SAFEHANDLES #if FEATURE_JNIOBJECTREFERENCE_INTPTRS - return new JniObjectReference (handle, handle_type); + var c = jniObjectReferenceControlBlock; + if (c == null) { + return default; + } + return new JniObjectReference (c->handle, (JniObjectReferenceType) c->handle_type); #endif // FEATURE_JNIOBJECTREFERENCE_INTPTRS } } @@ -137,8 +135,14 @@ protected void SetPeerReference (ref JniObjectReference reference, JniObjectRefe this.reference = reference; #endif // FEATURE_JNIOBJECTREFERENCE_SAFEHANDLES #if FEATURE_JNIOBJECTREFERENCE_INTPTRS - this.handle = reference.Handle; - this.handle_type = reference.Type; + var c = jniObjectReferenceControlBlock; + if (c == null) { + c = jniObjectReferenceControlBlock = + Java.Interop.JniObjectReferenceControlBlock.Alloc (reference); + } else { + c->handle = reference.Handle; + c->handle_type = (int) reference.Type; + } #endif // FEATURE_JNIOBJECTREFERENCE_INTPTRS JniObjectReference.Dispose (ref reference, options); @@ -260,11 +264,13 @@ protected void SetJavaStackTrace (JniObjectReference peerReferenceOverride = def void IJavaPeerable.Disposed () { + JniManagedPeerState |= Disposed; Dispose (disposing: true); } void IJavaPeerable.Finalized () { + JniManagedPeerState |= Disposed; Dispose (disposing: false); } @@ -281,7 +287,15 @@ void IJavaPeerable.SetJniManagedPeerState (JniManagedPeerStates value) void IJavaPeerable.SetPeerReference (JniObjectReference reference) { SetPeerReference (ref reference, JniObjectReferenceOptions.Copy); +#if FEATURE_JNIOBJECTREFERENCE_INTPTRS + if (!reference.IsValid && JniManagedPeerState.HasFlag (Disposed)) { + Java.Interop.JniObjectReferenceControlBlock.Free (ref jniObjectReferenceControlBlock); + } +#endif // FEATURE_JNIOBJECTREFERENCE_INTPTRS } + + IntPtr IJavaPeerable.JniObjectReferenceControlBlock => + (IntPtr) jniObjectReferenceControlBlock; } } diff --git a/src/Java.Interop/Java.Interop/JavaObject.cs b/src/Java.Interop/Java.Interop/JavaObject.cs index fd3d68d3e..04a14861f 100644 --- a/src/Java.Interop/Java.Interop/JavaObject.cs +++ b/src/Java.Interop/Java.Interop/JavaObject.cs @@ -8,7 +8,7 @@ namespace Java.Interop { [JniTypeSignature ("java/lang/Object", GenerateJavaPeer=false)] [Serializable] - unsafe public class JavaObject : IJavaPeerable + unsafe public partial class JavaObject : IJavaPeerable { internal const DynamicallyAccessedMemberTypes Constructors = DynamicallyAccessedMemberTypes.PublicConstructors | DynamicallyAccessedMemberTypes.NonPublicConstructors; @@ -25,13 +25,7 @@ unsafe public class JavaObject : IJavaPeerable [NonSerialized] JniObjectReference reference; #endif // FEATURE_JNIOBJECTREFERENCE_SAFEHANDLES #if FEATURE_JNIOBJECTREFERENCE_INTPTRS - [NonSerialized] IntPtr handle; - [NonSerialized] JniObjectReferenceType handle_type; - #pragma warning disable 0169 - // Used by JavaInteropGCBridge - [NonSerialized] IntPtr weak_handle; - [NonSerialized] int refs_added; - #pragma warning restore 0169 + [NonSerialized] unsafe JniObjectReferenceControlBlock* jniObjectReferenceControlBlock; #endif // FEATURE_JNIOBJECTREFERENCE_INTPTRS protected static readonly JniObjectReference* InvalidJniObjectReference = null; @@ -41,13 +35,17 @@ unsafe public class JavaObject : IJavaPeerable JniEnvironment.Runtime.ValueManager.FinalizePeer (this); } - public JniObjectReference PeerReference { + public unsafe JniObjectReference PeerReference { get { #if FEATURE_JNIOBJECTREFERENCE_SAFEHANDLES return reference; #endif // FEATURE_JNIOBJECTREFERENCE_SAFEHANDLES #if FEATURE_JNIOBJECTREFERENCE_INTPTRS - return new JniObjectReference (handle, handle_type); + var c = jniObjectReferenceControlBlock; + if (c == null) { + return default; + } + return new JniObjectReference (c->handle, (JniObjectReferenceType) c->handle_type); #endif // FEATURE_JNIOBJECTREFERENCE_INTPTRS } } @@ -92,8 +90,14 @@ protected void SetPeerReference (ref JniObjectReference reference, JniObjectRefe this.reference = reference; #endif // FEATURE_JNIOBJECTREFERENCE_SAFEHANDLES #if FEATURE_JNIOBJECTREFERENCE_INTPTRS - this.handle = reference.Handle; - this.handle_type = reference.Type; + var c = jniObjectReferenceControlBlock; + if (c == null) { + c = jniObjectReferenceControlBlock = + Java.Interop.JniObjectReferenceControlBlock.Alloc (reference); + } else { + c->handle = reference.Handle; + c->handle_type = (int) reference.Type; + } #endif // FEATURE_JNIOBJECTREFERENCE_INTPTRS JniObjectReference.Dispose (ref reference, options); @@ -148,11 +152,13 @@ public override unsafe int GetHashCode () void IJavaPeerable.Disposed () { + managedPeerState |= Disposed; Dispose (disposing: true); } void IJavaPeerable.Finalized () { + managedPeerState |= Disposed; Dispose (disposing: false); } @@ -169,7 +175,16 @@ void IJavaPeerable.SetJniManagedPeerState (JniManagedPeerStates value) void IJavaPeerable.SetPeerReference (JniObjectReference reference) { SetPeerReference (ref reference, JniObjectReferenceOptions.Copy); + +#if FEATURE_JNIOBJECTREFERENCE_INTPTRS + if (!reference.IsValid && managedPeerState.HasFlag (Disposed)) { + Java.Interop.JniObjectReferenceControlBlock.Free (ref jniObjectReferenceControlBlock); + } +#endif // FEATURE_JNIOBJECTREFERENCE_INTPTRS } + + IntPtr IJavaPeerable.JniObjectReferenceControlBlock => + (IntPtr) jniObjectReferenceControlBlock; } } diff --git a/src/Java.Interop/Java.Interop/JniManagedPeerStates.cs b/src/Java.Interop/Java.Interop/JniManagedPeerStates.cs index 765fb0b2d..b4f1fba3c 100644 --- a/src/Java.Interop/Java.Interop/JniManagedPeerStates.cs +++ b/src/Java.Interop/Java.Interop/JniManagedPeerStates.cs @@ -11,4 +11,12 @@ public enum JniManagedPeerStates { /// Replaceable = (1 << 1), } + + partial class JavaObject { + const JniManagedPeerStates Disposed = (JniManagedPeerStates) (1 << 2); + } + + partial class JavaException { + const JniManagedPeerStates Disposed = (JniManagedPeerStates) (1 << 2); + } } diff --git a/src/Java.Interop/Java.Interop/JniObjectReferenceControlBlock.cs b/src/Java.Interop/Java.Interop/JniObjectReferenceControlBlock.cs new file mode 100644 index 000000000..3f2d6f8ec --- /dev/null +++ b/src/Java.Interop/Java.Interop/JniObjectReferenceControlBlock.cs @@ -0,0 +1,30 @@ +using System; +using System.Runtime.InteropServices; + +namespace Java.Interop; + +internal struct JniObjectReferenceControlBlock { + public IntPtr handle; + public int handle_type; + public IntPtr weak_handle; + public int refs_added; + + public static readonly int Size = Marshal.SizeOf(); + + public static unsafe JniObjectReferenceControlBlock* Alloc (JniObjectReference reference) + { + var value = (JniObjectReferenceControlBlock*) NativeMemory.AllocZeroed (1, (uint) Size); + value->handle = reference.Handle; + value->handle_type = (int) reference.Type; + return value; + } + + public static unsafe void Free (ref JniObjectReferenceControlBlock* value) + { + if (value == null) { + return; + } + NativeMemory.Free (value); + value = null; + } +} diff --git a/src/Java.Interop/PublicAPI.Unshipped.txt b/src/Java.Interop/PublicAPI.Unshipped.txt index 90cde5ae6..90fda8ff8 100644 --- a/src/Java.Interop/PublicAPI.Unshipped.txt +++ b/src/Java.Interop/PublicAPI.Unshipped.txt @@ -11,3 +11,4 @@ Java.Interop.JniRuntime.JniTypeManager.GetInvokerType(System.Type! type) -> Syst Java.Interop.JniRuntime.JniValueManager.GetPeer(Java.Interop.JniObjectReference reference, System.Type? targetType = null) -> Java.Interop.IJavaPeerable? Java.Interop.JniTypeSignatureAttribute.InvokerType.get -> System.Type? Java.Interop.JniTypeSignatureAttribute.InvokerType.set -> void +Java.Interop.IJavaPeerable.JniObjectReferenceControlBlock.get -> nint diff --git a/src/java-interop/java-interop-gc-bridge-mono.cc b/src/java-interop/java-interop-gc-bridge-mono.cc index aca306c60..cebe8ece9 100644 --- a/src/java-interop/java-interop-gc-bridge-mono.cc +++ b/src/java-interop/java-interop-gc-bridge-mono.cc @@ -30,12 +30,16 @@ using namespace xamarin::android; typedef struct MonoJavaGCBridgeInfo { MonoClass *klass; - MonoClassField *handle; - MonoClassField *handle_type; - MonoClassField *refs_added; - MonoClassField *weak_handle; + MonoClassField *jniObjectReferenceControlBlock; } MonoJavaGCBridgeInfo; +typedef struct JniObjectReferenceControlBlock { + jobject handle; + int handle_type; + jobject weak_handle; + int refs_added; +} JniObjectReferenceControlBlock; + #define NUM_GC_BRIDGE_TYPES (4) struct JavaInteropGCBridge { @@ -72,6 +76,8 @@ struct JavaInteropGCBridge { char *gref_path, *lref_path; int gref_log_level, lref_log_level; int gref_cleanup, lref_cleanup; + + JavaInteropMarkCrossReferencesCallback mark_cross_references; }; static jobject @@ -366,13 +372,10 @@ java_interop_gc_bridge_register_bridgeable_type ( MonoJavaGCBridgeInfo *info = &bridge->mono_java_gc_bridge_info [i]; info->klass = mono_class_from_mono_type (type); - info->handle = mono_class_get_field_from_name (info->klass, const_cast ("handle")); - info->handle_type = mono_class_get_field_from_name (info->klass, const_cast ("handle_type")); - info->refs_added = mono_class_get_field_from_name (info->klass, const_cast ("refs_added")); - info->weak_handle = mono_class_get_field_from_name (info->klass, const_cast ("weak_handle")); - if (info->klass == NULL || info->handle == NULL || info->handle_type == NULL || - info->refs_added == NULL || info->weak_handle == NULL) + info->jniObjectReferenceControlBlock = mono_class_get_field_from_name (info->klass, const_cast ("jniObjectReferenceControlBlock")); + + if (info->klass == NULL || info->jniObjectReferenceControlBlock == NULL) return -1; bridge->num_bridge_types++; return 0; @@ -780,6 +783,18 @@ get_gc_bridge_info_for_object (JavaInteropGCBridge *bridge, MonoObject *object) return get_gc_bridge_info_for_class (bridge, mono_object_get_class (object)); } +static JniObjectReferenceControlBlock* +get_gc_control_block_for_object (JavaInteropGCBridge *bridge, MonoObject *obj) +{ + MonoJavaGCBridgeInfo *bridge_info = get_gc_bridge_info_for_object (bridge, obj); + if (bridge_info == NULL) + return NULL; + + JniObjectReferenceControlBlock *control_block; + mono_field_get_value (obj, bridge_info->jniObjectReferenceControlBlock, &control_block); + return control_block; +} + typedef mono_bool (*MonodroidGCTakeRefFunc) (JavaInteropGCBridge *bridge, JNIEnv *env, MonoObject *obj, const char *thread_name, int64_t thread_id); static MonodroidGCTakeRefFunc take_global_ref; @@ -788,12 +803,11 @@ static MonodroidGCTakeRefFunc take_weak_global_ref; static mono_bool take_global_ref_java (JavaInteropGCBridge *bridge, JNIEnv *env, MonoObject *obj, const char *thread_name, int64_t thread_id) { - MonoJavaGCBridgeInfo *bridge_info = get_gc_bridge_info_for_object (bridge, obj); - if (bridge_info == NULL) + JniObjectReferenceControlBlock *control_block = get_gc_control_block_for_object (bridge, obj); + if (control_block == NULL) return 0; - jobject weak; - mono_field_get_value (obj, bridge_info->weak_handle, &weak); + jobject weak = control_block->weak_handle; jobject handle = env->CallObjectMethod (weak, bridge->WeakReference_get); log_gref (bridge, "*try_take_global_2_1 obj=%p -> wref=%p handle=%p\n", obj, weak, handle); @@ -808,12 +822,10 @@ take_global_ref_java (JavaInteropGCBridge *bridge, JNIEnv *env, MonoObject *obj, java_interop_gc_bridge_weak_gref_log_delete (bridge, weak, get_object_ref_type (env, weak), thread_name, thread_id, "take_global_ref_java"); env->DeleteGlobalRef (weak); weak = NULL; - mono_field_set_value (obj, bridge_info->weak_handle, &weak); - mono_field_set_value (obj, bridge_info->handle, &handle); - - int type = JNIGlobalRefType; - mono_field_set_value (obj, bridge_info->handle_type, &type); + control_block->weak_handle = weak; + control_block->handle = handle; + control_block->handle_type = JNIGlobalRefType; return handle != NULL; } @@ -821,12 +833,11 @@ take_global_ref_java (JavaInteropGCBridge *bridge, JNIEnv *env, MonoObject *obj, static mono_bool take_weak_global_ref_java (JavaInteropGCBridge *bridge, JNIEnv *env, MonoObject *obj, const char *thread_name, int64_t thread_id) { - MonoJavaGCBridgeInfo *bridge_info = get_gc_bridge_info_for_object (bridge, obj); - if (bridge_info == NULL) + JniObjectReferenceControlBlock *control_block = get_gc_control_block_for_object (bridge, obj); + if (control_block == NULL) return 0; - jobject handle; - mono_field_get_value (obj, bridge_info->handle, &handle); + jobject handle = control_block->handle; jobject weaklocal = env->NewObject (bridge->WeakReference_class, bridge->WeakReference_init, handle); jobject weakglobal = env->NewGlobalRef (weaklocal); @@ -838,8 +849,8 @@ take_weak_global_ref_java (JavaInteropGCBridge *bridge, JNIEnv *env, MonoObject java_interop_gc_bridge_gref_log_delete (bridge, handle, get_object_ref_type (env, handle), thread_name, thread_id, "take_weak_global_ref_2_1_compat"); env->DeleteGlobalRef (handle); - - mono_field_set_value (obj, bridge_info->weak_handle, &weakglobal); + control_block->handle = NULL; + control_block->weak_handle = weakglobal; return 1; } @@ -847,13 +858,11 @@ take_weak_global_ref_java (JavaInteropGCBridge *bridge, JNIEnv *env, MonoObject static mono_bool take_global_ref_jni (JavaInteropGCBridge *bridge, JNIEnv *env, MonoObject *obj, const char *thread_name, int64_t thread_id) { - MonoJavaGCBridgeInfo *bridge_info = get_gc_bridge_info_for_object (bridge, obj); - if (bridge_info == NULL) + JniObjectReferenceControlBlock *control_block = get_gc_control_block_for_object (bridge, obj); + if (control_block == NULL) return 0; - jobject weak; - mono_field_get_value (obj, bridge_info->handle, &weak); - + jobject weak = control_block->handle; jobject handle = env->NewGlobalRef (weak); log_gref (bridge, "*try_take_global obj=%p -> wref=%p handle=%p\n", obj, weak, handle); @@ -868,22 +877,20 @@ take_global_ref_jni (JavaInteropGCBridge *bridge, JNIEnv *env, MonoObject *obj, thread_name, thread_id, "take_global_ref_jni"); env->DeleteWeakGlobalRef (weak); - mono_field_set_value (obj, bridge_info->handle, &handle); + control_block->handle = handle; + control_block->handle_type = JNIGlobalRefType; - int type = JNIGlobalRefType; - mono_field_set_value (obj, bridge_info->handle_type, &type); return handle != NULL; } static mono_bool take_weak_global_ref_jni (JavaInteropGCBridge *bridge, JNIEnv *env, MonoObject *obj, const char *thread_name, int64_t thread_id) { - MonoJavaGCBridgeInfo *bridge_info = get_gc_bridge_info_for_object (bridge, obj); - if (bridge_info == NULL) + JniObjectReferenceControlBlock *control_block = get_gc_control_block_for_object (bridge, obj); + if (control_block == NULL) return 0; - jobject handle; - mono_field_get_value (obj, bridge_info->handle, &handle); + jobject handle = control_block->handle; log_gref (bridge, "*take_weak obj=%p; handle=%p\n", obj, handle); @@ -896,10 +903,8 @@ take_weak_global_ref_jni (JavaInteropGCBridge *bridge, JNIEnv *env, MonoObject * thread_name, thread_id, "take_weak_global_ref_jni"); env->DeleteGlobalRef (handle); - mono_field_set_value (obj, bridge_info->handle, &weak); - - int type = JNIWeakGlobalRefType; - mono_field_set_value (obj, bridge_info->handle_type, &type); + control_block->handle = weak; + control_block->handle_type = JNIWeakGlobalRefType; return 1; } @@ -921,17 +926,18 @@ get_add_reference_method (JavaInteropGCBridge *bridge, JNIEnv *env, jobject obj, } static mono_bool -add_reference (JavaInteropGCBridge *bridge, JNIEnv *env, MonoObject *obj, MonoJavaGCBridgeInfo *bridge_info, MonoObject *reffed_obj) +add_reference (JavaInteropGCBridge *bridge, JNIEnv *env, MonoObject *obj, JniObjectReferenceControlBlock *control_block, MonoObject *reffed_obj) { MonoClass *klass = mono_object_get_class (obj); - jobject handle; - mono_field_get_value (obj, bridge_info->handle, &handle); + jobject handle = control_block->handle; jmethodID add_method_id = get_add_reference_method (bridge, env, handle, klass); if (add_method_id) { - jobject reffed_handle; - mono_field_get_value (reffed_obj, bridge_info->handle, &reffed_handle); + JniObjectReferenceControlBlock *reffed_control_block = get_gc_control_block_for_object (bridge, reffed_obj); + if (reffed_control_block == NULL) + return 0; + jobject reffed_handle = reffed_control_block->handle; env->CallVoidMethod (handle, add_method_id, reffed_handle); #if DEBUG if (bridge->gref_log_level > 1) @@ -979,28 +985,30 @@ gc_prepare_for_java_collection (JavaInteropGCBridge *bridge, JNIEnv *env, int nu /* add java refs for items on the list which reference each other */ for (int i = 0; i < num_sccs; i++) { MonoGCBridgeSCC *scc = sccs [i]; - MonoJavaGCBridgeInfo *bridge_info = NULL; + + JniObjectReferenceControlBlock *control_block = NULL; + /* start at the second item, ref j from j-1 */ for (int j = 1; j < scc->num_objs; j++) { - bridge_info = get_gc_bridge_info_for_object (bridge, scc->objs [j-1]); - if (bridge_info != NULL && add_reference (bridge, env, scc->objs [j-1], bridge_info, scc->objs [j])) { - mono_field_set_value (scc->objs [j-1], bridge_info->refs_added, &ref_val); + control_block = get_gc_control_block_for_object (bridge, scc->objs [j-1]); + if (control_block != NULL && add_reference (bridge, env, scc->objs [j-1], control_block, scc->objs [j])) { + control_block->refs_added = ref_val; } } /* ref the first from the last */ if (scc->num_objs > 1) { - bridge_info = get_gc_bridge_info_for_object (bridge, scc->objs [scc->num_objs-1]); - if (bridge_info != NULL && add_reference (bridge, env, scc->objs [scc->num_objs-1], bridge_info, scc->objs [0])) { - mono_field_set_value (scc->objs [scc->num_objs-1], bridge_info->refs_added, &ref_val); + control_block = get_gc_control_block_for_object (bridge, scc->objs [scc->num_objs-1]); + if (control_block != NULL && add_reference (bridge, env, scc->objs [scc->num_objs-1], control_block, scc->objs [0])) { + control_block->refs_added = ref_val; } } } /* add the cross scc refs */ for (int i = 0; i < num_xrefs; i++) { - MonoJavaGCBridgeInfo *bridge_info = get_gc_bridge_info_for_object (bridge, sccs [xrefs [i].src_scc_index]->objs [0]); - if (bridge_info != NULL && add_reference (bridge, env, sccs [xrefs [i].src_scc_index]->objs [0], bridge_info, sccs [xrefs [i].dst_scc_index]->objs [0])) { - mono_field_set_value (sccs [xrefs [i].src_scc_index]->objs [0], bridge_info->refs_added, &ref_val); + JniObjectReferenceControlBlock *control_block = get_gc_control_block_for_object (bridge, sccs [xrefs [i].src_scc_index]->objs [0]); + if (control_block != NULL && add_reference (bridge, env, sccs [xrefs [i].src_scc_index]->objs [0], control_block, sccs [xrefs [i].dst_scc_index]->objs [0])) { + control_block->refs_added = ref_val; } } @@ -1041,19 +1049,18 @@ gc_cleanup_after_java_collection (JavaInteropGCBridge *bridge, JNIEnv *env, int sccs [i]->is_alive = 0; for (int j = 0; j < sccs [i]->num_objs; j++) { MonoObject *obj = sccs [i]->objs [j]; - MonoJavaGCBridgeInfo *bridge_info = get_gc_bridge_info_for_object (bridge, obj); - if (bridge_info == NULL) + + JniObjectReferenceControlBlock *control_block = get_gc_control_block_for_object (bridge, obj); + if (control_block == NULL) continue; - jobject jref; - mono_field_get_value (obj, bridge_info->handle, &jref); + jobject jref = control_block->handle; if (jref) { alive++; if (j > 0) assert (sccs [i]->is_alive); sccs [i]->is_alive = 1; - int refs_added; - mono_field_get_value (obj, bridge_info->refs_added, &refs_added); + int refs_added = control_block->refs_added; if (refs_added) { jmethodID clear_method_id = get_clear_references_method (bridge, env, jref); if (clear_method_id) { @@ -1201,13 +1208,12 @@ gc_bridge_class_kind (MonoClass *klass) static mono_bool gc_is_bridge_object (MonoObject *object) { - void *handle; - MonoJavaGCBridgeInfo *bridge_info = get_gc_bridge_info_for_object (mono_bridge, object); - if (bridge_info == NULL) - return 0; + JniObjectReferenceControlBlock *control_block = get_gc_control_block_for_object (mono_bridge, object); + if (control_block == NULL) + return 0;; - mono_field_get_value (object, bridge_info->handle, &handle); + void *handle = control_block->handle; if (handle == NULL) { #if DEBUG MonoClass *mclass = mono_object_get_class (object); @@ -1279,6 +1285,46 @@ gc_cross_references (int num_sccs, MonoGCBridgeSCC **sccs, int num_xrefs, MonoGC free (thread_name); } +static void +managed_gc_cross_references (int num_sccs, MonoGCBridgeSCC **sccs, int num_xrefs, MonoGCBridgeXRef *xrefs) +{ + if (mono_bridge->mark_cross_references == NULL) { + assert (!"mono_bridge->mark_cross_references is NULL; WE SHOULD NOT BE EXECUTING"); + return; + } + int i; + + Srij_MarkCrossReferences cross_references = {}; + + cross_references.ComponentsLen = (void*) (intptr_t) num_sccs; + cross_references.Components = (Srij_StronglyConnectedComponent*) calloc (num_sccs, sizeof (Srij_StronglyConnectedComponent)); + for (i = 0; i < num_sccs; ++i) { + Srij_StronglyConnectedComponent *scc = &cross_references.Components [i]; + + scc->Count = (void*) (intptr_t) sccs [i]->num_objs; + scc->Context = (void**) calloc (sccs [i]->num_objs, sizeof (void*)); + for (int j = 0; j < sccs [i]->num_objs; ++j) { + MonoObject *obj = sccs [i]->objs [j]; + scc->Context [j] = get_gc_control_block_for_object (mono_bridge, obj); + } + } + + cross_references.CrossReferencesLen = (void*) (intptr_t) num_xrefs; + cross_references.CrossReferences = (Srij_ComponentCrossReference*) calloc (num_xrefs, sizeof (Srij_ComponentCrossReference)); + for (i = 0; i < num_xrefs; ++i) { + Srij_ComponentCrossReference *xref = &cross_references.CrossReferences [i]; + xref->SourceGroupIndex = (void*) (intptr_t) xrefs [i].src_scc_index; + xref->DestinationGroupIndex = (void*) (intptr_t) xrefs [i].dst_scc_index; + } + + mono_bridge->mark_cross_references (&cross_references); + + for (i = 0; i < num_sccs; ++i) { + Srij_StronglyConnectedComponent *scc = &cross_references.Components [i]; + sccs [i]->is_alive = scc->IsAlive; + } +} + int java_interop_gc_bridge_register_hooks (JavaInteropGCBridge *bridge, int weak_ref_kind) { @@ -1312,7 +1358,9 @@ java_interop_gc_bridge_register_hooks (JavaInteropGCBridge *bridge, int weak_ref bridge_cbs.bridge_version = SGEN_BRIDGE_VERSION; bridge_cbs.bridge_class_kind = gc_bridge_class_kind; bridge_cbs.is_bridge_object = gc_is_bridge_object; - bridge_cbs.cross_references = gc_cross_references; + bridge_cbs.cross_references = bridge->mark_cross_references + ? managed_gc_cross_references + : gc_cross_references; mono_gc_register_bridge_callbacks (&bridge_cbs); @@ -1328,3 +1376,24 @@ java_interop_gc_bridge_wait_for_bridge_processing (JavaInteropGCBridge *bridge) mono_gc_wait_for_bridge_processing (); return 0; } + +int +java_interop_gc_bridge_set_mark_cross_references (JavaInteropGCBridge *bridge, JavaInteropMarkCrossReferencesCallback markCrossReferences) +{ + if (bridge == NULL) + return -1; + + bridge->mark_cross_references = markCrossReferences; + + return 0; +} + +int +java_interop_gc_bridge_release_mark_cross_references_resources (JavaInteropGCBridge *bridge, Srij_MarkCrossReferences *crossReferences) +{ + if (bridge == NULL || crossReferences == NULL) + return -1; + + // leak it… + return 0; +} diff --git a/src/java-interop/java-interop-gc-bridge.h b/src/java-interop/java-interop-gc-bridge.h index 8c8444cb6..6715b33be 100644 --- a/src/java-interop/java-interop-gc-bridge.h +++ b/src/java-interop/java-interop-gc-bridge.h @@ -19,6 +19,26 @@ struct JavaInterop_System_RuntimeTypeHandle { void *value; }; +typedef struct Srij_ComponentCrossReference { + void *SourceGroupIndex; + void *DestinationGroupIndex; +} Srij_ComponentCrossReference; + +typedef struct Srij_StronglyConnectedComponent { + int IsAlive; + void *Count; + void **Context; +} Srij_StronglyConnectedComponent; + +typedef struct Srij_MarkCrossReferences { + void *ComponentsLen; + Srij_StronglyConnectedComponent *Components; + void* CrossReferencesLen; + Srij_ComponentCrossReference *CrossReferences; +} Srij_MarkCrossReferences; + +typedef void (*JavaInteropMarkCrossReferencesCallback) (Srij_MarkCrossReferences *crossReferences); + JAVA_INTEROP_API JavaInteropGCBridge *java_interop_gc_bridge_get_current (void); JAVA_INTEROP_API int java_interop_gc_bridge_set_current_once (JavaInteropGCBridge *bridge); @@ -32,6 +52,10 @@ JAVA_INTEROP_API int java_interop_gc_bridge_add_current_a JAVA_INTEROP_API int java_interop_gc_bridge_remove_current_app_domain (JavaInteropGCBridge *bridge); JAVA_INTEROP_API int java_interop_gc_bridge_set_bridge_processing_field (JavaInteropGCBridge *bridge, struct JavaInterop_System_RuntimeTypeHandle type_handle, char *field_name); + +JAVA_INTEROP_API int java_interop_gc_bridge_set_mark_cross_references (JavaInteropGCBridge *bridge, JavaInteropMarkCrossReferencesCallback markCrossReferences); +JAVA_INTEROP_API int java_interop_gc_bridge_release_mark_cross_references_resources (JavaInteropGCBridge *bridge, Srij_MarkCrossReferences *crossReferences); + JAVA_INTEROP_API int java_interop_gc_bridge_register_bridgeable_type (JavaInteropGCBridge *bridge, struct JavaInterop_System_RuntimeTypeHandle type_handle); JAVA_INTEROP_API int java_interop_gc_bridge_enable (JavaInteropGCBridge *bridge, int enable); diff --git a/tests/Java.Interop-Tests/Java.Interop/JavaObjectArrayTest.cs b/tests/Java.Interop-Tests/Java.Interop/JavaObjectArrayTest.cs index f02bbc06b..896c1c9a6 100644 --- a/tests/Java.Interop-Tests/Java.Interop/JavaObjectArrayTest.cs +++ b/tests/Java.Interop-Tests/Java.Interop/JavaObjectArrayTest.cs @@ -39,6 +39,30 @@ protected override bool SequenceEqual (IEnumerable a, IEnumerable b) { return JniMarshal.RecursiveEquals (a, b); } + + int beforeTestGrefCount; + + [SetUp] + public void LogBeginCurrentTestGrefCount () + { + beforeTestGrefCount = JniEnvironment.Runtime.GlobalReferenceCount; + JniEnvironment.Runtime.ObjectReferenceManager.WriteGlobalReferenceLine( + "{0}", + $"Begin {TestContext.CurrentContext.Test.FullName}; " + + $"GREFs={beforeTestGrefCount}; " + ); + } + + [TearDown] + public void LogEndCurrentTestToGrefCount () + { + int afterTestGrefCount = JniEnvironment.Runtime.GlobalReferenceCount; + JniEnvironment.Runtime.ObjectReferenceManager.WriteGlobalReferenceLine( + "{0}", + $"End {TestContext.CurrentContext.Test.FullName}; " + + $"GREFs={afterTestGrefCount}; diff={afterTestGrefCount - beforeTestGrefCount}" + ); + } } [TestFixture] diff --git a/tests/Java.Interop-Tests/Java.Interop/JavaObjectTest.cs b/tests/Java.Interop-Tests/Java.Interop/JavaObjectTest.cs index d90d5846f..33dac36fe 100644 --- a/tests/Java.Interop-Tests/Java.Interop/JavaObjectTest.cs +++ b/tests/Java.Interop-Tests/Java.Interop/JavaObjectTest.cs @@ -50,12 +50,12 @@ public void UnregisterFromRuntime () using (o = new JavaObject ()) { l = o.PeerReference.NewLocalRef (); Assert.AreEqual (JniObjectReferenceType.Global, o.PeerReference.Type); - Assert.AreEqual (registeredCount+1, JniRuntime.CurrentRuntime.ValueManager.GetSurfacedPeers ().Count); + Assert.AreEqual (registeredCount+1, JniRuntime.CurrentRuntime.ValueManager.GetSurfacedPeers ().Count, "registeredCount+1 should match!"); Assert.IsNotNull (JniRuntime.CurrentRuntime.ValueManager.PeekValue (l)); Assert.AreNotSame (l, o.PeerReference); Assert.AreSame (o, JniRuntime.CurrentRuntime.ValueManager.PeekValue (l)); } - Assert.AreEqual (registeredCount, JniRuntime.CurrentRuntime.ValueManager.GetSurfacedPeers ().Count); + Assert.AreEqual (registeredCount, JniRuntime.CurrentRuntime.ValueManager.GetSurfacedPeers ().Count, "registeredCount should match!"); Assert.IsNull (JniRuntime.CurrentRuntime.ValueManager.PeekValue (l)); JniObjectReference.Dispose (ref l); Assert.Throws (() => o.UnregisterFromRuntime ());