Skip to content
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

[Java.Interop] Add JavaPeerableRegistrationScope #1238

Closed
wants to merge 1 commit into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
35 changes: 35 additions & 0 deletions src/Java.Interop/Java.Interop/JavaPeerableRegistrationScope.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Diagnostics.CodeAnalysis;

namespace Java.Interop {
[Experimental ("JI9999")]
public enum JavaPeerableRegistrationScopeCleanup {
RegisterWithManager,
Dispose,
Release,
}

[Experimental ("JI9999")]
public ref struct JavaPeerableRegistrationScope {
PeerableCollection? scope;
bool disposed;

public JavaPeerableRegistrationScope (JavaPeerableRegistrationScopeCleanup cleanup)
{
scope = JniEnvironment.CurrentInfo.BeginRegistrationScope (cleanup);
disposed = false;
}

public void Dispose ()
{
if (disposed) {
return;
}
disposed = true;
JniEnvironment.CurrentInfo.EndRegistrationScope (scope);
scope = null;
}
}
}
20 changes: 4 additions & 16 deletions src/Java.Interop/Java.Interop/JavaProxyObject.cs
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,6 @@ sealed class JavaProxyObject : JavaObject, IEquatable<JavaProxyObject>
internal const string JniTypeName = "net/dot/jni/internal/JavaProxyObject";

static readonly JniPeerMembers _members = new JniPeerMembers (JniTypeName, typeof (JavaProxyObject));
static readonly ConditionalWeakTable<object, JavaProxyObject> CachedValues = new ConditionalWeakTable<object, JavaProxyObject> ();

[JniAddNativeMethodRegistrationAttribute]
static void RegisterNativeMembers (JniNativeMethodRegistrationArguments args)
Expand All @@ -29,10 +28,8 @@ public override JniPeerMembers JniPeerMembers {
}
}

JavaProxyObject (object value)
internal JavaProxyObject (object value)
{
if (value == null)
throw new ArgumentNullException (nameof (value));
Value = value;
}

Expand All @@ -57,19 +54,10 @@ public override bool Equals (object? obj)
return Value.ToString ();
}

[return: NotNullIfNotNull ("object")]
public static JavaProxyObject? GetProxy (object value)
protected override void Dispose (bool disposing)
{
if (value == null)
return null;

lock (CachedValues) {
if (CachedValues.TryGetValue (value, out var proxy))
return proxy;
proxy = new JavaProxyObject (value);
CachedValues.Add (value, proxy);
return proxy;
}
base.Dispose (disposing);
JniEnvironment.Runtime.ValueManager.RemoveProxy (Value);
}

// TODO: Keep in sync with the code generated by ExportedMemberBuilder
Expand Down
125 changes: 125 additions & 0 deletions src/Java.Interop/Java.Interop/JniEnvironment.cs
Original file line number Diff line number Diff line change
Expand Up @@ -2,10 +2,13 @@

using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Diagnostics;
using System.Diagnostics.CodeAnalysis;
using System.Linq;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
using System.Text;
using System.Threading;

