Skip to content

Commit 7f6f237

Browse files
jonpryorjonathanpeppers
authored andcommitted
[Java.Interop] JNI handles are now in a "control block"
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!
1 parent b1a0107 commit 7f6f237

File tree

8 files changed

+246
-89
lines changed

8 files changed

+246
-89
lines changed

src/Java.Interop/Java.Interop/IJavaPeerable.cs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,8 @@ public interface IJavaPeerable : IDisposable
3737
void Disposed ();
3838
/// <include file="../Documentation/Java.Interop/IJavaPeerable.xml" path="/docs/member[@name='M:Finalized']/*" />
3939
void Finalized ();
40+
41+
IntPtr JniObjectReferenceControlBlock => IntPtr.Zero;
4042
}
4143
}
4244

src/Java.Interop/Java.Interop/JavaException.cs

Lines changed: 19 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -18,13 +18,7 @@ unsafe public class JavaException : Exception, IJavaPeerable
1818
JniObjectReference reference;
1919
#endif // FEATURE_JNIOBJECTREFERENCE_SAFEHANDLES
2020
#if FEATURE_JNIOBJECTREFERENCE_INTPTRS
21-
IntPtr handle;
22-
JniObjectReferenceType handle_type;
23-
#pragma warning disable 0169
24-
// Used by JavaInteropGCBridge
25-
IntPtr weak_handle;
26-
int refs_added;
27-
#pragma warning restore 0169
21+
unsafe JniObjectReferenceControlBlock* jniObjectReferenceControlBlock;
2822
#endif // FEATURE_JNIOBJECTREFERENCE_INTPTRS
2923

3024
protected static readonly JniObjectReference* InvalidJniObjectReference = null;
@@ -106,7 +100,11 @@ public JniObjectReference PeerReference {
106100
return reference;
107101
#endif // FEATURE_JNIOBJECTREFERENCE_SAFEHANDLES
108102
#if FEATURE_JNIOBJECTREFERENCE_INTPTRS
109-
return new JniObjectReference (handle, handle_type);
103+
var c = jniObjectReferenceControlBlock;
104+
if (c == null) {
105+
return default;
106+
}
107+
return new JniObjectReference (c->handle, (JniObjectReferenceType) c->handle_type);
110108
#endif // FEATURE_JNIOBJECTREFERENCE_INTPTRS
111109
}
112110
}
@@ -137,8 +135,13 @@ protected void SetPeerReference (ref JniObjectReference reference, JniObjectRefe
137135
this.reference = reference;
138136
#endif // FEATURE_JNIOBJECTREFERENCE_SAFEHANDLES
139137
#if FEATURE_JNIOBJECTREFERENCE_INTPTRS
140-
this.handle = reference.Handle;
141-
this.handle_type = reference.Type;
138+
var c = jniObjectReferenceControlBlock;
139+
if (c == null) {
140+
c = jniObjectReferenceControlBlock =
141+
Java.Interop.JniObjectReferenceControlBlock.Alloc ();
142+
}
143+
c->handle = reference.Handle;
144+
c->handle_type = (int) reference.Type;
142145
#endif // FEATURE_JNIOBJECTREFERENCE_INTPTRS
143146

144147
JniObjectReference.Dispose (ref reference, options);
@@ -167,6 +170,9 @@ protected virtual void Dispose (bool disposing)
167170
if (inner != null) {
168171
inner.Dispose ();
169172
}
173+
#if FEATURE_JNIOBJECTREFERENCE_INTPTRS
174+
Java.Interop.JniObjectReferenceControlBlock.Free (ref jniObjectReferenceControlBlock);
175+
#endif // FEATURE_JNIOBJECTREFERENCE_INTPTRS
170176
}
171177

172178
public override bool Equals (object? obj)
@@ -282,6 +288,9 @@ void IJavaPeerable.SetPeerReference (JniObjectReference reference)
282288
{
283289
SetPeerReference (ref reference, JniObjectReferenceOptions.Copy);
284290
}
291+
292+
IntPtr IJavaPeerable.JniObjectReferenceControlBlock =>
293+
(IntPtr) jniObjectReferenceControlBlock;
285294
}
286295
}
287296

src/Java.Interop/Java.Interop/JavaObject.cs

Lines changed: 20 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -25,13 +25,7 @@ unsafe public class JavaObject : IJavaPeerable
2525
[NonSerialized] JniObjectReference reference;
2626
#endif // FEATURE_JNIOBJECTREFERENCE_SAFEHANDLES
2727
#if FEATURE_JNIOBJECTREFERENCE_INTPTRS
28-
[NonSerialized] IntPtr handle;
29-
[NonSerialized] JniObjectReferenceType handle_type;
30-
#pragma warning disable 0169
31-
// Used by JavaInteropGCBridge
32-
[NonSerialized] IntPtr weak_handle;
33-
[NonSerialized] int refs_added;
34-
#pragma warning restore 0169
28+
unsafe JniObjectReferenceControlBlock* jniObjectReferenceControlBlock;
3529
#endif // FEATURE_JNIOBJECTREFERENCE_INTPTRS
3630

3731
protected static readonly JniObjectReference* InvalidJniObjectReference = null;
@@ -41,13 +35,17 @@ unsafe public class JavaObject : IJavaPeerable
4135
JniEnvironment.Runtime.ValueManager.FinalizePeer (this);
4236
}
4337

