Skip to content

Use GC.KeepAlive() to prevent early collection of method parameters #719

Closed
@jonpryor

Description

@jonpryor

@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 {
            }
        }

Metadata

Metadata

Assignees

Labels

bugComponent does not function as intendedgeneratorIssues binding a Java library (generator, class-parse, etc.)

Type

No type

Projects

No projects

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions