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] Prevent premature collection w/ JniInstance* #768

Merged
merged 1 commit into from
Jan 5, 2021

Conversation

jonpryor
Copy link
Member

Context: dotnet/android@e88cfbc

While writing the commit message for xamarin/xamarin-android/@e88cfbcf,
it occurred to me that the same fundamental scenario of:

CallIntoJava (new JavaLangObjectSubclass ().Handle);
// GC collects instance after `.Handle`, before `CallIntoJava()`

could apply to JniPeerMembers.JniInstanceMethods.Invoke*()
invocations:

JniArgumentValue* __args = …;
_members.InstanceMethods.InvokeVirtualObjectMethod (__id, this, __args);
// What prevents `this` from being collected "too soon"?

Address this: update JniPeerMembers.JniInstanceMethods.Invoke*()
so that there is a GC.KeepAlive(self) after accessing
self.PeerReference. This will ensure that self isn't collected
"during" JniEnvironment.InstanceMethods.Call*Method() invocations.

Likewise update JniPeerMembers.JniInstanceFields.Get*Value() and
JniPeerMembers.JniInstanceFields.Set*Value() so that there is a
GC.KeepAlive(self) after the JniEnvironment.InstanceFields.*
invocation.

Context: dotnet/android@e88cfbc

While writing the commit message for dotnet/android@e88cfbcf,
it occurred to me that the same fundamental scenario of:

	CallIntoJava (new JavaLangObjectSubclass ().Handle);
	// GC collects instance after `.Handle`, before `CallIntoJava()`

could apply to `JniPeerMembers.JniInstanceMethods.Invoke*()`
invocations:

	JniArgumentValue* __args = …;
	_members.InstanceMethods.InvokeVirtualObjectMethod (__id, this, __args);
	// What prevents `this` from being collected "too soon"?

Address this: update `JniPeerMembers.JniInstanceMethods.Invoke*()`
so that there is a `GC.KeepAlive(self)` after accessing
`self.PeerReference`.  This will ensure that `self` isn't collected
"during" `JniEnvironment.InstanceMethods.Call*Method()` invocations.

Likewise update `JniPeerMembers.JniInstanceFields.Get*Value()` and
`JniPeerMembers.JniInstanceFields.Set*Value()` so that there is a
`GC.KeepAlive(self)` after the `JniEnvironment.InstanceFields.*`
invocation.
Copy link
Member

@radekdoulik radekdoulik left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM.

The only thing I was wondering about is
<#= returnType.ReturnType != "void" ? "var r = " : "" #>JniEnvironment.InstanceMethods.CallNonvirtual<#= returnType.JniCallType #>Method (self.PeerReference, j.JniPeerType.PeerReference, n, parameters); line. If I understand it correctly, the j.JniPeerType should be kept alive by self being alive. So that should be fine.

@radekdoulik radekdoulik merged commit da73d6a into dotnet:master Jan 5, 2021
@jonpryor jonpryor deleted the jonp-keepalive-this branch January 5, 2021 15:13
@jonpryor
Copy link
Member Author

jonpryor commented Jan 5, 2021

j.JniPeerType will never be collected: https://github.com/xamarin/java.interop/blob/da73d6a5cf5aaa2e50f1f4822bfeaef13d45b87c/src/Java.Interop/Java.Interop/JniPeerMembers.JniInstanceMethods.cs#L27-L28

(The jniPeerType.RegisterWithRuntime() ensure it's a GREF that will survive until the JniRuntime is disposed.)

Thus, j.JniPeerType.PeerReference will always be valid.

jonpryor added a commit that referenced this pull request Jan 5, 2021
Context: dotnet/android@e88cfbc

While writing the commit message for xamarin/xamarin-android/@e88cfbcf,
it occurred to me that the same fundamental scenario of:

	CallIntoJava (new JavaLangObjectSubclass ().Handle);
	// GC collects instance after `.Handle`, before `CallIntoJava()`

could apply to `JniPeerMembers.JniInstanceMethods.Invoke*()`
invocations:

	JniArgumentValue* __args = …;
	_members.InstanceMethods.InvokeVirtualObjectMethod (__id, this, __args);
	// What prevents `this` from being collected "too soon"?

Address this: update `JniPeerMembers.JniInstanceMethods.Invoke*()`
so that there is a `GC.KeepAlive(self)` after accessing
`self.PeerReference`.  This will ensure that `self` isn't collected
"during" `JniEnvironment.InstanceMethods.Call*Method()` invocations.

Likewise update `JniPeerMembers.JniInstanceFields.Get*Value()` and
`JniPeerMembers.JniInstanceFields.Set*Value()` so that there is a
`GC.KeepAlive(self)` after the `JniEnvironment.InstanceFields.*`
invocation.
jonpryor added a commit that referenced this pull request Jan 5, 2021
jonpryor added a commit that referenced this pull request Jan 5, 2021
Context: dotnet/android@e88cfbc

While writing the commit message for xamarin/xamarin-android/@e88cfbcf,
it occurred to me that the same fundamental scenario of:

	CallIntoJava (new JavaLangObjectSubclass ().Handle);
	// GC collects instance after `.Handle`, before `CallIntoJava()`

could apply to `JniPeerMembers.JniInstanceMethods.Invoke*()`
invocations:

	JniArgumentValue* __args = …;
	_members.InstanceMethods.InvokeVirtualObjectMethod (__id, this, __args);
	// What prevents `this` from being collected "too soon"?

Address this: update `JniPeerMembers.JniInstanceMethods.Invoke*()`
so that there is a `GC.KeepAlive(self)` after accessing
`self.PeerReference`.  This will ensure that `self` isn't collected
"during" `JniEnvironment.InstanceMethods.Call*Method()` invocations.

Likewise update `JniPeerMembers.JniInstanceFields.Get*Value()` and
`JniPeerMembers.JniInstanceFields.Set*Value()` so that there is a
`GC.KeepAlive(self)` after the `JniEnvironment.InstanceFields.*`
invocation.
@jpobst jpobst added this to the 11.1 (16.9 / 8.9) milestone Jan 5, 2021
@github-actions github-actions bot locked and limited conversation to collaborators Apr 12, 2024
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

3 participants