Commit 79d9533
authored
[generator] Use GC.KeepAlive for reference type method parameters. (#722)
Context: #719
@brendanzagaeski has been investigating a Xamarin.Android app crash:
JNI DETECTED ERROR IN APPLICATION: use of deleted global reference 0x3d86
from android.view.View crc64720bb2db43a66fe9.FragmentContainer.n_onCreateView(android.view.LayoutInflater, android.view.ViewGroup, android.os.Bundle)
…
at crc64720bb2db43a66fe9.FragmentContainer.n_onCreateView(Native method)
at crc64720bb2db43a66fe9.FragmentContainer.onCreateView(FragmentContainer.java:41)
This had been a head-scratcher, but we had a GREF log, so all should
be clear, right?
09-10 17:56:48.280 10123 10123 I monodroid-gref: +g+ grefc 1141 gwrefc 0 obj-handle 0x79/I -> new-handle 0x3d86/G from thread '(null)'(1)
09-10 17:56:48.294 10123 10123 I monodroid-gref: +w+ grefc 1140 gwrefc 2 obj-handle 0x3d86/G -> new-handle 0x1e3/W from thread 'finalizer'(10123)
09-10 17:56:48.294 10123 10123 I monodroid-gref: -g- grefc 1139 gwrefc 2 handle 0x3d86/G from thread 'finalizer'(10123)
The GREF log *wasn't* immediately clear: sure, the GREF was turned
into a Weak-GREF, and the Weak-GREF was then collected, but none of
this explained *how* were were using this deleted GREF.
(We were at this state of affairs for months: we "know" we're using
a deleted GREF, but we don't know *how* or *why*. It was a very hard
to hit bug.)
Eventually we had a ["that's funny"][0] event: *sure*, the GREF is
deleted, but:
1. Why is it being deleted by the finalizer?
2. …14ms *after construction*?
A [Garbage Collection][1] refresher may be in order, but in short:
1. All `Java.Lang.Object` subclasses are "bridged" objects.
2. During a GC, all "collectable" bridged objects are gathered.
A collectable object is one in which nothing in the managed
GC references the object.
3. Once the GC is complete, all gathered collectable bridged objects
are passed to a "cross references" callback.
The callback is called *outside* the "scope" of a GC; "the world"
is *not* frozen, other threads may be executing.
4. The "cross references" callback is the
`MonoGCBridgeCallbacks::cross_references` field provided provided
to [`mono_gc_register_bridge_callbacks()`][2].
In a Xamarin.Android app, the "cross references" callback will
"toggle" a JNI Global Reference to a JNI Weak Global Reference,
invoke a Java-side GC, and then try to obtain a
JNI Global Reference from the JNI Weak Global Reference.
If a non-`NULL` pointer is returned, the bridged object is kept
alive. If a `NULL` pointer is returned, the bridged object will
be considered dead, and will be added to the Finalization Queue
(as `Java.Lang.Object` has a finalizer).
Thus, it seemed "funny" that within 14ms an instance was created,
GC'd, and determined to be garbage, *especially* when we *knew* that
this instance was being passed to Java, which we expected to retain
the instance. (Yet wasn't…?)
After much banging of heads, and the yeoman's work of creating a
simplified and consistently reproducible test case, we *think* we
know the cause of the crash.
Consider our normal Managed-to-Java marshaling code, e.g.
partial class MaterialButton {
public unsafe MaterialButton (global::Android.Content.Context context)
: base (IntPtr.Zero, JniHandleOwnership.DoNotTransfer)
{
const string __id = "(Landroid/content/Context;)V";
if (((global::Java.Lang.Object) this).Handle != IntPtr.Zero)
return;
try {
JniArgumentValue* __args = stackalloc JniArgumentValue [1];
/* 1 */ __args [0] = new JniArgumentValue ((context == null) ? IntPtr.Zero : ((global::Java.Lang.Object) context).Handle);
/* 2 */ var __r = _members.InstanceMethods.StartCreateInstance (__id, ((object) this).GetType (), __args);
SetHandle (__r.Handle, JniHandleOwnership.TransferLocalRef);
/* 3 */ _members.InstanceMethods.FinishCreateInstance (__id, this, __args);
} finally {
}
}
}
At (1), `context.Handle` is -- a JNI GREF -- is stored into a
`JniArgumentValue* __args` value, and at (3) `__args` is passed into
JNI, which will presumably "Do Something" with that handle.
However, nothing ties `context.Handle` to `context`, so from
(2) onward, `context` *may* be eligible for garbage collection.
See also Chris Brumme's [Lifetime, GC.KeepAlive, handle recycling][3]
blog article. It's about .NET Framework, but the same fundamental
multithreading concepts apply.
The fix is to *ensure* that `context` is kept alive
*for as long as* `context.Handle` will be used, i.e. across the
JNI `_members.InstanceMethods.FinishCreateInstance()` call:
partial class MaterialButton {
public unsafe MaterialButton (global::Android.Content.Context context)
: base (IntPtr.Zero, JniHandleOwnership.DoNotTransfer)
{
const string __id = "(Landroid/content/Context;)V";
if (((global::Java.Lang.Object) this).Handle != IntPtr.Zero)
return;
try {
JniArgumentValue* __args = stackalloc JniArgumentValue [1];
/* 1 */ __args [0] = new JniArgumentValue ((context == null) ? IntPtr.Zero : ((global::Java.Lang.Object) context).Handle);
/* 2 */ var __r = _members.InstanceMethods.StartCreateInstance (__id, ((object) this).GetType (), __args);
SetHandle (__r.Handle, JniHandleOwnership.TransferLocalRef);
/* 3 */ _members.InstanceMethods.FinishCreateInstance (__id, this, __args);
} finally {
/* 4 */ global::System.GC.KeepAlive (context);
}
}
}
This should prevent e.g. `context` from being prematurely GC'd,
which in turn should prevent the `JNI DETECTED ERROR` message.
Update `tools/generator` to emit `GC.KeepAlive()` statements for
every parameter type which isn't a value type (`enum`, `int`, `string`,
etc.). `string` is considered a value type because we always send a
"deep copy" of the string contents, so it won't matter if the `string`
instance is GC'd immediately.
[0]: https://quoteinvestigator.com/2015/03/02/eureka-funny/
[1]: https://docs.microsoft.com/xamarin/android/internals/garbage-collection
[2]: http://docs.go-mono.com/?link=xhtml%3adeploy%2fmono-api-gc.html
[3]: https://docs.microsoft.com/archive/blogs/cbrumme/lifetime-gc-keepalive-handle-recycling1 parent 1a19ec0 commit 79d9533
File tree
25 files changed
+79
-0
lines changed- tests/generator-Tests
- Unit-Tests/CodeGeneratorExpectedResults
- JavaInterop1
- XAJavaInterop1-NRT
- XAJavaInterop1
- expected.ji
- Adapters
- Core_Jar2Xml
- GenericArguments
- NestedTypes
- NormalMethods
- NormalProperties
- ParameterXPath
- StaticProperties
- Streams
- TestInterface
- java.lang.Enum
- tools/generator/Java.Interop.Tools.Generator.CodeGeneration
25 files changed
+79
-0
lines changedLines changed: 2 additions & 0 deletions
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
17 | 17 | | |
18 | 18 | | |
19 | 19 | | |
| 20 | + | |
20 | 21 | | |
21 | 22 | | |
22 | 23 | | |
| |||
38 | 39 | | |
39 | 40 | | |
40 | 41 | | |
| 42 | + | |
41 | 43 | | |
42 | 44 | | |
43 | 45 | | |
Lines changed: 4 additions & 0 deletions
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
27 | 27 | | |
28 | 28 | | |
29 | 29 | | |
| 30 | + | |
30 | 31 | | |
31 | 32 | | |
32 | 33 | | |
| |||
46 | 47 | | |
47 | 48 | | |
48 | 49 | | |
| 50 | + | |
49 | 51 | | |
50 | 52 | | |
51 | 53 | | |
| |||
65 | 67 | | |
66 | 68 | | |
67 | 69 | | |
| 70 | + | |
68 | 71 | | |
69 | 72 | | |
70 | 73 | | |
| |||
84 | 87 | | |
85 | 88 | | |
86 | 89 | | |
| 90 | + | |
87 | 91 | | |
88 | 92 | | |
89 | 93 | | |
| |||
Lines changed: 4 additions & 0 deletions
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
36 | 36 | | |
37 | 37 | | |
38 | 38 | | |
| 39 | + | |
39 | 40 | | |
40 | 41 | | |
41 | 42 | | |
| |||
65 | 66 | | |
66 | 67 | | |
67 | 68 | | |
| 69 | + | |
68 | 70 | | |
69 | 71 | | |
70 | 72 | | |
| |||
94 | 96 | | |
95 | 97 | | |
96 | 98 | | |
| 99 | + | |
97 | 100 | | |
98 | 101 | | |
99 | 102 | | |
| |||
123 | 126 | | |
124 | 127 | | |
125 | 128 | | |
| 129 | + | |
126 | 130 | | |
127 | 131 | | |
128 | 132 | | |
| |||
Lines changed: 2 additions & 0 deletions
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
17 | 17 | | |
18 | 18 | | |
19 | 19 | | |
| 20 | + | |
20 | 21 | | |
21 | 22 | | |
22 | 23 | | |
| |||
38 | 39 | | |
39 | 40 | | |
40 | 41 | | |
| 42 | + | |
41 | 43 | | |
42 | 44 | | |
43 | 45 | | |
Lines changed: 2 additions & 0 deletions
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
17 | 17 | | |
18 | 18 | | |
19 | 19 | | |
| 20 | + | |
20 | 21 | | |
21 | 22 | | |
22 | 23 | | |
| |||
38 | 39 | | |
39 | 40 | | |
40 | 41 | | |
| 42 | + | |
41 | 43 | | |
42 | 44 | | |
43 | 45 | | |
Lines changed: 2 additions & 0 deletions
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
83 | 83 | | |
84 | 84 | | |
85 | 85 | | |
| 86 | + | |
86 | 87 | | |
87 | 88 | | |
88 | 89 | | |
| |||
126 | 127 | | |
127 | 128 | | |
128 | 129 | | |
| 130 | + | |
129 | 131 | | |
130 | 132 | | |
131 | 133 | | |
| |||
Lines changed: 1 addition & 0 deletions
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
110 | 110 | | |
111 | 111 | | |
112 | 112 | | |
| 113 | + | |
113 | 114 | | |
114 | 115 | | |
115 | 116 | | |
| |||
Lines changed: 3 additions & 0 deletions
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
49 | 49 | | |
50 | 50 | | |
51 | 51 | | |
| 52 | + | |
52 | 53 | | |
53 | 54 | | |
54 | 55 | | |
| |||
70 | 71 | | |
71 | 72 | | |
72 | 73 | | |
| 74 | + | |
73 | 75 | | |
74 | 76 | | |
75 | 77 | | |
| |||
102 | 104 | | |
103 | 105 | | |
104 | 106 | | |
| 107 | + | |
105 | 108 | | |
106 | 109 | | |
107 | 110 | | |
| |||
Lines changed: 1 addition & 0 deletions
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
60 | 60 | | |
61 | 61 | | |
62 | 62 | | |
| 63 | + | |
63 | 64 | | |
64 | 65 | | |
65 | 66 | | |
| |||
Lines changed: 3 additions & 0 deletions
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
176 | 176 | | |
177 | 177 | | |
178 | 178 | | |
| 179 | + | |
179 | 180 | | |
180 | 181 | | |
181 | 182 | | |
| |||
206 | 207 | | |
207 | 208 | | |
208 | 209 | | |
| 210 | + | |
209 | 211 | | |
210 | 212 | | |
211 | 213 | | |
| |||
238 | 240 | | |
239 | 241 | | |
240 | 242 | | |
| 243 | + | |
241 | 244 | | |
242 | 245 | | |
243 | 246 | | |
| |||
0 commit comments