diff --git a/.gitattributes b/.gitattributes index fa7720b693b..67b0c8d4a26 100644 --- a/.gitattributes +++ b/.gitattributes @@ -35,3 +35,5 @@ Makefile eol=lf *.wixproj eol=crlf *.wxs eol=crlf *.rtf eol=crlf + +packages/* filter=lfs diff=lfs merge=lfs -text diff --git a/Documentation/workflow/DevelopmentTips.md b/Documentation/workflow/DevelopmentTips.md index 768cc60a55f..ccb971d0f8b 100644 --- a/Documentation/workflow/DevelopmentTips.md +++ b/Documentation/workflow/DevelopmentTips.md @@ -450,13 +450,26 @@ A second (better) way is to add this MSBuild target to your Android `.csproj` file: ```xml - + + + + <_Version>10.0.0-dev + - + + + + ``` diff --git a/NuGet.config b/NuGet.config index 2343f543fbf..ea03f18b3e3 100644 --- a/NuGet.config +++ b/NuGet.config @@ -7,6 +7,7 @@ + diff --git a/build-tools/automation/yaml-templates/build-linux.yaml b/build-tools/automation/yaml-templates/build-linux.yaml index 08999e2f033..ce1dc15b94e 100644 --- a/build-tools/automation/yaml-templates/build-linux.yaml +++ b/build-tools/automation/yaml-templates/build-linux.yaml @@ -53,6 +53,12 @@ stages: path: ${{ parameters.checkoutPath }} persistCredentials: ${{ parameters.checkoutPersistCredentials }} + - script: | + git lfs install + git lfs pull + displayName: git lfs setup + workingDirectory: ${{ parameters.xaSourcePath }} + # Always checkout a second resource to ensure we are using multi-repo checkout behavior # https://learn.microsoft.com/en-us/azure/devops/pipelines/repos/multi-repo-checkout?view=azure-devops#checkout-path - checkout: maui diff --git a/build-tools/automation/yaml-templates/build-macos.yaml b/build-tools/automation/yaml-templates/build-macos.yaml index 58817ec76da..81ad7e53f25 100644 --- a/build-tools/automation/yaml-templates/build-macos.yaml +++ b/build-tools/automation/yaml-templates/build-macos.yaml @@ -75,6 +75,12 @@ stages: path: ${{ parameters.checkoutPath }} persistCredentials: ${{ parameters.checkoutPersistCredentials }} + - script: | + git lfs install + git lfs pull + displayName: git lfs setup + workingDirectory: ${{ parameters.xaSourcePath }} + - template: /build-tools/automation/yaml-templates/commercial-build.yaml parameters: xaSourcePath: ${{ parameters.xaSourcePath }} diff --git a/build-tools/automation/yaml-templates/build-windows.yaml b/build-tools/automation/yaml-templates/build-windows.yaml index 95db7e69485..a3dff0a04f0 100644 --- a/build-tools/automation/yaml-templates/build-windows.yaml +++ b/build-tools/automation/yaml-templates/build-windows.yaml @@ -35,6 +35,12 @@ stages: path: ${{ parameters.checkoutPath }} persistCredentials: ${{ parameters.checkoutPersistCredentials }} + - script: | + git lfs install + git lfs pull + displayName: git lfs setup + workingDirectory: ${{ parameters.xaSourcePath }} + - template: /build-tools/automation/yaml-templates/kill-processes.yaml - template: /build-tools/automation/yaml-templates/clean.yaml diff --git a/build-tools/automation/yaml-templates/setup-test-environment.yaml b/build-tools/automation/yaml-templates/setup-test-environment.yaml index 19a98a86fad..d54ba8005bb 100644 --- a/build-tools/automation/yaml-templates/setup-test-environment.yaml +++ b/build-tools/automation/yaml-templates/setup-test-environment.yaml @@ -22,6 +22,12 @@ steps: clean: true submodules: recursive +- script: | + git lfs install + git lfs pull + displayName: git lfs setup + workingDirectory: ${{ parameters.xaSourcePath }} + - template: /build-tools/automation/yaml-templates/setup-jdk-variables.yaml parameters: jdkMajorVersion: ${{ parameters.jdkMajorVersion }} diff --git a/build-tools/scripts/custom-runtime.targets b/build-tools/scripts/custom-runtime.targets new file mode 100644 index 00000000000..4e623f40f3c --- /dev/null +++ b/build-tools/scripts/custom-runtime.targets @@ -0,0 +1,22 @@ + + + + + <_Version>10.0.0-dev + + + + + + + diff --git a/external/Java.Interop b/external/Java.Interop index d07ed327dbd..7174669897e 160000 --- a/external/Java.Interop +++ b/external/Java.Interop @@ -1 +1 @@ -Subproject commit d07ed327dbd4477f0eab5d277b92fd4f57bfb893 +Subproject commit 7174669897efbf38fcf9a340d3127b65a4b8d352 diff --git a/packages/Microsoft.NETCore.App.Ref.10.0.0-dev.nupkg b/packages/Microsoft.NETCore.App.Ref.10.0.0-dev.nupkg new file mode 100644 index 00000000000..962bb77cfd4 --- /dev/null +++ b/packages/Microsoft.NETCore.App.Ref.10.0.0-dev.nupkg @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:5240fb5a42a71dc7b9e01f8d75c2251cf81ee6f4ffc571ce50ed0d2d9cb23d1f +size 5313568 diff --git a/packages/Microsoft.NETCore.App.Runtime.android-arm64.10.0.0-dev.nupkg b/packages/Microsoft.NETCore.App.Runtime.android-arm64.10.0.0-dev.nupkg new file mode 100644 index 00000000000..32d7885b4e4 --- /dev/null +++ b/packages/Microsoft.NETCore.App.Runtime.android-arm64.10.0.0-dev.nupkg @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:8eb905f6f152401b6375d2e3c51ca753b2545717fd4cb89674f7ff188c3e86c7 +size 215752961 diff --git a/packages/Microsoft.NETCore.App.Runtime.android-x64.10.0.0-dev.nupkg b/packages/Microsoft.NETCore.App.Runtime.android-x64.10.0.0-dev.nupkg new file mode 100644 index 00000000000..3a361daf778 --- /dev/null +++ b/packages/Microsoft.NETCore.App.Runtime.android-x64.10.0.0-dev.nupkg @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:78a57312c28961bb0e36aea2ae337dc57141652b58723f0229014e13cb3826b0 +size 217468673 diff --git a/src/Mono.Android.Runtime/Mono.Android.Runtime.csproj b/src/Mono.Android.Runtime/Mono.Android.Runtime.csproj index aac8b3bfe4c..c416d7f8830 100644 --- a/src/Mono.Android.Runtime/Mono.Android.Runtime.csproj +++ b/src/Mono.Android.Runtime/Mono.Android.Runtime.csproj @@ -64,5 +64,6 @@ + diff --git a/src/Mono.Android/Android.Runtime/RuntimeNativeMethods.cs b/src/Mono.Android/Android.Runtime/RuntimeNativeMethods.cs index e740dfdb4c9..f8cb8e95f17 100644 --- a/src/Mono.Android/Android.Runtime/RuntimeNativeMethods.cs +++ b/src/Mono.Android/Android.Runtime/RuntimeNativeMethods.cs @@ -2,6 +2,7 @@ using System; using System.Runtime.CompilerServices; using System.Runtime.InteropServices; +using System.Runtime.InteropServices.Java; using System.Text; namespace Android.Runtime @@ -18,7 +19,7 @@ enum TraceKind : uint All = Java | Managed | Native | Signals, } - internal static class RuntimeNativeMethods + internal unsafe static class RuntimeNativeMethods { [DllImport (RuntimeConstants.InternalDllName, CallingConvention = CallingConvention.Cdecl)] internal extern static void monodroid_log (LogLevel level, LogCategories category, string message); @@ -92,6 +93,17 @@ internal static class RuntimeNativeMethods [DllImport (RuntimeConstants.InternalDllName, CallingConvention = CallingConvention.Cdecl)] internal static extern bool clr_typemap_java_to_managed (string java_type_name, out IntPtr managed_assembly_name, out uint managed_type_token_id); + /// + /// TODO: implement this in the native side. + /// Initializes the "GC Bridge" implementation for the CoreCLR runtime. + /// + /// A function pointer to a C# callback that will be invoked when bridge processing has completed. + /// A function pointer that should be passed to JavaMarshal.Initialize() on startup. + [DllImport (RuntimeConstants.InternalDllName, CallingConvention = CallingConvention.Cdecl)] + internal static extern delegate* unmanaged clr_initialize_gc_bridge ( + 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/ManagedValueManager.cs b/src/Mono.Android/Microsoft.Android.Runtime/ManagedValueManager.cs index c58cfbab431..2b4d5f5ffff 100644 --- a/src/Mono.Android/Microsoft.Android.Runtime/ManagedValueManager.cs +++ b/src/Mono.Android/Microsoft.Android.Runtime/ManagedValueManager.cs @@ -10,6 +10,7 @@ using System.Reflection; using System.Runtime.CompilerServices; using System.Runtime.InteropServices; +using System.Runtime.InteropServices.Java; using System.Threading; using Android.Runtime; using Java.Interop; @@ -20,10 +21,12 @@ class ManagedValueManager : JniRuntime.JniValueManager { const DynamicallyAccessedMemberTypes Constructors = DynamicallyAccessedMemberTypes.PublicConstructors | DynamicallyAccessedMemberTypes.NonPublicConstructors; - Dictionary>? RegisteredInstances = new Dictionary>(); + Dictionary>? RegisteredInstances = new (); - internal ManagedValueManager () + internal unsafe ManagedValueManager () { + var mark_cross_references_ftn = RuntimeNativeMethods.clr_initialize_gc_bridge (&BridgeProcessingFinished); + JavaMarshal.Initialize (mark_cross_references_ftn); } public override void WaitForGCBridgeProcessing () @@ -35,7 +38,7 @@ public override void CollectPeers () if (RegisteredInstances == null) throw new ObjectDisposedException (nameof (ManagedValueManager)); - var peers = new List (); + var peers = new List (); lock (RegisteredInstances) { foreach (var ps in RegisteredInstances.Values) { @@ -48,7 +51,8 @@ public override void CollectPeers () List? exceptions = null; foreach (var peer in peers) { try { - peer.Dispose (); + if (peer.Target is IDisposable disposable) + disposable.Dispose (); } catch (Exception e) { exceptions = exceptions ?? new List (); @@ -74,10 +78,10 @@ public override void AddPeer (IJavaPeerable value) } int key = value.JniIdentityHashCode; lock (RegisteredInstances) { - List? peers; + List? peers; if (!RegisteredInstances.TryGetValue (key, out peers)) { - peers = new List () { - value, + peers = new List () { + CreateReferenceTrackingHandle (value) }; RegisteredInstances.Add (key, peers); return; @@ -85,22 +89,24 @@ public override void AddPeer (IJavaPeerable value) for (int i = peers.Count - 1; i >= 0; i--) { var p = peers [i]; - if (!JniEnvironment.Types.IsSameObject (p.PeerReference, value.PeerReference)) + if (p.Target is not IJavaPeerable peer) + continue; + if (!JniEnvironment.Types.IsSameObject (peer.PeerReference, value.PeerReference)) continue; if (Replaceable (p)) { - peers [i] = value; + peers [i] = CreateReferenceTrackingHandle (value); } else { - WarnNotReplacing (key, value, p); + WarnNotReplacing (key, value, peer); } return; } - peers.Add (value); + peers.Add (CreateReferenceTrackingHandle (value)); } } - static bool Replaceable (IJavaPeerable peer) + static bool Replaceable (GCHandle handle) { - if (peer == null) + if (handle.Target is not IJavaPeerable peer) return true; return peer.JniManagedPeerState.HasFlag (JniManagedPeerStates.Replaceable); } @@ -132,14 +138,14 @@ void WarnNotReplacing (int key, IJavaPeerable ignoreValue, IJavaPeerable keepVal int key = GetJniIdentityHashCode (reference); lock (RegisteredInstances) { - List? peers; + List? peers; if (!RegisteredInstances.TryGetValue (key, out peers)) return null; for (int i = peers.Count - 1; i >= 0; i--) { var p = peers [i]; - if (JniEnvironment.Types.IsSameObject (reference, p.PeerReference)) - return p; + if (p.Target is IJavaPeerable peer && JniEnvironment.Types.IsSameObject (reference, peer.PeerReference)) + return peer; } if (peers.Count == 0) RegisteredInstances.Remove (key); @@ -157,14 +163,15 @@ public override void RemovePeer (IJavaPeerable value) int key = value.JniIdentityHashCode; lock (RegisteredInstances) { - List? peers; + List? peers; if (!RegisteredInstances.TryGetValue (key, out peers)) return; for (int i = peers.Count - 1; i >= 0; i--) { var p = peers [i]; - if (object.ReferenceEquals (value, p)) { + if (object.ReferenceEquals (value, p.Target)) { peers.RemoveAt (i); + FreeHandle (p); } } if (peers.Count == 0) @@ -251,13 +258,41 @@ public override List GetSurfacedPeers () var peers = new List (RegisteredInstances.Count); foreach (var e in RegisteredInstances) { foreach (var p in e.Value) { - peers.Add (new JniSurfacedPeerInfo (e.Key, new WeakReference (p))); + if (p.Target is not IJavaPeerable peer) + continue; + peers.Add (new JniSurfacedPeerInfo (e.Key, new WeakReference (peer))); } } return peers; } } + static GCHandle CreateReferenceTrackingHandle (IJavaPeerable value) => + JavaMarshal.CreateReferenceTrackingHandle (value, value.JniObjectReferenceControlBlock); + + static unsafe void FreeHandle (GCHandle handle) + { + IntPtr context = JavaMarshal.GetContext (handle); + NativeMemory.Free ((void*) context); + } + + [UnmanagedCallersOnly] + internal static unsafe void BridgeProcessingFinished (MarkCrossReferences* mcr) + { + List handlesToFree = []; + for (int i = 0; i < mcr->ComponentsLen; i++) + { + for (int j = 0; j < mcr->Components [i].Count; j++) + { + IntPtr *pContext = (IntPtr*) mcr->Components [i].Context [j]; + handlesToFree.Add (GCHandle.FromIntPtr (*pContext)); + NativeMemory.Free (pContext); + } + } + + JavaMarshal.FinishCrossReferenceProcessing (mcr, CollectionsMarshal.AsSpan (handlesToFree)); + } + const BindingFlags ActivationConstructorBindingFlags = BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance; static readonly Type[] XAConstructorSignature = new Type [] { typeof (IntPtr), typeof (JniHandleOwnership) }; diff --git a/src/Mono.Android/Mono.Android.csproj b/src/Mono.Android/Mono.Android.csproj index ec9eff97c93..f15a5b077d2 100644 --- a/src/Mono.Android/Mono.Android.csproj +++ b/src/Mono.Android/Mono.Android.csproj @@ -113,6 +113,7 @@ + diff --git a/src/Xamarin.Android.Build.Tasks/Tests/Xamarin.ProjectTools/Common/XamarinProject.cs b/src/Xamarin.Android.Build.Tasks/Tests/Xamarin.ProjectTools/Common/XamarinProject.cs index 601406c0748..9ec46e97ca9 100644 --- a/src/Xamarin.Android.Build.Tasks/Tests/Xamarin.ProjectTools/Common/XamarinProject.cs +++ b/src/Xamarin.Android.Build.Tasks/Tests/Xamarin.ProjectTools/Common/XamarinProject.cs @@ -391,6 +391,17 @@ public void CopyNuGetConfig (string relativeDirectory) doc.Save (projNugetConfig); } } + + // Copy packages folder + var repoPackages = Path.Combine (XABuildPaths.TopDirectory, "packages"); + var projPackages = Path.Combine (Root, relativeDirectory, "packages"); + if (Directory.Exists (repoPackages)) { + Directory.CreateDirectory (projPackages); + foreach (var package in Directory.GetFiles (repoPackages, "*.nupkg")) { + var destination = Path.Combine (projPackages, Path.GetFileName (package)); + File.Copy (package, destination, overwrite: true); + } + } } /// diff --git a/src/native/clr/host/CMakeLists.txt b/src/native/clr/host/CMakeLists.txt index d53734eaf8c..44f3edab57d 100644 --- a/src/native/clr/host/CMakeLists.txt +++ b/src/native/clr/host/CMakeLists.txt @@ -29,6 +29,7 @@ set(XAMARIN_NET_ANDROID_STATIC_LIB "${XAMARIN_NET_ANDROID_LIB}-static") set(XAMARIN_MONODROID_SOURCES assembly-store.cc + gc-bridge.cc host.cc host-jni.cc host-util.cc diff --git a/src/native/clr/host/gc-bridge.cc b/src/native/clr/host/gc-bridge.cc new file mode 100644 index 00000000000..c03da967d99 --- /dev/null +++ b/src/native/clr/host/gc-bridge.cc @@ -0,0 +1,44 @@ +#include +#include +#include + +using namespace xamarin::android; + +void GCBridge::initialize_on_load (JNIEnv *env) noexcept +{ + abort_if_invalid_pointer_argument (env, "env"); + + jclass lref = env->FindClass ("java/lang/Runtime"); + jmethodID Runtime_getRuntime = env->GetStaticMethodID (lref, "getRuntime", "()Ljava/lang/Runtime;"); + Runtime_gc = env->GetMethodID (lref, "gc", "()V"); + Runtime_instance = OSBridge::lref_to_gref (env, env->CallStaticObjectMethod (lref, Runtime_getRuntime)); + env->DeleteLocalRef (lref); + + abort_unless ( + Runtime_gc != nullptr && Runtime_instance != nullptr, + "Failed to look up Java GC runtime API." + ); +} + +[[gnu::always_inline]] +void GCBridge::trigger_java_gc () noexcept +{ + JNIEnv *env = OSBridge::ensure_jnienv (); + + // NOTE: Mono has a number of pre- and post- calls before invoking the Java GC. At this point + // it is unknown whether the CoreCLR GC bridge will need anything of that sort, so we just trigger + // the Java GC here. + env->CallVoidMethod (Runtime_instance, Runtime_gc); +} + +void GCBridge::mark_cross_references (MarkCrossReferences* crossRefs) noexcept +{ + if (bridge_processing_finish_callback == nullptr) [[unlikely]] { + return; + } + + trigger_java_gc (); + + // Call back into managed code + bridge_processing_finish_callback (crossRefs); +} diff --git a/src/native/clr/host/generate-pinvoke-tables.cc b/src/native/clr/host/generate-pinvoke-tables.cc index 1619a09b32a..cbf8bb01d19 100644 --- a/src/native/clr/host/generate-pinvoke-tables.cc +++ b/src/native/clr/host/generate-pinvoke-tables.cc @@ -72,6 +72,7 @@ const std::vector internal_pinvoke_names = { "monodroid_TypeManager_get_java_class_name", "clr_typemap_managed_to_java", "clr_typemap_java_to_managed", + "clr_initialize_gc_bridge", "_monodroid_weak_gref_delete", "_monodroid_weak_gref_get", "_monodroid_weak_gref_new", diff --git a/src/native/clr/host/host.cc b/src/native/clr/host/host.cc index 5ca6b4f7877..595e8a1da57 100644 --- a/src/native/clr/host/host.cc +++ b/src/native/clr/host/host.cc @@ -9,6 +9,7 @@ #include #include +#include #include #include #include @@ -492,6 +493,7 @@ auto Host::Java_JNI_OnLoad (JavaVM *vm, [[maybe_unused]] void *reserved) noexcep JNIEnv *env = nullptr; vm->GetEnv ((void**)&env, JNI_VERSION_1_6); OSBridge::initialize_on_onload (vm, env); + GCBridge::initialize_on_load (env); AndroidSystem::init_max_gref_count (); return JNI_VERSION_1_6; diff --git a/src/native/clr/host/internal-pinvokes.cc b/src/native/clr/host/internal-pinvokes.cc index 4d801afe032..bf5a0ba4918 100644 --- a/src/native/clr/host/internal-pinvokes.cc +++ b/src/native/clr/host/internal-pinvokes.cc @@ -1,3 +1,4 @@ +#include #include #include #include @@ -36,6 +37,12 @@ bool clr_typemap_java_to_managed (const char *java_type_name, char const** assem return TypeMapper::typemap_java_to_managed (java_type_name, assembly_name, managed_type_token_id); } +MarkCrossReferencesFtn clr_initialize_gc_bridge (MarkCrossReferencesFtn callback) noexcept +{ + GCBridge::set_finish_callback (callback); + return GCBridge::mark_cross_references; +} + void monodroid_log (LogLevel level, LogCategories category, const char *message) noexcept { switch (level) { diff --git a/src/native/clr/host/pinvoke-tables.include b/src/native/clr/host/pinvoke-tables.include index d02e8bcf825..4872eb0d97e 100644 --- a/src/native/clr/host/pinvoke-tables.include +++ b/src/native/clr/host/pinvoke-tables.include @@ -11,7 +11,7 @@ namespace { #if INTPTR_MAX == INT64_MAX //64-bit internal p/invoke table - std::array internal_pinvokes {{ + std::array internal_pinvokes {{ {0xa50ce5de13bf8b5, "_monodroid_timezone_get_default_id", reinterpret_cast(&_monodroid_timezone_get_default_id)}, {0x3ade4348ac8ce0fa, "_monodroid_freeifaddrs", reinterpret_cast(&_monodroid_freeifaddrs)}, {0x3b2467e7eadd4a6a, "_monodroid_lref_log_new", reinterpret_cast(&_monodroid_lref_log_new)}, @@ -21,6 +21,7 @@ namespace { {0x5f0b4e426eff086b, "_monodroid_detect_cpu_and_architecture", reinterpret_cast(&_monodroid_detect_cpu_and_architecture)}, {0x9099a4b95e3c3a89, "_monodroid_lref_log_delete", reinterpret_cast(&_monodroid_lref_log_delete)}, {0x9187e6bc6294cacf, "clr_typemap_managed_to_java", reinterpret_cast(&clr_typemap_managed_to_java)}, + {0x920bf58357fb56f3, "clr_initialize_gc_bridge", reinterpret_cast(&clr_initialize_gc_bridge)}, {0x9a946dfe9916a942, "clr_typemap_java_to_managed", reinterpret_cast(&clr_typemap_java_to_managed)}, {0xa6ec846592d99536, "_monodroid_weak_gref_delete", reinterpret_cast(&_monodroid_weak_gref_delete)}, {0xa7f58f3ee428cc6b, "_monodroid_gref_log_delete", reinterpret_cast(&_monodroid_gref_log_delete)}, @@ -531,7 +532,7 @@ constexpr hash_t system_security_cryptography_native_android_library_hash = 0x18 constexpr hash_t system_globalization_native_library_hash = 0x28b5c8fca080abd5; #else //32-bit internal p/invoke table - std::array internal_pinvokes {{ + std::array internal_pinvokes {{ {0xb7a486a, "monodroid_TypeManager_get_java_class_name", reinterpret_cast(&monodroid_TypeManager_get_java_class_name)}, {0x2aea7c33, "_monodroid_max_gref_get", reinterpret_cast(&_monodroid_max_gref_get)}, {0x3227d81a, "monodroid_timing_start", reinterpret_cast(&monodroid_timing_start)}, @@ -544,6 +545,7 @@ constexpr hash_t system_globalization_native_library_hash = 0x28b5c8fca080abd5; {0x9a734f16, "_monodroid_weak_gref_get", reinterpret_cast(&_monodroid_weak_gref_get)}, {0x9c5b24a8, "_monodroid_weak_gref_new", reinterpret_cast(&_monodroid_weak_gref_new)}, {0xa04e5d1c, "monodroid_free", reinterpret_cast(&monodroid_free)}, + {0xa3c1e548, "clr_initialize_gc_bridge", reinterpret_cast(&clr_initialize_gc_bridge)}, {0xad511c82, "_monodroid_timezone_get_default_id", reinterpret_cast(&_monodroid_timezone_get_default_id)}, {0xb02468aa, "_monodroid_gref_get", reinterpret_cast(&_monodroid_gref_get)}, {0xb6431f9a, "clr_typemap_java_to_managed", reinterpret_cast(&clr_typemap_java_to_managed)}, @@ -1051,6 +1053,6 @@ constexpr hash_t system_security_cryptography_native_android_library_hash = 0x93 constexpr hash_t system_globalization_native_library_hash = 0xa66f1e5a; #endif -constexpr size_t internal_pinvokes_count = 26; +constexpr size_t internal_pinvokes_count = 27; constexpr size_t dotnet_pinvokes_count = 477; } // end of anonymous namespace diff --git a/src/native/clr/include/host/gc-bridge.hh b/src/native/clr/include/host/gc-bridge.hh new file mode 100644 index 00000000000..e472f733256 --- /dev/null +++ b/src/native/clr/include/host/gc-bridge.hh @@ -0,0 +1,48 @@ +#pragma once + +#include + +#include + +struct StronglyConnectedComponent +{ + size_t Count; + void** ContextMemory; +}; + +struct ComponentCrossReference +{ + size_t SourceGroupIndex; + size_t DestinationGroupIndex; +}; + +struct MarkCrossReferences +{ + size_t ComponentsLen; + StronglyConnectedComponent* Components; + size_t CrossReferencesLen; + ComponentCrossReference* CrossReferences; +}; + +using MarkCrossReferencesFtn = void (*)(MarkCrossReferences*); + +namespace xamarin::android { + class GCBridge + { + public: + static void initialize_on_load (JNIEnv *env) noexcept; + static void trigger_java_gc () noexcept; + static void mark_cross_references (MarkCrossReferences* crossRefs) noexcept; + + static void set_finish_callback (MarkCrossReferencesFtn callback) noexcept + { + abort_if_invalid_pointer_argument (callback, "callback"); + bridge_processing_finish_callback = callback; + } + + private: + static inline jobject Runtime_instance = nullptr; + static inline jmethodID Runtime_gc = nullptr; + static inline MarkCrossReferencesFtn bridge_processing_finish_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 a3c69c6c6e7..5a01d121633 100644 --- a/src/native/clr/include/runtime-base/internal-pinvokes.hh +++ b/src/native/clr/include/runtime-base/internal-pinvokes.hh @@ -3,6 +3,7 @@ #include #include +#include #include #include "logger.hh" #include @@ -14,6 +15,7 @@ extern "C" { void _monodroid_gref_log_delete (jobject handle, char type, const char *threadName, int threadId, const char *from, int from_writable) noexcept; 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; + MarkCrossReferencesFtn clr_initialize_gc_bridge (MarkCrossReferencesFtn callback) noexcept; void monodroid_log (xamarin::android::LogLevel level, LogCategories category, const char *message) noexcept; char* monodroid_TypeManager_get_java_class_name (jclass klass) noexcept; void monodroid_free (void *ptr) noexcept; diff --git a/src/native/mono/monodroid/monodroid-glue.cc b/src/native/mono/monodroid/monodroid-glue.cc index d8c722f48f2..9cffb809d0b 100644 --- a/src/native/mono/monodroid/monodroid-glue.cc +++ b/src/native/mono/monodroid/monodroid-glue.cc @@ -793,22 +793,15 @@ MonodroidRuntime::create_domain (JNIEnv *env, jstring_array_wrapper &runtimeApks MonodroidRuntime::lookup_bridge_info (MonoClass *klass, const OSBridge::MonoJavaGCBridgeType *type, OSBridge::MonoJavaGCBridgeInfo *info) noexcept { info->klass = klass; - 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->key_handle = mono_class_get_field_from_name (info->klass, const_cast ("key_handle")); + info->jniObjectReferenceControlBlock = mono_class_get_field_from_name (info->klass, const_cast("jniObjectReferenceControlBlock")); - // key_handle is optional, as Java.Interop.JavaObject doesn't currently have it - if (info->klass == nullptr || info->handle == nullptr || info->handle_type == nullptr || info->refs_added == nullptr) { + if (info->klass == nullptr || info->jniObjectReferenceControlBlock == nullptr) { Helpers::abort_application ( - Util::monodroid_strdup_printf ( - "The type `%s.%s` is missing required instance fields! handle=%p handle_type=%p refs_added=%p key_handle=%p", + std::format ( + "The type `{}.{} is missing required instance fields! jniObjectReferenceControlBlock={:p}", type->_namespace, type->_typename, - info->handle, - info->handle_type, - info->refs_added, - info->key_handle + static_cast(info->jniObjectReferenceControlBlock) ) ); } diff --git a/src/native/mono/monodroid/osbridge.cc b/src/native/mono/monodroid/osbridge.cc index 919506d3fda..4c8cd6a77a8 100644 --- a/src/native/mono/monodroid/osbridge.cc +++ b/src/native/mono/monodroid/osbridge.cc @@ -42,9 +42,6 @@ const uint32_t OSBridge::NUM_GC_BRIDGE_TYPES = NUM_XA_GC_BRIDGE_TYPES + NUM_J OSBridge::MonoJavaGCBridgeInfo OSBridge::mono_java_gc_bridge_info [NUM_GC_BRIDGE_TYPES]; OSBridge::MonoJavaGCBridgeInfo OSBridge::empty_bridge_info = { - nullptr, - nullptr, - nullptr, nullptr, nullptr }; @@ -76,9 +73,13 @@ OSBridge::clear_mono_java_gc_bridge_info () for (uint32_t c = 0; c < NUM_GC_BRIDGE_TYPES; c++) { MonoJavaGCBridgeInfo *info = &mono_java_gc_bridge_info [c]; info->klass = nullptr; - info->handle = nullptr; - info->handle_type = nullptr; - info->refs_added = nullptr; + auto control_block = reinterpret_cast(info->jniObjectReferenceControlBlock); + if (control_block == nullptr) [[unlikely]] { + continue; + } + control_block->handle = nullptr; + control_block->handle_type = 0; + control_block->refs_added = 0; } } @@ -102,6 +103,19 @@ OSBridge::get_gc_bridge_index (MonoClass *klass) : -1; } +OSBridge::JniObjectReferenceControlBlock* +OSBridge::get_gc_control_block_for_object (MonoObject *obj) +{ + MonoJavaGCBridgeInfo *bridge_info = get_gc_bridge_info_for_object (obj); + if (bridge_info == nullptr) { + return nullptr; + } + + JniObjectReferenceControlBlock *control_block = nullptr; + mono_field_get_value (obj, bridge_info->jniObjectReferenceControlBlock, &control_block); + return control_block; +} + OSBridge::MonoJavaGCBridgeInfo * OSBridge::get_gc_bridge_info_for_class (MonoClass *klass) { @@ -459,11 +473,12 @@ OSBridge::take_global_ref_jni (JNIEnv *env, MonoObject *obj) jobject handle, weak; int type = JNIGlobalRefType; - MonoJavaGCBridgeInfo *bridge_info = get_gc_bridge_info_for_object (obj); - if (bridge_info == nullptr) + JniObjectReferenceControlBlock *control_block = get_gc_control_block_for_object (obj); + if (control_block == nullptr) { return 0; + } - mono_field_get_value (obj, bridge_info->handle, &weak); + mono_field_get_value (obj, reinterpret_cast(control_block->handle), &weak); handle = env->NewGlobalRef (weak); if (gref_log) { 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) " at [[gc:take_global_ref_jni]]", 0); } else if (Logger::gc_spew_enabled ()) [[unlikely]] { void *key_handle = nullptr; - if (bridge_info->key_handle) { - mono_field_get_value (obj, bridge_info->key_handle, &key_handle); + if (control_block->weak_handle) { + mono_field_get_value (obj, reinterpret_cast(control_block->weak_handle), &key_handle); } MonoClass *klass = mono_object_get_class (obj); @@ -491,8 +506,8 @@ OSBridge::take_global_ref_jni (JNIEnv *env, MonoObject *obj) free (message); } - mono_field_set_value (obj, bridge_info->handle, &handle); - mono_field_set_value (obj, bridge_info->handle_type, &type); + mono_field_set_value (obj, reinterpret_cast(control_block->handle), &handle); + mono_field_set_value (obj, reinterpret_cast(control_block->handle_type), &type); _monodroid_weak_gref_delete (weak, get_object_ref_type (env, weak), "finalizer", gettid (), " at [[gc:take_global_ref_jni]]", 0); @@ -507,11 +522,12 @@ OSBridge::take_weak_global_ref_jni (JNIEnv *env, MonoObject *obj) jobject handle, weak; int type = JNIWeakGlobalRefType; - MonoJavaGCBridgeInfo *bridge_info = get_gc_bridge_info_for_object (obj); - if (bridge_info == nullptr) + JniObjectReferenceControlBlock *control_block = get_gc_control_block_for_object (obj); + if (control_block == nullptr) { return 0; + } - mono_field_get_value (obj, bridge_info->handle, &handle); + mono_field_get_value (obj, reinterpret_cast(control_block->handle), &handle); if (gref_log) { fprintf (gref_log, "*take_weak obj=%p; handle=%p\n", obj, handle); fflush (gref_log); @@ -522,8 +538,8 @@ OSBridge::take_weak_global_ref_jni (JNIEnv *env, MonoObject *obj) weak, get_object_ref_type (env, weak), "finalizer", gettid (), " at [[gc:take_weak_global_ref_jni]]", 0); - mono_field_set_value (obj, bridge_info->handle, &weak); - mono_field_set_value (obj, bridge_info->handle_type, &type); + mono_field_set_value (obj, reinterpret_cast(control_block->handle), &weak); + mono_field_set_value (obj, reinterpret_cast(control_block->handle_type), &type); _monodroid_gref_log_delete (handle, get_object_ref_type (env, handle), "finalizer", gettid (), " at [[gc:take_weak_global_ref_jni]]", 0); @@ -558,13 +574,23 @@ OSBridge::gc_bridge_class_kind (MonoClass *klass) mono_bool OSBridge::gc_is_bridge_object (MonoObject *object) { - void *handle; + if (object == nullptr) [[unlikely]] { + log_debug (LOG_GC, "gc_is_bridge_object was passed a NULL object pointer"); + return FALSE; + } - MonoJavaGCBridgeInfo *bridge_info = get_gc_bridge_info_for_object (object); - if (bridge_info == nullptr) - return 0; + JniObjectReferenceControlBlock *control_block = get_gc_control_block_for_object (object); + if (control_block == nullptr) { + return FALSE; + } + + if (control_block->handle == nullptr) { + log_warn (LOG_GC, "gc_is_bridge_object: control block's handle is NULL"); + return FALSE; + } - mono_field_get_value (object, bridge_info->handle, &handle); + void *handle; + mono_field_get_value (object, reinterpret_cast(control_block->handle), &handle); if (handle == nullptr) { #if DEBUG MonoClass *mclass = mono_object_get_class (object); @@ -574,10 +600,10 @@ OSBridge::gc_is_bridge_object (MonoObject *object) optional_string (mono_class_get_name (mclass)) ); #endif - return 0; + return FALSE; } - return 1; + return TRUE; } // Add a reference from an IGCUserPeer jobject to another jobject @@ -604,10 +630,11 @@ mono_bool OSBridge::load_reference_target (OSBridge::AddReferenceTarget target, OSBridge::MonoJavaGCBridgeInfo** bridge_info, jobject *handle) { if (target.is_mono_object) { - *bridge_info = get_gc_bridge_info_for_object (target.obj); - if (!*bridge_info) + JniObjectReferenceControlBlock *control_block = get_gc_control_block_for_object (target.obj); + if (control_block == nullptr) { return FALSE; - mono_field_get_value (target.obj, (*bridge_info)->handle, handle); + } + mono_field_get_value (target.obj, reinterpret_cast(control_block->handle), handle); } else { *handle = target.jobj; } @@ -649,7 +676,11 @@ OSBridge::add_reference (JNIEnv *env, OSBridge::AddReferenceTarget target, OSBri // Java temporaries do not need this because the entire GCUserPeer is discarded. if (success && target.is_mono_object) { int ref_val = 1; - mono_field_set_value (target.obj, bridge_info->refs_added, &ref_val); + JniObjectReferenceControlBlock *control_block = get_gc_control_block_for_object (target.obj); + if (control_block == nullptr) { + return FALSE; + } + mono_field_set_value (target.obj, reinterpret_cast(control_block->refs_added), &ref_val); } #if DEBUG @@ -867,14 +898,13 @@ OSBridge::gc_cleanup_after_java_collection (JNIEnv *env, int num_sccs, MonoGCBri sccs [i]->is_alive = 0; for (j = 0; j < sccs [i]->num_objs; j++) { - MonoJavaGCBridgeInfo *bridge_info; - obj = sccs [i]->objs [j]; - bridge_info = get_gc_bridge_info_for_object (obj); - if (bridge_info == nullptr) + JniObjectReferenceControlBlock *control_block = get_gc_control_block_for_object (obj); + if (control_block == nullptr) { continue; - mono_field_get_value (obj, bridge_info->handle, &jref); + } + mono_field_get_value (obj, reinterpret_cast(control_block->handle), &jref); if (jref) { alive++; if (j > 0) { @@ -884,7 +914,7 @@ OSBridge::gc_cleanup_after_java_collection (JNIEnv *env, int num_sccs, MonoGCBri ); } sccs [i]->is_alive = 1; - mono_field_get_value (obj, bridge_info->refs_added, &refs_added); + mono_field_get_value (obj, (MonoClassField*) control_block->refs_added, &refs_added); if (refs_added) { jclass java_class = env->GetObjectClass (jref); 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 for (j = 0; j < sccs [i]->num_objs; ++j) { MonoObject *obj = sccs [i]->objs [j]; - MonoJavaGCBridgeInfo *bridge_info = get_gc_bridge_info_for_object (obj); + JniObjectReferenceControlBlock *control_block = get_gc_control_block_for_object (obj); jobject handle = 0; void *key_handle = nullptr; - if (bridge_info != nullptr) { - mono_field_get_value (obj, bridge_info->handle, &handle); - if (bridge_info->key_handle != nullptr) { - mono_field_get_value (obj, bridge_info->key_handle, &key_handle); + if (control_block != nullptr) { + mono_field_get_value (obj, reinterpret_cast(control_block->handle), &handle); + if (control_block->weak_handle != nullptr) { + mono_field_get_value (obj, reinterpret_cast(control_block->weak_handle), &key_handle); } } MonoClass *klass = mono_object_get_class (obj); diff --git a/src/native/mono/monodroid/osbridge.hh b/src/native/mono/monodroid/osbridge.hh index 46f4af69ef8..f439a57aedd 100644 --- a/src/native/mono/monodroid/osbridge.hh +++ b/src/native/mono/monodroid/osbridge.hh @@ -37,10 +37,15 @@ namespace xamarin::android::internal struct MonoJavaGCBridgeInfo { MonoClass *klass; - MonoClassField *handle; - MonoClassField *handle_type; - MonoClassField *refs_added; - MonoClassField *key_handle; + MonoClassField *jniObjectReferenceControlBlock; + }; + + struct JniObjectReferenceControlBlock + { + jobject handle; + int handle_type; + jobject weak_handle; + int refs_added; }; // add_reference can work with objects which are either MonoObjects with java peers, or raw jobjects @@ -127,6 +132,7 @@ namespace xamarin::android::internal private: int get_gc_bridge_index (MonoClass *klass); + JniObjectReferenceControlBlock* get_gc_control_block_for_object (MonoObject *obj); MonoJavaGCBridgeInfo* get_gc_bridge_info_for_class (MonoClass *klass); MonoJavaGCBridgeInfo* get_gc_bridge_info_for_object (MonoObject *object); char get_object_ref_type (JNIEnv *env, void *handle); diff --git a/tests/Mono.Android-Tests/Mono.Android-Tests/Mono.Android.NET-Tests.csproj b/tests/Mono.Android-Tests/Mono.Android-Tests/Mono.Android.NET-Tests.csproj index b762c8be98e..b1ca5288f3d 100644 --- a/tests/Mono.Android-Tests/Mono.Android-Tests/Mono.Android.NET-Tests.csproj +++ b/tests/Mono.Android-Tests/Mono.Android-Tests/Mono.Android.NET-Tests.csproj @@ -252,6 +252,7 @@ +