Skip to content

Commit 40d27eb

Browse files
[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!
1 parent 81a9015 commit 40d27eb

File tree

10 files changed

+280
-93
lines changed

10 files changed

+280
-93
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: 25 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@
55
namespace Java.Interop
66
{
77
[JniTypeSignature (JniTypeName, GenerateJavaPeer=false)]
8-
unsafe public class JavaException : Exception, IJavaPeerable
8+
unsafe public partial class JavaException : Exception, IJavaPeerable
99
{
1010
internal const string JniTypeName = "java/lang/Throwable";
1111
readonly static JniPeerMembers _members = new JniPeerMembers (JniTypeName, typeof (JavaException));
@@ -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,14 @@ 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 (reference);
142+
} else {
143+
c->handle = reference.Handle;
144+
c->handle_type = (int) reference.Type;
145+
}
142146
#endif // FEATURE_JNIOBJECTREFERENCE_INTPTRS
143147

144148
JniObjectReference.Dispose (ref reference, options);
@@ -260,11 +264,13 @@ protected void SetJavaStackTrace (JniObjectReference peerReferenceOverride = def
260264

261265
void IJavaPeerable.Disposed ()
262266
{
267+
JniManagedPeerState |= Disposed;
263268
Dispose (disposing: true);
264269
}
265270

266271
void IJavaPeerable.Finalized ()
267272
{
273+
JniManagedPeerState |= Disposed;
268274
Dispose (disposing: false);
269275
}
270276

@@ -281,7 +287,15 @@ void IJavaPeerable.SetJniManagedPeerState (JniManagedPeerStates value)
281287
void IJavaPeerable.SetPeerReference (JniObjectReference reference)
282288
{
283289
SetPeerReference (ref reference, JniObjectReferenceOptions.Copy);
290+
#if FEATURE_JNIOBJECTREFERENCE_INTPTRS
291+
if (!reference.IsValid && JniManagedPeerState.HasFlag (Disposed)) {
292+
Java.Interop.JniObjectReferenceControlBlock.Free (ref jniObjectReferenceControlBlock);
293+
}
294+
#endif // FEATURE_JNIOBJECTREFERENCE_INTPTRS
284295
}
296+
297+
IntPtr IJavaPeerable.JniObjectReferenceControlBlock =>
298+
(IntPtr) jniObjectReferenceControlBlock;
285299
}
286300
}
287301

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

Lines changed: 27 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@ namespace Java.Interop
88
{
99
[JniTypeSignature ("java/lang/Object", GenerateJavaPeer=false)]
1010
[Serializable]
11-
unsafe public class JavaObject : IJavaPeerable
11+
unsafe public partial class JavaObject : IJavaPeerable
1212
{
1313
internal const DynamicallyAccessedMemberTypes Constructors = DynamicallyAccessedMemberTypes.PublicConstructors | DynamicallyAccessedMemberTypes.NonPublicConstructors;
1414

@@ -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+
[NonSerialized] 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,14 @@ 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 (reference);
97+
} else {
98+
c->handle = reference.Handle;
99+
c->handle_type = (int) reference.Type;
100+
}
97101
#endif // FEATURE_JNIOBJECTREFERENCE_INTPTRS
98102

99103
JniObjectReference.Dispose (ref reference, options);
@@ -148,11 +152,13 @@ public override unsafe int GetHashCode ()
148152

149153
void IJavaPeerable.Disposed ()
150154
{
155+
managedPeerState |= Disposed;
151156
Dispose (disposing: true);
152157
}
153158

154159
void IJavaPeerable.Finalized ()
155160
{
161+
managedPeerState |= Disposed;
156162
Dispose (disposing: false);
157163
}
158164

@@ -169,7 +175,16 @@ void IJavaPeerable.SetJniManagedPeerState (JniManagedPeerStates value)
169175
void IJavaPeerable.SetPeerReference (JniObjectReference reference)
170176
{
171177
SetPeerReference (ref reference, JniObjectReferenceOptions.Copy);
178+
179+
#if FEATURE_JNIOBJECTREFERENCE_INTPTRS
180+
if (!reference.IsValid && managedPeerState.HasFlag (Disposed)) {
181+
Java.Interop.JniObjectReferenceControlBlock.Free (ref jniObjectReferenceControlBlock);
182+
}
183+
#endif // FEATURE_JNIOBJECTREFERENCE_INTPTRS
172184
}
185+
186+
IntPtr IJavaPeerable.JniObjectReferenceControlBlock =>
187+
(IntPtr) jniObjectReferenceControlBlock;
173188
}
174189
}
175190

src/Java.Interop/Java.Interop/JniManagedPeerStates.cs

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,4 +11,12 @@ public enum JniManagedPeerStates {
1111
/// <include file="../Documentation/Java.Interop/JniManagedPeerStates.xml" path="/docs/member[@name='F:Replaceable']/*" />
1212
Replaceable = (1 << 1),
1313
}
14+
15+
partial class JavaObject {
16+
const JniManagedPeerStates Disposed = (JniManagedPeerStates) (1 << 2);
17+
}
18+
19+
partial class JavaException {
20+
const JniManagedPeerStates Disposed = (JniManagedPeerStates) (1 << 2);
21+
}
1422
}
Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
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 (JniObjectReference reference)
15+
{
16+
var value = (JniObjectReferenceControlBlock*) NativeMemory.AllocZeroed (1, (uint) Size);
17+
value->handle = reference.Handle;
18+
value->handle_type = (int) reference.Type;
19+
return value;
20+
}
21+
22+
public static unsafe void Free (ref JniObjectReferenceControlBlock* value)
23+
{
24+
if (value == null) {
25+
return;
26+
}
27+
NativeMemory.Free (value);
28+
value = null;
29+
}
30+
}

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

0 commit comments

Comments
 (0)