diff --git a/src/Microsoft.Android.Runtime.NativeAOT/Java.Interop/JreRuntime.cs b/src/Microsoft.Android.Runtime.NativeAOT/Java.Interop/JreRuntime.cs index 41c2370e1a4..5891149578f 100644 --- a/src/Microsoft.Android.Runtime.NativeAOT/Java.Interop/JreRuntime.cs +++ b/src/Microsoft.Android.Runtime.NativeAOT/Java.Interop/JreRuntime.cs @@ -61,7 +61,7 @@ static NativeAotRuntimeOptions CreateJreVM (NativeAotRuntimeOptions builder) builder.TypeManager ??= CreateDefaultTypeManager (); #endif // NET - builder.ValueManager ??= JavaMarshalValueManager.Instance; + builder.ValueManager ??= new JavaMarshalValueManager (); builder.ObjectReferenceManager ??= new Android.Runtime.AndroidObjectReferenceManager (); if (builder.InvocationPointer != IntPtr.Zero || builder.EnvironmentPointer != IntPtr.Zero) diff --git a/src/Mono.Android/Android.Runtime/JNIEnvInit.cs b/src/Mono.Android/Android.Runtime/JNIEnvInit.cs index 3d339d16eb7..c2e7ea913ca 100644 --- a/src/Mono.Android/Android.Runtime/JNIEnvInit.cs +++ b/src/Mono.Android/Android.Runtime/JNIEnvInit.cs @@ -187,7 +187,11 @@ internal static JniRuntime.JniValueManager CreateValueManager () return new AndroidValueManager (); } - if (RuntimeFeature.IsCoreClrRuntime || RuntimeFeature.IsNativeAotRuntime) { + if (RuntimeFeature.IsCoreClrRuntime) { + return new JavaMarshalValueManager (); + } + + if (RuntimeFeature.IsNativeAotRuntime) { return new JavaMarshalValueManager (); } diff --git a/src/Mono.Android/Android.Runtime/RuntimeNativeMethods.cs b/src/Mono.Android/Android.Runtime/RuntimeNativeMethods.cs index 551b76516fd..df7f992d442 100644 --- a/src/Mono.Android/Android.Runtime/RuntimeNativeMethods.cs +++ b/src/Mono.Android/Android.Runtime/RuntimeNativeMethods.cs @@ -125,8 +125,9 @@ internal unsafe static partial class RuntimeNativeMethods [LibraryImport (RuntimeConstants.InternalDllName)] [UnmanagedCallConv (CallConvs = new[] { typeof (CallConvCdecl) })] internal static partial delegate* unmanaged clr_initialize_gc_bridge ( + IntPtr java_marshal_value_manager_handle, delegate* unmanaged bridge_processing_started_callback, - delegate* unmanaged bridge_processing_finished_callback); + delegate* unmanaged bridge_processing_finished_callback); [MethodImplAttribute(MethodImplOptions.InternalCall)] internal static extern void monodroid_unhandled_exception (Exception javaException); diff --git a/src/Mono.Android/Microsoft.Android.Runtime/JavaMarshalValueManager.cs b/src/Mono.Android/Microsoft.Android.Runtime/JavaMarshalValueManager.cs index e07fda8e225..e80ac8b3512 100644 --- a/src/Mono.Android/Microsoft.Android.Runtime/JavaMarshalValueManager.cs +++ b/src/Mono.Android/Microsoft.Android.Runtime/JavaMarshalValueManager.cs @@ -27,18 +27,11 @@ class JavaMarshalValueManager : JniRuntime.JniValueManager bool disposed; - static JavaMarshalValueManager? s_instance; - - public static JavaMarshalValueManager Instance => - s_instance ?? throw new InvalidOperationException ("JavaMarshalValueManager has not been initialized. Call the constructor first."); - - unsafe internal JavaMarshalValueManager () + public unsafe JavaMarshalValueManager () { - var previous = Interlocked.CompareExchange (ref s_instance, this, null); - Debug.Assert (previous is null, "JavaMarshalValueManager must only be created once."); - - // There can only be one instance because JavaMarshal.Initialize can only be called once. - var mark_cross_references_ftn = RuntimeNativeMethods.clr_initialize_gc_bridge (&BridgeProcessingStarted, &BridgeProcessingFinished); + var javaMarshalValueManagerHandle = new GCHandle (this); + var mark_cross_references_ftn = RuntimeNativeMethods.clr_initialize_gc_bridge ( + GCHandle.ToIntPtr (javaMarshalValueManagerHandle), &BridgeProcessingStarted, &BridgeProcessingFinished); JavaMarshal.Initialize (mark_cross_references_ftn); } @@ -356,6 +349,12 @@ public static GCHandle GetAssociatedGCHandle (HandleContext* context) public static unsafe void EnsureAllContextsAreOurs (MarkCrossReferencesArgs* mcr) { +// This call site is reachable on all platforms. 'MarkCrossReferencesArgs.ComponentCount' is only supported on: 'android'. +// This call site is reachable on all platforms. 'MarkCrossReferencesArgs.Components' is only supported on: 'android'. +// This call site is reachable on all platforms. 'StronglyConnectedComponent.Count' is only supported on: 'android'. +// This call site is reachable on all platforms. 'StronglyConnectedComponent.Contexts' is only supported on: 'android'. +#pragma warning disable CA1416 + lock (referenceTrackingHandles) { for (nuint i = 0; i < mcr->ComponentCount; i++) { StronglyConnectedComponent component = mcr->Components [i]; @@ -375,7 +374,9 @@ static void EnsureContextIsOurs (IntPtr context) if (!referenceTrackingHandles.ContainsKey (context)) { throw new InvalidOperationException ("Unknown reference tracking handle."); } - } + } + +#pragma warning restore CA1416 } public static HandleContext* Alloc (IJavaPeerable peer) @@ -422,20 +423,32 @@ static unsafe void BridgeProcessingStarted (MarkCrossReferencesArgs* mcr) } [UnmanagedCallersOnly] - static unsafe void BridgeProcessingFinished (MarkCrossReferencesArgs* mcr) + static unsafe void BridgeProcessingFinished (IntPtr javaMarshalValueManagerHandle, MarkCrossReferencesArgs* mcr) { if (mcr == null) { throw new ArgumentNullException (nameof (mcr), "MarkCrossReferencesArgs should never be null."); } - ReadOnlySpan handlesToFree = ProcessCollectedContexts (mcr); + JavaMarshalValueManager instance = GCHandle.FromIntPtr (javaMarshalValueManagerHandle).Target; + + ReadOnlySpan handlesToFree = instance.ProcessCollectedContexts (mcr); + + +// This call site is reachable on all platforms. 'JavaMarshal.FinishCrossReferenceProcessing(MarkCrossReferencesArgs*, ReadOnlySpan)' is only supported on: 'android'. +#pragma warning disable CA1416 JavaMarshal.FinishCrossReferenceProcessing (mcr, handlesToFree); +#pragma warning restore CA1416 } - static unsafe ReadOnlySpan ProcessCollectedContexts (MarkCrossReferencesArgs* mcr) + unsafe ReadOnlySpan ProcessCollectedContexts (MarkCrossReferencesArgs* mcr) { List handlesToFree = []; - JavaMarshalValueManager instance = Instance; + +// This call site is reachable on all platforms. 'MarkCrossReferencesArgs.ComponentCount' is only supported on: 'android'. +// This call site is reachable on all platforms. 'MarkCrossReferencesArgs.Components' is only supported on: 'android'. +// This call site is reachable on all platforms. 'StronglyConnectedComponent.Count' is only supported on: 'android'. +// This call site is reachable on all platforms. 'StronglyConnectedComponent.Contexts' is only supported on: 'android'. +#pragma warning disable CA1416 for (int i = 0; (nuint)i < mcr->ComponentCount; i++) { StronglyConnectedComponent component = mcr->Components [i]; @@ -444,6 +457,8 @@ static unsafe ReadOnlySpan ProcessCollectedContexts (MarkCrossReferenc } } +#pragma warning restore CA1416 + void ProcessContext (HandleContext* context) { if (context == null) { @@ -460,7 +475,7 @@ void ProcessContext (HandleContext* context) // Note: modifying the RegisteredInstances dictionary while processing the collected contexts // is tricky and can lead to deadlocks, so we remember which contexts were collected and we will free // them later outside of the bridge processing loop. - instance.CollectedContexts.Enqueue ((IntPtr)context); + CollectedContexts.Enqueue ((IntPtr)context); // important: we must not free the handle before passing it to JavaMarshal.FinishCrossReferenceProcessing handlesToFree.Add (handle); diff --git a/src/native/clr/host/gc-bridge.cc b/src/native/clr/host/gc-bridge.cc index 6bf3a387e96..efd8f502ae4 100644 --- a/src/native/clr/host/gc-bridge.cc +++ b/src/native/clr/host/gc-bridge.cc @@ -74,7 +74,7 @@ void GCBridge::bridge_processing () noexcept BridgeProcessing bridge_processing {args}; bridge_processing.process (); - bridge_processing_finished_callback (args); + bridge_processing_finished_callback (java_marshal_value_manager_handle, args); } } diff --git a/src/native/clr/host/internal-pinvokes-shared.cc b/src/native/clr/host/internal-pinvokes-shared.cc index 813e6c64f90..b828fdcfe15 100644 --- a/src/native/clr/host/internal-pinvokes-shared.cc +++ b/src/native/clr/host/internal-pinvokes-shared.cc @@ -45,10 +45,11 @@ void _monodroid_weak_gref_delete (jobject handle, char type, const char *threadN } BridgeProcessingFtn clr_initialize_gc_bridge ( + intptr_t java_marshal_value_manager_handle, BridgeProcessingStartedFtn bridge_processing_started_callback, BridgeProcessingFinishedFtn bridge_processing_finished_callback) noexcept { - return GCBridge::initialize_callback (bridge_processing_started_callback, bridge_processing_finished_callback); + return GCBridge::initialize_callback (java_marshal_value_manager_handle, bridge_processing_started_callback, bridge_processing_finished_callback); } void monodroid_log (LogLevel level, LogCategories category, const char *message) noexcept diff --git a/src/native/clr/include/host/gc-bridge.hh b/src/native/clr/include/host/gc-bridge.hh index 80c0ac68132..6430e6f237a 100644 --- a/src/native/clr/include/host/gc-bridge.hh +++ b/src/native/clr/include/host/gc-bridge.hh @@ -50,7 +50,7 @@ struct MarkCrossReferencesArgs }; using BridgeProcessingStartedFtn = void (*)(MarkCrossReferencesArgs*); -using BridgeProcessingFinishedFtn = void (*)(MarkCrossReferencesArgs*); +using BridgeProcessingFinishedFtn = void (*)(intptr_t, MarkCrossReferencesArgs*); using BridgeProcessingFtn = void (*)(MarkCrossReferencesArgs*); namespace xamarin::android { @@ -61,6 +61,7 @@ namespace xamarin::android { static void initialize_on_runtime_init (JNIEnv *env, jclass runtimeClass) noexcept; static BridgeProcessingFtn initialize_callback ( + intptr_t java_marshal_value_manager, BridgeProcessingStartedFtn bridge_processing_started, BridgeProcessingFinishedFtn bridge_processing_finished) noexcept { @@ -69,6 +70,7 @@ namespace xamarin::android { abort_unless (GCBridge::bridge_processing_started_callback == nullptr, "GC bridge processing started callback is already set"); abort_unless (GCBridge::bridge_processing_finished_callback == nullptr, "GC bridge processing finished callback is already set"); + GCBridge::java_marshal_value_manager_handle = java_marshal_value_manager; GCBridge::bridge_processing_started_callback = bridge_processing_started; GCBridge::bridge_processing_finished_callback = bridge_processing_finished; @@ -89,6 +91,7 @@ namespace xamarin::android { static inline jobject Runtime_instance = nullptr; static inline jmethodID Runtime_gc = nullptr; + static inline intptr_t java_marshal_value_manager_handle; static inline BridgeProcessingStartedFtn bridge_processing_started_callback = nullptr; static inline BridgeProcessingFinishedFtn bridge_processing_finished_callback = nullptr; diff --git a/src/native/clr/include/runtime-base/internal-pinvokes.hh b/src/native/clr/include/runtime-base/internal-pinvokes.hh index 5f67a5397f9..5f0aa12baf8 100644 --- a/src/native/clr/include/runtime-base/internal-pinvokes.hh +++ b/src/native/clr/include/runtime-base/internal-pinvokes.hh @@ -18,6 +18,7 @@ extern "C" { const char* clr_typemap_managed_to_java (const char *typeName, const uint8_t *mvid) noexcept; bool clr_typemap_java_to_managed (const char *java_type_name, char const** assembly_name, uint32_t *managed_type_token_id) noexcept; BridgeProcessingFtn clr_initialize_gc_bridge ( + intptr_t java_marshal_value_manager_handle, BridgeProcessingStartedFtn bridge_processing_started_callback, BridgeProcessingFinishedFtn mark_cross_references_callback) noexcept; void monodroid_log (xamarin::android::LogLevel level, LogCategories category, const char *message) noexcept;