Closed
Description
@brendanzagaeski has diagnosed an issue wherein object references are collected when we think they shouldn't be.
Consider the following generator
-emitted binding code:
public unsafe bool HideSoftInputFromWindow (Android.OS.IBinder? windowToken, [global::Android.Runtime.GeneratedEnum] Android.Views.InputMethods.HideSoftInputFlags flags)
{
const string __id = "hideSoftInputFromWindow.(Landroid/os/IBinder;I)Z";
try {
JniArgumentValue* __args = stackalloc JniArgumentValue [2];
/* 1 */ __args [0] = new JniArgumentValue ((windowToken == null) ? IntPtr.Zero : ((global::Java.Lang.Object) windowToken).Handle);
/* 2 */ __args [1] = new JniArgumentValue ((int) flags);
/* 3 */ var __rm = _members.InstanceMethods.InvokeAbstractBooleanMethod (__id, this, __args);
return __rm;
} finally {
}
}
Depending on "various factors", it is possible that Mono's GC will decide that windowToken
-- used on line (1) -- is eligible for collection on line (2), when it must be kept alive until after line (3) completes execution.
If windowToken
isn't kept alive, then it could be finalized before/during the InvokeAbstractBooleanMethod()
invocation, which could invalidate/change windowToken.Handle
, and cause lots of head scratching, confusion, and app crashes.
The fix? We need to emit a GC.KeepAlive()
for all reference types until after the Invoke*
invocation has completed:
public unsafe bool HideSoftInputFromWindow (Android.OS.IBinder? windowToken, [global::Android.Runtime.GeneratedEnum] Android.Views.InputMethods.HideSoftInputFlags flags)
{
const string __id = "hideSoftInputFromWindow.(Landroid/os/IBinder;I)Z";
try {
JniArgumentValue* __args = stackalloc JniArgumentValue [2];
__args [0] = new JniArgumentValue ((windowToken == null) ? IntPtr.Zero : ((global::Java.Lang.Object) windowToken).Handle);
__args [1] = new JniArgumentValue ((int) flags);
var __rm = _members.InstanceMethods.InvokeAbstractBooleanMethod (__id, this, __args);
/* FIX */ global::System.GC.KeepAlive (windowToken);
return __rm;
} finally {
}
}