-
Notifications
You must be signed in to change notification settings - Fork 569
[Mono.Android] call new Java "GC Bridge" APIs #10125
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Closed
Closed
Changes from 3 commits
Commits
Show all changes
34 commits
Select commit
Hold shift + click to select a range
0ae638d
[Mono.Android] call new Java "GC Bridge" APIs
jonathanpeppers f4a2148
What if everything was managed-only?
jonathanpeppers aa0a4ae
Now uses: https://github.com/dotnet/java-interop/pull/1334
jonathanpeppers 0bc80b2
Revert "What if everything was managed-only?"
jonathanpeppers 8f9c0fc
Setup `g_bpFinishCallback`
jonathanpeppers fda9d43
Update src/native/clr/host/internal-pinvokes.cc
jonathanpeppers eba552f
Update src/native/clr/host/internal-pinvokes.cc
jonathanpeppers 4313f89
dotnet/runtime builds from BrzVlad:runtime:feature-clr-gcbridge
jonathanpeppers f4f8dcf
Add native portion of the GC interface
grendello 4d8bd88
Don't need this anymore
grendello fccda54
Enable git lfs on AzDO
jonathanpeppers 5b2e13e
Enable `$(system.debug)`
jonathanpeppers 461d127
Revert "Enable `$(system.debug)`"
jonathanpeppers 6cac797
Revert "Enable git lfs on AzDO"
jonathanpeppers 0654d76
Update azure-pipelines.yaml
jonathanpeppers a0f2561
Working on yaml/git lfs
jonathanpeppers 0221203
git lfs working directory
jonathanpeppers c990184
workingDirectory: ${{ parameters.xaSourcePath }}
jonathanpeppers f42f328
`git lfs install`
jonathanpeppers 0ebf234
[native\mono] use `JniObjectReferenceControlBlock` structure
jonathanpeppers ba60ff7
Update to APIs from BrzVlad's branch
jonathanpeppers 02c2562
Some updates to the recent native code changes
grendello 664be1a
android-x64 runtime pack
jonathanpeppers 96e1b9c
Remove comment that no longer applies
grendello 75cfd4d
git lfs for test lanes
jonathanpeppers d51469c
Copy `packages` folder
jonathanpeppers 1c25938
On-device tests use `custom-runtime.targets`
jonathanpeppers 5023378
Update pinvoke-tables.include
jonathanpeppers 1fba234
Let's see...
grendello b5b05f2
Bump to updated version of JI
grendello 7e56838
Don't use HEAD of the JI PR, it doesn't build
grendello afe529f
Merge branch 'main' into dev/peppers/gcbridge
jonathanpeppers b677236
Update pinvoke-tables.include
jonathanpeppers a7c26b5
Merge branch 'main' into dev/peppers/gcbridge
jonathanpeppers File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -35,3 +35,5 @@ Makefile eol=lf | |
| *.wixproj eol=crlf | ||
| *.wxs eol=crlf | ||
| *.rtf eol=crlf | ||
|
|
||
| packages/* filter=lfs diff=lfs merge=lfs -text | ||
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,21 @@ | ||
| <Project> | ||
| <!-- Use version in local packages folder --> | ||
| <Target Name="UpdateRuntimeVersion" BeforeTargets="ProcessFrameworkReferences"> | ||
| <PropertyGroup> | ||
| <_Version>10.0.0-dev</_Version> | ||
| </PropertyGroup> | ||
| <ItemGroup> | ||
| <KnownFrameworkReference Update="Microsoft.NETCore.App" | ||
| Condition=" '%(KnownFrameworkReference.TargetFramework)' == 'net10.0' " | ||
| DefaultRuntimeFrameworkVersion="$(_Version)" | ||
| LatestRuntimeFrameworkVersion="$(_Version)" | ||
| TargetingPackVersion="$(_Version)" | ||
| /> | ||
| <KnownRuntimePack | ||
| Update="Microsoft.NETCore.App" | ||
| Condition=" '%(KnownRuntimePack.TargetFramework)' == 'net10.0' " | ||
| LatestRuntimeFrameworkVersion="$(_Version)" | ||
| /> | ||
| </ItemGroup> | ||
| </Target> | ||
| </Project> |
Submodule Java.Interop
updated
7 files
Git LFS file not shown
3 changes: 3 additions & 0 deletions
3
packages/Microsoft.NETCore.App.Runtime.android-arm64.10.0.0-dev.nupkg
Git LFS file not shown
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -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,11 @@ class ManagedValueManager : JniRuntime.JniValueManager | |
| { | ||
| const DynamicallyAccessedMemberTypes Constructors = DynamicallyAccessedMemberTypes.PublicConstructors | DynamicallyAccessedMemberTypes.NonPublicConstructors; | ||
|
|
||
| Dictionary<int, List<IJavaPeerable>>? RegisteredInstances = new Dictionary<int, List<IJavaPeerable>>(); | ||
| Dictionary<int, List<GCHandle>>? RegisteredInstances = new (); | ||
|
|
||
| internal ManagedValueManager () | ||
| internal unsafe ManagedValueManager () | ||
| { | ||
| JavaMarshal.Initialize (&FinishBridgeProcessing); | ||
| } | ||
|
|
||
| public override void WaitForGCBridgeProcessing () | ||
|
|
@@ -35,7 +37,7 @@ public override void CollectPeers () | |
| if (RegisteredInstances == null) | ||
| throw new ObjectDisposedException (nameof (ManagedValueManager)); | ||
|
|
||
| var peers = new List<IJavaPeerable> (); | ||
| var peers = new List<GCHandle> (); | ||
|
|
||
| lock (RegisteredInstances) { | ||
| foreach (var ps in RegisteredInstances.Values) { | ||
|
|
@@ -48,7 +50,8 @@ public override void CollectPeers () | |
| List<Exception>? 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<Exception> (); | ||
|
|
@@ -74,33 +77,35 @@ public override void AddPeer (IJavaPeerable value) | |
| } | ||
| int key = value.JniIdentityHashCode; | ||
| lock (RegisteredInstances) { | ||
| List<IJavaPeerable>? peers; | ||
| List<GCHandle>? peers; | ||
| if (!RegisteredInstances.TryGetValue (key, out peers)) { | ||
| peers = new List<IJavaPeerable> () { | ||
| value, | ||
| peers = new List<GCHandle> () { | ||
| CreateReferenceTrackingHandle (value) | ||
| }; | ||
| RegisteredInstances.Add (key, peers); | ||
| return; | ||
| } | ||
|
|
||
| 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 +137,14 @@ void WarnNotReplacing (int key, IJavaPeerable ignoreValue, IJavaPeerable keepVal | |
| int key = GetJniIdentityHashCode (reference); | ||
|
|
||
| lock (RegisteredInstances) { | ||
| List<IJavaPeerable>? peers; | ||
| List<GCHandle>? 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 +162,15 @@ public override void RemovePeer (IJavaPeerable value) | |
|
|
||
| int key = value.JniIdentityHashCode; | ||
| lock (RegisteredInstances) { | ||
| List<IJavaPeerable>? peers; | ||
| List<GCHandle>? 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 +257,34 @@ public override List<JniSurfacedPeerInfo> GetSurfacedPeers () | |
| var peers = new List<JniSurfacedPeerInfo> (RegisteredInstances.Count); | ||
| foreach (var e in RegisteredInstances) { | ||
| foreach (var p in e.Value) { | ||
| peers.Add (new JniSurfacedPeerInfo (e.Key, new WeakReference<IJavaPeerable> (p))); | ||
| if (p.Target is not IJavaPeerable peer) | ||
| continue; | ||
| peers.Add (new JniSurfacedPeerInfo (e.Key, new WeakReference<IJavaPeerable> (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 FinishBridgeProcessing (nint sccsLen, StronglyConnectedComponent* sccs, nint ccrsLen, ComponentCrossReference* ccrs) | ||
| { | ||
| Java.Lang.JavaSystem.Gc (); | ||
|
Member
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This is the C# binding of |
||
|
|
||
| JavaMarshal.ReleaseMarkCrossReferenceResources ( | ||
| new Span<StronglyConnectedComponent> (sccs, (int) sccsLen), | ||
| new Span<ComponentCrossReference> (ccrs, (int) ccrsLen)); | ||
| } | ||
|
|
||
| const BindingFlags ActivationConstructorBindingFlags = BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance; | ||
|
|
||
| static readonly Type[] XAConstructorSignature = new Type [] { typeof (IntPtr), typeof (JniHandleOwnership) }; | ||
|
|
||
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@jonathanpeppers Note that the method that you register here will be called directly from the GC while the world is stopped. This means that you shouldn't register a managed method here. Instead there should be a native method that is not blocking (aka it dispatches the work of doing the java GC to a separate thread) and then returns so the C# GC can conclude. Once this separate thread finishes with the java gc, only then it can callback into managed so it does the
FinishCrossReferenceProcessing(according to the new version of the api)There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I will revert f4a2148, I was already kind of skeptical that everything could be done in managed code.
/cc @grendello I think we need to be able to run these lines in our CoreCLR native code:
android/src/native/mono/monodroid/osbridge.cc
Lines 983 to 989 in 1f89124
Then at the end, it would call the UCO
FinishBridgeProcessing()method.There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@BrzVlad how hard it is to register an
ecallwith CoreCLR? We can register a standard p/invoke method here, butecallwould be faster. I seem to recall it's not as straightforward in CoreCLR as it is in MonoVM, though?There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
By
ecalldo you mean mono'smono_add_internal_call? It is not clear to me how would you plan to use it in this context ?There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yep, equivalent to Mono's
icall. I assume there's a way to obtain its address from native code and use it to register in the above call.There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Maybe I'm missing some context, but
icallsare used for managed code to call into native runtime code, that is registered to the runtime. While inJavaMarshal.Initialize (..);you are registering a native callback to be called by the runtime during GC. This native code is called directly, there should be no overhead or transitions involved, so I don't understand how anicallwould be relevant here.There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
OK, so it's me who misunderstood - I thought the callback would be called by the managed code at some point, not the native runtime. If it's a direct call from native runtime then the point is moot. Thanks for the clarification :) (I'm only now getting acquainted with this new GC bridge)