-
Notifications
You must be signed in to change notification settings - Fork 5k
Add CoreCLR support for android GC bridge #116310
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
Open
BrzVlad
wants to merge
26
commits into
dotnet:main
Choose a base branch
from
BrzVlad:feature-clr-gcbridge
base: main
Could not load branches
Branch not found: {{ refName }}
Loading
Could not load tags
Nothing to show
Loading
Are you sure you want to change the base?
Some commits from the old base branch may be removed from the timeline,
and old review comments may become outdated.
Open
+2,671
−27
Conversation
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
src/coreclr/System.Private.CoreLib/src/System/Runtime/InteropServices/GCHandle.CoreCLR.cs
Outdated
Show resolved
Hide resolved
jkotas
reviewed
Jun 4, 2025
BrzVlad
commented
Jun 5, 2025
e4fade9
to
190552f
Compare
This was referenced Jun 5, 2025
40e5e75
to
ff4ea6c
Compare
jkotas
reviewed
Jun 8, 2025
ff4ea6c
to
f3e49ae
Compare
From Aaron's implementation.
From Aaron's implementation
Checking if the object is promoted was validating the next object header in debug builds. During bridge tarjan computation, we patch the object header for some objects in order to store data used by the bridge algorithm, so we need to disable this validation.
HANDLE_MAX_INTERNAL_TYPES value new instead of malloc assert for allocation failure Reuse memory for ColorData and ScanData between collections. We still do alloc/free for other type of data, for example for arrays representing edges between SCCs. Actually print class name when enabling tarjan bridge logs. Add separate IsPromoted method to the gc interface Rename TriggerGCBridge to TriggerClientBridgeProcessing to be more specific about what it is doing.
ccb99ad
to
4d61fbe
Compare
Create reset event during javamarshal initialize
jkotas
reviewed
Jun 14, 2025
jkotas
reviewed
Jun 14, 2025
jkotas
reviewed
Jun 14, 2025
jkotas
reviewed
Jun 14, 2025
This was referenced Jun 19, 2025
jkotas
reviewed
Jun 19, 2025
99fb057
to
28a2b21
Compare
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
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.
This change adds runtime support for the GCBridge api described in #115506 and to be used on android. It includes most of initial work from #114184.
When the GCBridge feature is used, at the start of the application
JavaMarshal.Initialize
is called. This will provide to the runtime a native callback (markCrossReferences
) to be called during the GC when the collection takes place. During GC, we compute the set of strongly connected components containing bridge objects that are dead in the .NET space. These SCCs are passed to the callback so the .NET android implementation would reproduce the links between the java counterparts in order to determine whether the .NET object needs to be collected or not (The constraint is that the C# peer keeps the Java Peer alive and vice verse. We make no effort to handle finalization, so a resurrected C# object can have the Java Peer collected). Once the .NET Android runtime does the java collection it will report back to the runtime with the list of bridge objects that can be freed and with the previously passed SCC related pointers to be freed.A bridge object is an object that can have a JavaPeer. The CoreCLR runtime has no insight into this, the only thing it understands are cross reference handles. These are GCHandles that have an additional pointer associated with them, so additional information related to the java peer can be attached. Objects that have a cross reference handle allocated, will always survive the current GC collection, because we can't collect them until we get permission from the Java world. Once the cross reference gchandle is freed, the associated object becomes ordinary, detached from any java peer, and it is free for collection in the .NET heap.
At the end of mark phase, during GC, we iterate over all cross reference handles. When we encounter a handle with target that hasn't yet been marked, we add it to a list (these objects will have to be marked so they remain alive after this collection, given we need to probe the java world first). Once we obtained the set of dead bridge objects, we apply the tarjan algorithm (this algorithm is ported directly from mono's implementation). This algorithm will operate on the dead object graph, reachable from the initial set of dead bridge objects. In order to implement this secondary scanning mechanism, for objects that we reach, we hijack the object header with a
ScanData
that contains all information relevant to the SCC building algorithm. Once we finished building the SCCs, while still in the GC, we callback into the .NET Android viaTriggerClientBridgeProcessing
that will end up calling the mark cross reference callback provided byJavaMarshal.Initialize
. This callback will have to dispatch the neceesary work for another thread to run, since it needs to return quickly, for the C# GC to continue its execution.Because the world gets resumed without having decided yet whether the bridge objects will be alive or dead, for weak references, we would need to wait for the java bridge processing to finish before we can resolve the Target. Aside from the general problem of resurrecting a C# peer that has the Java Peer collected, this mechanism will be used internally by the .NET Android in order to manually manage liveness of these bridge objects, in the scenario of calling Dispose on an object. This synchronisation will be used at the core of .NET Android Runtime interop. In order to implement this, weak refs for bridge objects are not nulled during GC (these objects are promoted during collection) but rather at the finishing stage of bridge processing. This change is conservative and adds bridge waiting only for WeakReference, not when using GCHandle, following the existing approach in COM.
This PR adds a few tests in the runtime tests. The tests have a native counterpart that acts as the client bridge, not doing anything, just doing random sleep instead of doing the Java collection. The test creates a set of objects with certain links between then, creates weak refs to the BridgeObjects and then doesn't reference anything else. Depending on the built graph, it expects a certain number of SCCs and cross refs constructed by the tarjan algorithm, and then reports all bridge objects as alive or dead. The test will also check to see if the Target for all the weak refs is the expected one.