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..53e74606f1b 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/scripts/custom-runtime.targets b/build-tools/scripts/custom-runtime.targets new file mode 100644 index 00000000000..c25059b3315 --- /dev/null +++ b/build-tools/scripts/custom-runtime.targets @@ -0,0 +1,21 @@ + + + + + <_Version>10.0.0-dev + + + + + + + diff --git a/external/Java.Interop b/external/Java.Interop index d07ed327dbd..174fd85c7fd 160000 --- a/external/Java.Interop +++ b/external/Java.Interop @@ -1 +1 @@ -Subproject commit d07ed327dbd4477f0eab5d277b92fd4f57bfb893 +Subproject commit 174fd85c7fd5c81d48554c89afa7e5b618348160 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/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..13a55b90319 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..265217765d8 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,32 @@ 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 (nint sccsLen, StronglyConnectedComponent* sccs, nint ccrsLen, ComponentCrossReference* ccrs) + { + JavaMarshal.ReleaseMarkCrossReferenceResources ( + new Span (sccs, (int) sccsLen), + new Span (ccrs, (int) ccrsLen)); + } + 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/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/internal-pinvokes.cc b/src/native/clr/host/internal-pinvokes.cc index 4d801afe032..f5c79eacfee 100644 --- a/src/native/clr/host/internal-pinvokes.cc +++ b/src/native/clr/host/internal-pinvokes.cc @@ -36,6 +36,27 @@ 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); } +namespace { + MarkCrossReferencesFtn g_bpFinishCallback = nullptr; +} + +static void clr_mark_cross_references (size_t sccsLen, StronglyConnectedComponent* sccs, size_t ccrsLen, ComponentCrossReference* ccrs) +{ + if (g_bpFinishCallback == nullptr) [[unlikely]] { + return; + } + // TODO: implement this + + // Call back into managed code + g_bpFinishCallback (sccsLen, sccs, ccrsLen, ccrs); +} + +MarkCrossReferencesFtn clr_initialize_gc_bridge (MarkCrossReferencesFtn callback) noexcept +{ + g_bpFinishCallback = callback; + return clr_mark_cross_references; +} + void monodroid_log (LogLevel level, LogCategories category, const char *message) noexcept { switch (level) { diff --git a/src/native/clr/include/runtime-base/internal-pinvokes.hh b/src/native/clr/include/runtime-base/internal-pinvokes.hh index a3c69c6c6e7..0d2c80661ff 100644 --- a/src/native/clr/include/runtime-base/internal-pinvokes.hh +++ b/src/native/clr/include/runtime-base/internal-pinvokes.hh @@ -7,6 +7,20 @@ #include "logger.hh" #include +struct StronglyConnectedComponent +{ + size_t Count; + void** ContextMemory; +}; + +struct ComponentCrossReference +{ + size_t SourceGroupIndex; + size_t DestinationGroupIndex; +}; + +typedef void (*MarkCrossReferencesFtn)(size_t, StronglyConnectedComponent*, size_t, ComponentCrossReference*); + extern "C" { int _monodroid_gref_get () noexcept; void _monodroid_gref_log (const char *message) noexcept; @@ -14,6 +28,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;