44-
public JniObjectReference PeerReference {
38+
public unsafe JniObjectReference PeerReference {
4539
get {
4640
#if FEATURE_JNIOBJECTREFERENCE_SAFEHANDLES
4741
return reference;
4842
#endif // FEATURE_JNIOBJECTREFERENCE_SAFEHANDLES
4943
#if FEATURE_JNIOBJECTREFERENCE_INTPTRS
50-
return new JniObjectReference (handle, handle_type);
44+
var c = jniObjectReferenceControlBlock;
45+
if (c == null) {
46+
return default;
47+
}
48+
return new JniObjectReference (c->handle, (JniObjectReferenceType) c->handle_type);
5149
#endif // FEATURE_JNIOBJECTREFERENCE_INTPTRS
5250
}
5351
}
@@ -92,8 +90,13 @@ protected void SetPeerReference (ref JniObjectReference reference, JniObjectRefe
9290
this.reference = reference;
9391
#endif // FEATURE_JNIOBJECTREFERENCE_SAFEHANDLES
9492
#if FEATURE_JNIOBJECTREFERENCE_INTPTRS
95-
this.handle = reference.Handle;
96-
this.handle_type = reference.Type;
93+
var c = jniObjectReferenceControlBlock;
94+
if (c == null) {
95+
c = jniObjectReferenceControlBlock =
96+
Java.Interop.JniObjectReferenceControlBlock.Alloc ();
97+
}
98+
c->handle = reference.Handle;
99+
c->handle_type = (int) reference.Type;
97100
#endif // FEATURE_JNIOBJECTREFERENCE_INTPTRS
98101

99102
JniObjectReference.Dispose (ref reference, options);
@@ -118,6 +121,9 @@ public virtual void DisposeUnlessReferenced ()
118121

119122
protected virtual void Dispose (bool disposing)
120123
{
124+
#if FEATURE_JNIOBJECTREFERENCE_INTPTRS
125+
Java.Interop.JniObjectReferenceControlBlock.Free (ref jniObjectReferenceControlBlock);
126+
#endif // FEATURE_JNIOBJECTREFERENCE_INTPTRS
121127
}
122128

123129
public override bool Equals (object? obj)
@@ -170,6 +176,9 @@ void IJavaPeerable.SetPeerReference (JniObjectReference reference)
170176
{
171177
SetPeerReference (ref reference, JniObjectReferenceOptions.Copy);
172178
}
179+
180+
IntPtr IJavaPeerable.JniObjectReferenceControlBlock =>
181+
(IntPtr) jniObjectReferenceControlBlock;
173182
}
174183
}
175184

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
using System;
2+
using System.Runtime.InteropServices;
3+
4+
namespace Java.Interop;
5+
6+
internal struct JniObjectReferenceControlBlock {
7+
public IntPtr handle;
8+
public int handle_type;
9+
public IntPtr weak_handle;
10+
public int refs_added;
11+
12+
public static readonly int Size = Marshal.SizeOf<JniObjectReferenceControlBlock>();
13+
14+
public static unsafe JniObjectReferenceControlBlock* Alloc ()
15+
{
16+
return (JniObjectReferenceControlBlock*) NativeMemory.AllocZeroed (1, (uint) Size);
17+
}
18+
19+
public static unsafe void Free (ref JniObjectReferenceControlBlock* value)
20+
{
21+
if (value == null) {
22+
return;
23+
}
24+
NativeMemory.Free (value);
25+
value = null;
26+
}
27+
}

src/Java.Interop/PublicAPI.Unshipped.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,3 +11,4 @@ Java.Interop.JniRuntime.JniTypeManager.GetInvokerType(System.Type! type) -> Syst
1111
Java.Interop.JniRuntime.JniValueManager.GetPeer(Java.Interop.JniObjectReference reference, System.Type? targetType = null) -> Java.Interop.IJavaPeerable?
1212
Java.Interop.JniTypeSignatureAttribute.InvokerType.get -> System.Type?
1313
Java.Interop.JniTypeSignatureAttribute.InvokerType.set -> void
14+
Java.Interop.IJavaPeerable.JniObjectReferenceControlBlock.get -> nint

src/Java.Runtime.Environment/Java.Interop/MonoRuntimeValueManager.cs

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -415,6 +415,18 @@ partial class JreNativeMethods {
415415

416416
[DllImport (JavaInteropLib, CallingConvention=CallingConvention.Cdecl)]
417417
internal static extern void java_interop_gc_bridge_wait_for_bridge_processing (IntPtr bridge);
418+
419+
[DllImport (JavaInteropLib, CallingConvention=CallingConvention.Cdecl)]
420+
internal static extern unsafe int java_interop_gc_bridge_set_mark_cross_references (
421+
IntPtr bridge,
422+
delegate* unmanaged[Cdecl]<System.Runtime.InteropServices.Java.MarkCrossReferences*, void> markCrossReferences
423+
);
424+
425+
[DllImport (JavaInteropLib, CallingConvention=CallingConvention.Cdecl)]
426+
internal static extern unsafe int java_interop_gc_bridge_release_mark_cross_references_resources (
427+
IntPtr bridge,
428+
System.Runtime.InteropServices.Java.MarkCrossReferences* crossReferences
429+
);
418430
}
419431

420432
sealed class OverrideStackTrace : Exception {

0 commit comments

Comments
 (0)