namespace Java.Interop {
Expand Down Expand Up @@ -209,6 +212,8 @@ sealed class JniEnvironmentInfo : IDisposable {
bool disposed;
JniRuntime? runtime;

List<PeerableCollection?>? scopes;

public int LocalReferenceCount {get; internal set;}
public bool WithinNewObjectScope {get; set;}
public JniRuntime Runtime {
Expand Down Expand Up @@ -243,6 +248,11 @@ public bool IsValid {
get {return Runtime != null && environmentPointer != IntPtr.Zero;}
}

public List<PeerableCollection?>?
Scopes => scopes;
public PeerableCollection? CurrentScope =>
scopes == null ? null : scopes [scopes.Count-1];

public JniEnvironmentInfo ()
{
Runtime = JniRuntime.CurrentRuntime;
Expand Down Expand Up @@ -293,6 +303,44 @@ public void Dispose ()
disposed = true;
}

#pragma warning disable JI9999
public PeerableCollection? BeginRegistrationScope (JavaPeerableRegistrationScopeCleanup cleanup)
{
if (cleanup != JavaPeerableRegistrationScopeCleanup.RegisterWithManager &&
!Runtime.ValueManager.SupportsPeerableRegistrationScopes) {
throw new NotSupportedException ("Peerable registration scopes are not supported by this runtime.");
}
scopes ??= new List<PeerableCollection?> ();
if (cleanup == JavaPeerableRegistrationScopeCleanup.RegisterWithManager) {
scopes.Add (null);
return null;
}
var scope = new PeerableCollection (cleanup);
scopes.Add (scope);
return scope;
}

public void EndRegistrationScope (PeerableCollection? scope)
{
Debug.Assert (scopes != null);
if (scopes == null) {
return;
}

for (int i = scopes.Count; i > 0; --i) {
var s = scopes [i - 1];
if (s == scope) {
scopes.RemoveAt (i - 1);
break;
}
}
if (scopes.Count == 0) {
scopes = null;
}
scope?.DisposeScope ();
}
#pragma warning restore JI9999

#if FEATURE_JNIENVIRONMENT_SAFEHANDLES
internal List<List<JniLocalReference>> LocalReferences = new List<List<JniLocalReference>> () {
new List<JniLocalReference> (),
Expand All @@ -309,5 +357,82 @@ static unsafe JniEnvironmentInvoker CreateInvoker (IntPtr handle)
}
#endif // !FEATURE_JNIENVIRONMENT_JI_PINVOKES
}

#pragma warning disable JI9999
sealed class PeerableCollection : KeyedCollection<int, IJavaPeerable> {
public JavaPeerableRegistrationScopeCleanup Cleanup { get; }

public PeerableCollection (JavaPeerableRegistrationScopeCleanup cleanup)
{
Cleanup = cleanup;
}

protected override int GetKeyForItem (IJavaPeerable item) => item.JniIdentityHashCode;

public IJavaPeerable? GetPeerable (JniObjectReference reference, int identityHashCode)
{
if (!reference.IsValid) {
return null;
}
if (TryGetValue (identityHashCode, out var p) &&
JniEnvironment.Types.IsSameObject (reference, p.PeerReference)) {
return p;
}
return null;
}

public void DisposeScope ()
{
Console.Error.WriteLine ($"# jonp: DisposeScope: {Cleanup}");
Debug.Assert (Cleanup != JavaPeerableRegistrationScopeCleanup.RegisterWithManager);
switch (Cleanup) {
case JavaPeerableRegistrationScopeCleanup.Dispose:
List<Exception>? exceptions = null;
foreach (var p in this) {
DisposePeer (p, ref exceptions);
}
Clear ();
if (exceptions != null) {
throw new AggregateException ("Exceptions while disposing peers.", exceptions);
}
break;
case JavaPeerableRegistrationScopeCleanup.Release:
Clear ();
break;
case JavaPeerableRegistrationScopeCleanup.RegisterWithManager:
default:
throw new NotSupportedException ($"Unsupported scope cleanup value: {Cleanup}");
}

[SuppressMessage ("Design", "CA1031:Do not catch general exception types",
Justification = "Exceptions are bundled into an AggregateException and rethrown")]
static void DisposePeer (IJavaPeerable peer, ref List<Exception>? exceptions)
{
try {
Console.Error.WriteLine ($"# jonp: DisposeScope: disposing of: {peer} {peer.PeerReference}");
peer.Dispose ();
} catch (Exception e) {
exceptions ??= new ();
exceptions.Add (e);
Trace.WriteLine (e);
}
}
}

public override string ToString ()
{
var c = (Collection<IJavaPeerable>) this;
var s = new StringBuilder ();
s.Append ("PeerableCollection[").Append (Count).Append ("](");
for (int i = 0; i < Count; ++i ) {
s.AppendLine ();
var e = c [i];
s.Append ($" [{i}] hash={e.JniIdentityHashCode} ref={e.PeerReference} type={e.GetType ().ToString ()} value=`{e.ToString ()}`");
}
s.Append (")");
return s.ToString ();
}
}
#pragma warning restore JI9999
}

Loading