-
Notifications
You must be signed in to change notification settings - Fork 5k
[API Proposal]: Java Marshalling API #115506
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
Comments
Tagging subscribers to this area: @dotnet/interop-contrib |
In addition to the computation and passing of the SCCs and CCRs, that are handled by this API, there are a few implementation details currently present on mono.
After the original rounds of discussions around design, the conclusion was that we will have the fetching of a WeakReference target wait for bridge processing and the finishing step of the bridge processing would clear the weak refs for bridge objects with dead java peers. It seems that the only way to achieve this would to fully iterate over the weakrefs and null them, every single time a ReferenceTrackingHandle is freed. I don't think we can do this when we get back via Given this might be somewhat expensive, it raises the question on how relevant this nulling of weakrefs really is, considering that, even with the current design, resurrection could still happen and we can end up, in theory, with a C# object that had its java peer collected. I couldn't find a detailed reason for its existence, it seems to have been added in So as potential changes for this api:
|
The For a more visual explanation, please watch https://youtu.be/h57uvjMQMgU?t=534 which describes the same problem albeit with a slightly more eleborate (and less blocking) solution. |
Context: dotnet/runtime#115506 Context: dotnet/android#10125 We have an *API*, but not (yet) usable *implementation*. "Import" the `ManagedValueManager` from dotnet/android#10125, renaming to `JavaBridgedValueManager`, and add the proposed bridge API from dotnet/runtime#115506 to verify that it all compiles. It compiles! Next step: does it *work*? If I squint right, the proposed API looks very very similar to the existing MonoVM GC bridge API. Can I implement the proposed API in terms of MonoVM, and then have C# code perform the bridge code instead of native code, when using MonoVM? Let's find out!
@AaronRobinsonMSFT: what should the I had thought that it would be"mirror"
However, one field is missing in this world view:
Thus, how does partial struct StronglyConnectedComponent {
public int IsAlive; // 0 if dead, non-0 if alive
} |
Naming: |
That seems reasonable to me. @jkotas and I also previously talked about another callback, but replacing the "Release" with a broader "Finished" that serves both for memory clean-up and passing along the freed handles works too.
An explicit WaitForBridgeProcessing API shouldn't be needed as we can handle this as an implementation detail where the aforementioned "Finished" API must be called to track the bridge is now done.
Yep, it will be.
The |
which doesn't actually quite make sense to me: the bridge impl has a #114184 , meanwhile, has no Java I thus don't fully understand how it works. |
Right. Any calls to JVM APIs are the domain of the GC Bridge (2nd stage) and not needed in the dotnet/runtime repo. Only the SCC building (1st stage) is handled by runtime and that is passed to the 2nd stage which will do the JVM interaction. |
It sounds like the workflow you're invisioning goes something like this:
Steps (2) through (6) would all occur under what is now considered the "bridge processing" lock. |
I'm not sure that's race free. We need to prevent any modification to the compressed SCC graph. That's currently ensured by calling |
These are independent operations in this new model. GC bridge still needs to lock around their dictionary just as they currently do. The lock within is an implementation detail that avoids us having to expose an explicit begin/end callback mechanism. We internally call |
I'm not concerned about a race condition between Timeline:
|
This is triggered by the GC during a gen2. All threads are suspended. |
So that design is intentionally different from MonoVM which did NOT call the cross references callback in stop-the-world phase, correct? Was there any thought about possible interactions with background GCs? Just trying to make sure that this is specified, not necessarily questioning the intent... It essentially implies that the callback cannot be implemented in managed code and that Android/Java.Interop is responsible for saving the information and offloading it to background processing (unless you block the .NET GC until the Java GC finishes along with all the threads in the stop-the-world stage). |
Are you suggesting here that .NET Android could do its own synchronization to prevent the issue that Filip was describing, without any help from the runtime ? For example, when
I don't see why we would need to collect any @jonpryor Any opinion on nulling of the
The alternative would be that |
There's a problem in the example you've shown. Resolving the Pseudo-code: WeakReference<JavaHttpClient> cachedHttpClient;
... // GC1 start here
JavaHttpClient? localHttpClient = cachedHttpClient.Target;
if (localHttpClient is null) {
// Create new HTTP client and assign it to localHttpClient and cachedHttpClient
}
// Thread2 may have finished Java GC and disposed the JavaHttpClient instance
await localHttpClient.GetAsync(...); |
@filipnavara Overall the summary of my point is that, due to resurrection, in theory it is possible to have C# objects with dead java peers, even now on mono. For example
So waiting and nulling |
Definitely, but using finalizer and resurrecting objects is rather odd edge case where I expect all the hell to break loose. Using |
Yes. They will need to do that anyways and already do. The runtime isn't going to be able to lock their dictionary that maps from JNI handle to
This is exactly the workflow being proposed. We're on the same page. |
It looks to me like the current proposed API shape doesn't cover enough surface to make I'm still generally not convinced whether it should be the consumer of |
Correct. The current "Release" needs to be updated to be a "Finish" and pass along the
It is going to be simpler for the bridge to handle the thread management or else this API becomes more complicated in an area we are explicitly not trying to innovate on. As previously mentioned if it occurs we need to innovate it won't just be the data structures but also the general API and semantics that will move as well.
The background thread can be written in managed with this approach as well and it is something we could experiement with, but my guess is it will be less performant to do it in managed code. I say that because, looking at @BrzVlad's flow diagram, it would mean that while the 2nd stage was running subsequent GCs would impact it and delay getting the 2nd stage complete. The logic in the 2nd stage is relatively straight forward, but does interact with the JVM a fair bit. Pushing that logic into managed would also increase the number of interop calls substantially for very little benefit (JVM strong <-> weak refs). |
I guess that's fair point for not doing it in managed code. My argument was never about allowing a part of it in managed code, it was about allowing all of it in managed code which was tried (dotnet/java-interop#1334) and failed for obvious reasons. |
@BrzVlad Do you think with the updated API shape we can take this to API review? I'd like to get this reviewed sooner rather than later. |
In my opinion the API looks good. To better visualize the usage/implementation, I have the prototype at https://github.com/BrzVlad/runtime/commits/feature-clr-gcbridge/ which appears to be working correctly, passing the local test that I added. The branch is a WIP, I haven't yet cleaned it up. The last two commits contain the waiting for bridge processing when checking the target of a |
@BrzVlad The overall flow/architecture look good. There are two issues that need to be addressed.
Once the branch is in a PR, I can make a few changes on the VM side. Otherwise, this looks like what we've talked about. |
Context: dotnet/runtime#115506 Context: dotnet/android#10125 We have an *API*, but not (yet) usable *implementation*. "Import" the `ManagedValueManager` from dotnet/android#10125, renaming to `JavaBridgedValueManager`, and add the proposed bridge API from dotnet/runtime#115506 to verify that it all compiles. It compiles! Next step: does it *work*? If I squint right, the proposed API looks very very similar to the existing MonoVM GC bridge API. Can I implement the proposed API in terms of MonoVM, and then have C# code perform the bridge code instead of native code, when using MonoVM? Let's find out!
Uh oh!
There was an error while loading. Please reload this page.
Background and motivation
For interop with JVM based languages, an interaction model must be made to facilitate lifetime managements through GC coordination. This support is primary for Android for .NET, but there is no technical limitation to this support being enabled for non-Android scenarios.
See #114184 and https://github.com/BrzVlad/runtime/tree/feature-clr-gcbridge
API Proposal
API Usage
Case example can be found at #114184
Alternative Designs
Place under
System.Runtime.InteropServices.GCBridge
Do not limit to
SupportedOSPlatform("android")
Risks
Risk is missing some scenario/semantic in order to ensure Android for .NET is successful. This would be a new interop stack and be narrow in scope, following our Swift, ObjectiveC and WinRT interop solutions in .NET Core.
The text was updated successfully, but these errors were encountered: