You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
[Java.Interop] JNI handles are now in a "control block" (#1339)
Originally from: #1334
Context: dotnet/runtime#114184
Context: dotnet/android#10125
Context: dotnet/android#10125 (comment)
Part of adding a GC bridge to CoreCLR are the new APIs:
namespace System.Runtime.InteropServices.Java;
public struct ComponentCrossReference {
public nint SourceGroupIndex, DestinationGroupIndex;
}
public unsafe struct StronglyConnectedComponent {
public nint Count;
public IntPtr* Context;
}
public static partial class JavaMarshal {
public static unsafe void Initialize(
delegate* unmanaged<
System.IntPtr, // sccsLen
StronglyConnectedComponent*, // sccs
System.IntPtr, // ccrsLen
ComponentCrossReference*, // ccrs
void> markCrossReferences);
public static GCHandle CreateReferenceTrackingHandle(object obj, IntPtr context);
public static IntPtr GetContext(GCHandle obj);
}
Of note is the "data flow" of `context`:
* `JavaMarshal.CreateReferenceTrackingHandle()` has a "`context`" parameter.
* The `context` parameter to `JavaMarshal.CreateReferenceTrackingHandle()` is the return value of `JavaMarshal.GetContext()`
* The `context` parameter to `JavaMarshal.CreateReferenceTrackingHandle()` is stored within `StronglyConnectedComponent.Context`.
* The `markCrossReferences` parameter of `JavaMarshal.Initialize()` is called by the GC bridge and given a native array of `StronglyConnectedComponent` instances, which contains `Context`.
The short of it is that the proposed GC bridge doesn't contain direct access to the `IJavaPeerable` instances in play. Instead, it has access to "context" which must contain the JNI Object Reference information that the `markCrossReferences` callback needs access to.
Furthermore, the `context` pointer value *cannot change*, i.e. it needs to be a native pointer value a'la **malloc**(3), ***not*** a value which can be moved by the GC. (The *contents* can change; the pointer value cannot.))
While we're still prototyping this, what we currently believe we need is the JNI object reference, JNI object reference type, and (maybe?) the JNI Weak Global Reference value and "refs added" values.
Update `IJavaPeerable` to add a `JniObjectReferenceControlBlock` property which can be used as the `context` parameter:
partial interface IJavaPeerable {
IntPtr JniObjectReferenceControlBlock => 0;
}
This supports usage of:
IJavaPeerable value = …
GCHandle handle = JavaMarshal.CreateReferenceTrackingHandle(
value,
value.JniObjectReferenceControlBlock
);
It works!
Build:
dotnet build -c Release -p:DotNetTargetFramework=net8.0 -t:Prepare
dotnet build -c Release -p:DotNetTargetFramework=net8.0
dotnet publish --self-contained -p:UseMonoRuntime=true -p:DotNetTargetFramework=net8.0 \
-p:UseAppHost=true -p:ErrorOnDuplicatePublishOutputFiles=false \
samples/Hello-Java.Base/Hello-Java.Base.csproj -r osx-x64
Run:
JAVA_INTEROP_GREF_LOG=g.txt ./samples/Hello-Java.Base/bin/Release/osx-x64/publish/Hello-Java.Base
`g.txt` contains:
…
*take_weak obj=0x10560dc70; handle=0x7f7f42f15148
+w+ grefc 8 gwrefc 1 obj-handle 0x7f7f42f15148/G -> new-handle 0x7f7f43008401/W from thread '(null)'(1)
take_weak_global_ref_jni
-g- grefc 7 gwrefc 1 handle 0x7f7f42f15148/G from thread '(null)'(1)
take_weak_global_ref_jni
*try_take_global obj=0x10560dc70 -> wref=0x7f7f43008401 handle=0x0
-w- grefc 7 gwrefc 0 handle 0x7f7f43008401/W from thread '(null)'(1)
take_global_ref_jni
Finalizing PeerReference=0x0/G IdentityHashCode=0x70dea4e Instance=0x59b98e2b Instance.Type=Hello.MyJLO
Implement MonoVM BC Bridge :: Java Marshaling API "thunk"
Commit 9e9daf4 suggested that we could probably "thunk" the MonoVM API to implement the proposed Java Bridge API..
Implemented the thunk!
0 commit comments