Skip to content

Commit 2440416

Browse files
authored
[Java.Interop] Add JniIdentityHashCode to ObjectDisposedException (#1276)
Context: dotnet/android#9039 Context: dotnet/android@32495f3 @jonpryor suspects that the `ObjectDisposedException` being thrown within dotnet/android#9039 *may* be due to a GC-related bug. A problem with diagnosing this is tracking object lifetimes: yes, an `Android.Runtime.InputStreamInvoker` is throwing `ObjectDisposedException`, but in local reproductions, there are *multiple* `InputStreamInvoker` instances created! Which one is throwing? A local answer to that was "Update `InputStreamInvoker.Read()` to log `BaseInputStream.JniIdentityHashCode`", which *was* useful, but is not a "scalable" solution. Review all `throw new ObjectDisposedException()` calls within `Java.Interop.dll`, and update all sites which use `IJavaPeerable` to include the `JniIdentityHashCode` value in the exception message. This would result in a message like: System.ObjectDisposedException: Cannot access disposed object with JniIdentityHashCode=0x12345678. Object name: 'Android.Runtime.InputStreamInvoker'. at Java.Interop.JniPeerMembers.AssertSelf(IJavaPeerable ) …
1 parent 2bdf2bc commit 2440416

File tree

6 files changed

+19
-12
lines changed

6 files changed

+19
-12
lines changed

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

+1-1
Original file line numberDiff line numberDiff line change
@@ -140,7 +140,7 @@ protected void SetPeerReference (ref JniObjectReference reference, JniObjectRefe
140140
public void UnregisterFromRuntime ()
141141
{
142142
if (!PeerReference.IsValid)
143-
throw new ObjectDisposedException (GetType ().FullName);
143+
throw JniEnvironment.CreateObjectDisposedException (this);
144144
JniEnvironment.Runtime.ValueManager.RemovePeer (this);
145145
}
146146

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

+1-1
Original file line numberDiff line numberDiff line change
@@ -96,7 +96,7 @@ protected void SetPeerReference (ref JniObjectReference reference, JniObjectRefe
9696
public void UnregisterFromRuntime ()
9797
{
9898
if (!PeerReference.IsValid)
99-
throw new ObjectDisposedException (GetType ().FullName);
99+
throw JniEnvironment.CreateObjectDisposedException (this);
100100
JniEnvironment.Runtime.ValueManager.RemovePeer (this);
101101
}
102102

src/Java.Interop/Java.Interop/JavaPrimitiveArrays.cs

+8-8
Original file line numberDiff line numberDiff line change
@@ -178,7 +178,7 @@ protected override JniArrayElements CreateElements ()
178178
public new unsafe JniBooleanArrayElements GetElements ()
179179
{
180180
if (!PeerReference.IsValid)
181-
throw new ObjectDisposedException (this.GetType ().FullName);
181+
throw JniEnvironment.CreateObjectDisposedException (this);
182182
var elements = JniEnvironment.Arrays.GetBooleanArrayElements (PeerReference, null);
183183
if (elements == null)
184184
throw new InvalidOperationException ("`JniEnvironment.Arrays.GetBooleanArrayElements()` returned NULL!");
@@ -382,7 +382,7 @@ protected override JniArrayElements CreateElements ()
382382
public new unsafe JniSByteArrayElements GetElements ()
383383
{
384384
if (!PeerReference.IsValid)
385-
throw new ObjectDisposedException (this.GetType ().FullName);
385+
throw JniEnvironment.CreateObjectDisposedException (this);
386386
var elements = JniEnvironment.Arrays.GetByteArrayElements (PeerReference, null);
387387
if (elements == null)
388388
throw new InvalidOperationException ("`JniEnvironment.Arrays.GetByteArrayElements()` returned NULL!");
@@ -586,7 +586,7 @@ protected override JniArrayElements CreateElements ()
586586
public new unsafe JniCharArrayElements GetElements ()
587587
{
588588
if (!PeerReference.IsValid)
589-
throw new ObjectDisposedException (this.GetType ().FullName);
589+
throw JniEnvironment.CreateObjectDisposedException (this);
590590
var elements = JniEnvironment.Arrays.GetCharArrayElements (PeerReference, null);
591591
if (elements == null)
592592
throw new InvalidOperationException ("`JniEnvironment.Arrays.GetCharArrayElements()` returned NULL!");
@@ -790,7 +790,7 @@ protected override JniArrayElements CreateElements ()
790790
public new unsafe JniInt16ArrayElements GetElements ()
791791
{
792792
if (!PeerReference.IsValid)
793-
throw new ObjectDisposedException (this.GetType ().FullName);
793+
throw JniEnvironment.CreateObjectDisposedException (this);
794794
var elements = JniEnvironment.Arrays.GetShortArrayElements (PeerReference, null);
795795
if (elements == null)
796796
throw new InvalidOperationException ("`JniEnvironment.Arrays.GetShortArrayElements()` returned NULL!");
@@ -994,7 +994,7 @@ protected override JniArrayElements CreateElements ()
994994
public new unsafe JniInt32ArrayElements GetElements ()
995995
{
996996
if (!PeerReference.IsValid)
997-
throw new ObjectDisposedException (this.GetType ().FullName);
997+
throw JniEnvironment.CreateObjectDisposedException (this);
998998
var elements = JniEnvironment.Arrays.GetIntArrayElements (PeerReference, null);
999999
if (elements == null)
10001000
throw new InvalidOperationException ("`JniEnvironment.Arrays.GetIntArrayElements()` returned NULL!");
@@ -1198,7 +1198,7 @@ protected override JniArrayElements CreateElements ()
11981198
public new unsafe JniInt64ArrayElements GetElements ()
11991199
{
12001200
if (!PeerReference.IsValid)
1201-
throw new ObjectDisposedException (this.GetType ().FullName);
1201+
throw JniEnvironment.CreateObjectDisposedException (this);
12021202
var elements = JniEnvironment.Arrays.GetLongArrayElements (PeerReference, null);
12031203
if (elements == null)
12041204
throw new InvalidOperationException ("`JniEnvironment.Arrays.GetLongArrayElements()` returned NULL!");
@@ -1402,7 +1402,7 @@ protected override JniArrayElements CreateElements ()
14021402
public new unsafe JniSingleArrayElements GetElements ()
14031403
{
14041404
if (!PeerReference.IsValid)
1405-
throw new ObjectDisposedException (this.GetType ().FullName);
1405+
throw JniEnvironment.CreateObjectDisposedException (this);
14061406
var elements = JniEnvironment.Arrays.GetFloatArrayElements (PeerReference, null);
14071407
if (elements == null)
14081408
throw new InvalidOperationException ("`JniEnvironment.Arrays.GetFloatArrayElements()` returned NULL!");
@@ -1606,7 +1606,7 @@ protected override JniArrayElements CreateElements ()
16061606
public new unsafe JniDoubleArrayElements GetElements ()
16071607
{
16081608
if (!PeerReference.IsValid)
1609-
throw new ObjectDisposedException (this.GetType ().FullName);
1609+
throw JniEnvironment.CreateObjectDisposedException (this);
16101610
var elements = JniEnvironment.Arrays.GetDoubleArrayElements (PeerReference, null);
16111611
if (elements == null)
16121612
throw new InvalidOperationException ("`JniEnvironment.Arrays.GetDoubleArrayElements()` returned NULL!");

src/Java.Interop/Java.Interop/JavaPrimitiveArrays.tt

+2-1
Original file line numberDiff line numberDiff line change
@@ -175,7 +175,7 @@ namespace Java.Interop {
175175
public new unsafe Jni<#= info.TypeModifier #>ArrayElements GetElements ()
176176
{
177177
if (!PeerReference.IsValid)
178-
throw new ObjectDisposedException (this.GetType ().FullName);
178+
throw JniEnvironment.CreateObjectDisposedException (this);
179179
var elements = JniEnvironment.Arrays.Get<#= info.JniMarshalType #>ArrayElements (PeerReference, null);
180180
if (elements == null)
181181
throw new InvalidOperationException ("`JniEnvironment.Arrays.Get<#= info.JniMarshalType #>ArrayElements()` returned NULL!");
@@ -280,6 +280,7 @@ namespace Java.Interop {
280280
JavaArray<<#= info.ManagedType #>>.DestroyArgumentState<Java<#= info.TypeModifier #>Array> (value, ref state, synchronize);
281281
}
282282

283+
[RequiresDynamicCode (ExpressionRequiresUnreferencedCode)]
283284
[RequiresUnreferencedCode (ExpressionRequiresUnreferencedCode)]
284285
public override Expression CreateParameterToManagedExpression (JniValueMarshalerContext context, ParameterExpression sourceValue, ParameterAttributes synchronize = 0, Type? targetType = null)
285286
{

src/Java.Interop/Java.Interop/JniEnvironment.cs

+6
Original file line numberDiff line numberDiff line change
@@ -91,6 +91,12 @@ internal static void SetEnvironmentInfo (JniEnvironmentInfo info)
9191
return Runtime.GetExceptionForThrowable (ref e, JniObjectReferenceOptions.CopyAndDispose);
9292
}
9393

94+
internal static Exception CreateObjectDisposedException (IJavaPeerable value)
95+
{
96+
return new ObjectDisposedException (value.GetType ().FullName,
97+
$"Cannot access disposed object with JniIdentityHashCode={value.JniIdentityHashCode}.");
98+
}
99+
94100
internal static void LogCreateLocalRef (JniObjectReference value)
95101
{
96102
if (!value.IsValid)

src/Java.Interop/Java.Interop/JniPeerMembers.cs

+1-1
Original file line numberDiff line numberDiff line change
@@ -150,7 +150,7 @@ internal static void AssertSelf (IJavaPeerable self)
150150

151151
var peer = self.PeerReference;
152152
if (!peer.IsValid)
153-
throw new ObjectDisposedException (self.GetType ().FullName);
153+
throw JniEnvironment.CreateObjectDisposedException (self);
154154

155155
#if FEATURE_JNIOBJECTREFERENCE_SAFEHANDLES
156156
var lref = peer.SafeHandle as JniLocalReference;

0 commit comments

Comments
 (0)