Skip to content

Commit 41aa06d

Browse files
[Java.Interop] Lazily allocate instance method cache
Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
1 parent 47eabaf commit 41aa06d

2 files changed

Lines changed: 38 additions & 6 deletions

File tree

external/Java.Interop/src/Java.Interop/Java.Interop/JniPeerMembers.JniInstanceMethods.cs

Lines changed: 15 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22

33
using System;
44
using System.Collections.Concurrent;
5+
using System.Threading;
56

67
namespace Java.Interop
78
{
@@ -39,12 +40,12 @@ internal JniType JniPeerType {
3940

4041
readonly Type DeclaringType;
4142

42-
readonly ConcurrentDictionary<string, JniMethodInfo> InstanceMethods = new ConcurrentDictionary<string, JniMethodInfo> (1, 3, StringComparer.Ordinal);
43+
ConcurrentDictionary<string, JniMethodInfo>? InstanceMethods;
4344
readonly ConcurrentDictionary<Type, JniInstanceMethods> SubclassConstructors = new ConcurrentDictionary<Type, JniInstanceMethods> (1, 1);
4445

4546
internal void Dispose ()
4647
{
47-
InstanceMethods.Clear ();
48+
Interlocked.Exchange (ref InstanceMethods, null)?.Clear ();
4849
foreach (var p in SubclassConstructors.Values)
4950
p.Dispose ();
5051
SubclassConstructors.Clear ();
@@ -58,7 +59,7 @@ public JniMethodInfo GetConstructor (string signature)
5859
{
5960
if (signature == null)
6061
throw new ArgumentNullException (nameof (signature));
61-
return InstanceMethods.GetOrAdd (signature, static (member, methods) =>
62+
return GetInstanceMethods ().GetOrAdd (signature, static (member, methods) =>
6263
methods.JniPeerType.GetConstructor (member), this);
6364
}
6465

@@ -94,13 +95,23 @@ internal JniInstanceMethods GetConstructorsForType (Type declaringType)
9495

9596
public JniMethodInfo GetMethodInfo (string encodedMember)
9697
{
97-
return InstanceMethods.GetOrAdd (encodedMember, static (member, methods) => {
98+
return GetInstanceMethods ().GetOrAdd (encodedMember, static (member, methods) => {
9899
string method, signature;
99100
JniPeerMembers.GetNameAndSignature (member, out method, out signature);
100101
return methods.GetMethodInfo (method, signature);
101102
}, this);
102103
}
103104

105+
ConcurrentDictionary<string, JniMethodInfo> GetInstanceMethods ()
106+
{
107+
var methods = Volatile.Read (ref InstanceMethods);
108+
if (methods != null)
109+
return methods;
110+
111+
var candidate = new ConcurrentDictionary<string, JniMethodInfo> (1, 3, StringComparer.Ordinal);
112+
return Interlocked.CompareExchange (ref InstanceMethods, candidate, null) ?? candidate;
113+
}
114+
104115
JniMethodInfo GetMethodInfo (string method, string signature)
105116
{
106117
var m = (JniMethodInfo?) null;

external/Java.Interop/tests/Java.Interop-Tests/Java.Interop/JniPeerMembersTests.cs

Lines changed: 23 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
using System;
22
using System.Collections.Concurrent;
33
using System.Reflection;
4+
using System.Threading.Tasks;
45

56
using Java.Interop;
67
using NUnit.Framework;
@@ -21,15 +22,35 @@ public void Ctor_CanReferenceNonexistentType ()
2122
[Category ("TrimmableTypeMapUnsupported")]
2223
public void VirtualInvokeOnBaseInvokesMostDerivedJavaMethod ()
2324
{
24-
var registered = GetInstanceMethods (MyString._members.InstanceMethods);
25-
Assert.AreEqual (0, registered.Count);
25+
Assert.IsNull (GetInstanceMethods (MyString._members.InstanceMethods));
2626
using (var s = new MyString ("hello!")) {
27+
var registered = GetInstanceMethods (MyString._members.InstanceMethods);
2728
Assert.AreEqual (1, registered.Count); // for the constructor
2829
Assert.AreEqual ("hello!", s.ToString ());
2930
Assert.AreEqual (1, registered.Count);
3031
}
3132
}
3233

34+
[Test]
35+
[Category ("TrimmableTypeMapUnsupported")]
36+
public void ConcurrentFirstUsePublishesSingleInstanceMethodCache ()
37+
{
38+
var members = new JniPeerMembers (MyString.JniTypeName, typeof (MyString));
39+
var methods = members.InstanceMethods;
40+
var constructors = new JniMethodInfo [16];
41+
42+
Assert.IsNull (GetInstanceMethods (methods));
43+
Parallel.For (0, constructors.Length, i => constructors [i] = methods.GetConstructor ("()V"));
44+
45+
var registered = GetInstanceMethods (methods);
46+
Assert.AreEqual (1, registered.Count);
47+
foreach (var constructor in constructors)
48+
Assert.AreSame (constructors [0], constructor);
49+
Assert.AreSame (registered ["()V"], constructors [0]);
50+
51+
JniPeerMembers.Dispose (members);
52+
}
53+
3354
static ConcurrentDictionary<string, JniMethodInfo> GetInstanceMethods (JniPeerMembers.JniInstanceMethods methods)
3455
{
3556
var f = typeof (JniPeerMembers.JniInstanceMethods).GetField ("InstanceMethods", BindingFlags.NonPublic | BindingFlags.Instance);

0 commit comments

Comments
 (0)