Bump to xamarin/Java.Interop/master@007b35b #5022
Merged
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
Fixes: #4989
What happens if you dispose an instance while disposing the instance?
Here,
MyDisposableObject.Dispose(bool)
callsObject.Dispose()
when
Object.Handle
is valid. This "feels" admittedly unusual, butIDisposable.Dispose()
is supposed to be Idempotent: it can becalled multiple times with no ill effects. Shouldn't this be the same?
Unfortunately, it isn't the same; it crashes, hard:
Ouch.
The cause of the crash isn't the "successive"
.Dispose()
invocationswithin
MyTestMethod()
, but rather the nested.Dispose()
invocation within
MyDisposableObject.Dispose()
.Runtime execution is thus:
JavaObject.Dispose()
JniRuntime.JniValueManager.DisposePeer(this)
var h = this.PeerReference
JniRuntime.JniValueManager.DisposePeer(h, this)
JavaObject.Disposed()
MyDisposableObject.Dispose(disposing:true)
JavaObject.Dispose()
// back to (1)?JniRuntime.JniValueManager.DisposePeer(this)
var h = this.PeerReference
// second ref toh
JniRuntime.JniValueManager.DisposePeer(h, this)
, which passesh
to e.g.JniEnvironment.Types.GetJniTypeNameFromInstance()
,thus requiring that
h
be a valid JNI reference, and also callsJniObjectReference.Dispose()
, invalidatingh
.Types.GetJniTypeNameFromInstance()
withthe
h
from (3).The problem appears to be the recursive
Dispose()
invocation on(7), but the actual problem is step (3): by holding a cached/"old"
value of
this.PeerReference
-- and then later using that same valuein
JniRuntime.JniValueManager.DisposePeer()
-- when the nestedJavaObject.Dispose()
invocation continues execution,this.PeerReference
will be invalidated, but the copy of the handlefrom (3) will still be used! This causes the JVM to very loudly abort.
The fix is to defer the "caching" present in (3): instead of storing
the
PeerReference
value "immediately" -- and disposing the samevalue "later" -- don't store the value until after
IJavaPeerable.Disposed()
is called. This gives theDispose(disposing:true)
method a chance to execute beforeretaining any cached references to
PeerReference
-- which may inturn invalidate
PeerReference
! -- thus ensuring that we only attemptto dispose valid JNI handles.
Bump to dotnet/java-interop@007b35b, which contains fixes to
JniRuntime.JniValueManager.DisposePeer()
, and add appropriate tests.