Skip to content

Commit 6a389de

Browse files
committed
[Java.Interop-Tests] Add CreatePeer_ReplaceableDoesNotReplace test
Context: 3043d89 Context: dotnet/android#9862 Context: dotnet/android#9862 (comment) In dotnet/android#9862, there is an observed "race condition" around `Android.App.Application` subclass creation. *Two* instances of `AndroidApp` were created, one from the "normal" app startup: at crc647fae2f69c19dcd0d.AndroidApp.n_onCreate(Native Method) at crc647fae2f69c19dcd0d.AndroidApp.onCreate(AndroidApp.java:25) at android.app.Instrumentation.callApplicationOnCreate(Instrumentation.java:1316) and another from an `androidx.work.WorkerFactory`: at mono.android.TypeManager.n_activate(Native Method) at mono.android.TypeManager.Activate(TypeManager.java:7) at crc647fae2f69c19dcd0d.SyncWorker.<init>(SyncWorker.java:23) at java.lang.reflect.Constructor.newInstance0(Native Method) at java.lang.reflect.Constructor.newInstance(Constructor.java:343) at androidx.work.WorkerFactory.createWorkerWithDefaultFallback(WorkerFactory.java:95) However, what was odd about this "race condition" was that the *second* instance created would reliably win! Further investigation suggested that this was less of a "race condition" and more a bug in `AndroidRuntime`, wherein when "Replaceable" instances were created, an existing instance would *always* be replaced. Aside: JniManagedPeerStates.Replaceable is from 3043d89: > `JniManagedPeerStates.Replaceable` … means > that the Peer instance was created through the activation constructor. > It additionally means that if two managed instances are created around > the same Java instance, the non-Replaceable instance will be the one > returned by JniRuntime.JniValueManager.PeekObject(). What we're observing in dotnet/android#9862 is that while the Replaceable instance is replaced, it's being replaced by *another* Replaceable instance! This feels bananas; yes, Replaceable should be replacable, but only by *non*-Replaceable instances. Update `JniRuntimeJniValueManagerContract` to add a new `CreatePeer_ReplaceableDoesNotReplace()` test to codify the desired semantic that Replaceable instances do not replace Replaceable instances. Surprisingly, this does not fail on java-interop! Apparently `ManagedValueManager.AddPeer()` bails early when `PeekPeer()` finds a value, while `AndroidRuntime.AddPeer()` does not bail early.
1 parent c1cd06d commit 6a389de

File tree

1 file changed

+20
-0
lines changed

1 file changed

+20
-0
lines changed

tests/Java.Interop-Tests/Java.Interop/JniRuntimeJniValueManagerContract.cs

+20
Original file line numberDiff line numberDiff line change
@@ -159,6 +159,26 @@ public unsafe void CreatePeer_UsesFallbackType ()
159159
Assert.AreSame (typeof (IJavaInterfaceInvoker), p!.GetType ());
160160
}
161161

162+
[Test]
163+
public void CreatePeer_ReplaceableDoesNotReplace ()
164+
{
165+
var v = new AnotherJavaInterfaceImpl ();
166+
var lref = v.PeerReference.NewLocalRef ();
167+
v.Dispose ();
168+
169+
try {
170+
var peer1 = valueManager.CreatePeer (ref lref, JniObjectReferenceOptions.Copy, typeof (AnotherJavaInterfaceImpl));
171+
Assert.IsTrue (peer1.JniManagedPeerState.HasFlag (JniManagedPeerStates.Replaceable));
172+
var peer2 = valueManager.CreatePeer (ref lref, JniObjectReferenceOptions.Copy, typeof (AnotherJavaInterfaceImpl));
173+
Assert.IsTrue (peer2.JniManagedPeerState.HasFlag (JniManagedPeerStates.Replaceable));
174+
Assert.AreNotSame (peer1, peer2);
175+
var peeked = valueManager.PeekPeer (peer2.PeerReference);
176+
Assert.AreSame (peer1, peeked);
177+
} finally {
178+
JniObjectReference.Dispose (ref lref);
179+
}
180+
}
181+
162182
[Test]
163183
public void CreateValue ()
164184
{

0 commit comments

Comments
 (0)