-
Notifications
You must be signed in to change notification settings - Fork 3.8k
[runtime] Add support for weak fields. #5972
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
Conversation
ecf62ac to
7e629a2
Compare
kumpera
left a comment
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
There are multiple blockers with this PR:
-
The GC code gets both generational and moving heap assumptions wrong.
-
It's missing boehm support.
-
The metadata code can be potentially slow due to scanning a few big tables upfront. We should measure the memory and time costs of this PR before merging it.
-
There's no AOT caching of that info nor does the patch handles cached AOT metadata.
-
It doesn't handle generic instances, structs, static or thread-local variables. It doesn't fail the type either when finding those unsupported types.
-
The JIT incorrectly generates write barriers to weak field stores. Either that or make use of remset info to scan those old gen objects.
-
The test suite needs to be expanded to include at least this scenarios:
- old gen obj with weak field pointing to young gen object. Have the young gen object get promoted and verify that it remains ok.
- LOS obj with weak field (array of structs with a weak field) pointing to young gen object. Get the young gen object promoted and verify that it remains ok.
- Pass a weak field byref, load/store from it and verify.
- A weak field in a struct or a generic class.
mono/metadata/class.c
Outdated
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Did you check that interfaces_inited, has_finalize_inited, fields_inited and has_failure are all modified with the loader lock held?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
MonoClass has this comment:
/* ALL BITFIELDS SHOULD BE WRITTEN WHILE HOLDING THE LOADER LOCK */
mono/metadata/custom-attrs.c
Outdated
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Missing a mono_error_cleanup.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Will fix.
mono/metadata/class.c
Outdated
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This will recompute the weak layout of base types multiple times and this could be quite inefficient.
Since we compute the layout of the parent type first, we could just check klass->has_weak_fields and bootstrap from that bitmap instead of doing all of this heavy weight lookup.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
How will this interact with AOT cached info that won't call mono_class_layout_fields for cached classes?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
How will this work for generic instances?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
How will this handle a struct with a weak field?
mono/metadata/custom-attrs.c
Outdated
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This is quite inefficient. The member ref table can be as big as 10x of the typedef table.
We should first scan the typeref table for a type in corlib or "Mono.Runtime.Testing" assembly before delving into the memberef table.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Will add that.
mono/metadata/custom-attrs.c
Outdated
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This check is assumes that WeakAttribute will only have one constructor. This must be documented in the type definition otherwise things will magically break for no good reason.
mono/metadata/null-gc.c
Outdated
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Can't null-gc behavior be of just asserting?
mono/sgen/sgen-gchandles.c
Outdated
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This is wrong. An old gen object might have a weak field pointing to an young gen object that got promoted.
mono/sgen/sgen-gchandles.c
Outdated
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This is wrong, the object can move and we must update the value in the gchandle table
mono/sgen/sgen-gchandles.c
Outdated
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This is wrong, since weak fields are not scanned as part of remset processing, we must scan all of those objects.
mono/metadata/class-accessors.c
Outdated
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
We should embed the bitmap at the end of WeakBitmapData and avoid the extra allocation and indirection.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The assumption is that weak fields are not common, so the storage doesn't need to be optimized too much.
mono/sgen/sgen-gchandles.c
Outdated
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
We must check if the object moved and update the field.
| { | ||
| // | ||
| // We use a gc handle to be able to do some processing for these objects at every gc | ||
| // |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I would add a note here that this handle is freed by scan_for_weak.
|
Is this a new feature in .NET, or a mono specific extension? |
|
Currently, its a mono extension intended to be used on ios, similar to the weak references in swift. |
| guint32 field_idx = first_field_idx + (field - p->fields); | ||
| if (MONO_TYPE_IS_REFERENCE (field->type) && mono_assembly_is_weak_field (p->image, field_idx + 1)) { | ||
| has_weak_fields = TRUE; | ||
| mono_trace_message (MONO_TRACE_TYPE, "Field %s:%s at offset %x is weak.", field->parent->name, field->name, field->offset); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Why does it have to keep scanning?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
A class can have multiple weak fields.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
sure but we are looking only for the first one, right?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
We are also printing out the trace message. Since these fields are not common, I don't think continuing the loop is a problem.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
There's still the issue that we're iterating over the parents multiple times.
This info should be cached in class and not recomputed multiple times for the same class.
Plus, this breaks SRE if it has a parent with a Weak field.
| namespace System { | ||
|
|
||
| [AttributeUsage(AttributeTargets.Field)] | ||
| public sealed class WeakAttribute : Attribute |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I still think the name is very common, this can be easily breaking change due to ambiguity. We should rename it to WeakReferenceAttribute
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It might also be useful to add sample into this file how it's intended to be used
|
|
||
| while ((field = mono_class_get_fields (p, &iter))) { | ||
| guint32 field_idx = first_field_idx + (field - p->fields); | ||
| if (MONO_TYPE_IS_REFERENCE (field->type) && mono_assembly_is_weak_field (p->image, field_idx + 1)) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
We should also check for readonly flag
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
why the readonly flag matters for this?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It just feels wrong to me to have Weak on readonly field, the value should not be ever unset
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This PR doesn't include any form of error handling for bad field configurations. It's left for the C# compiler to eventually do it.
A readonly weak ref will work as advertised, only inited in the constructor and eventually zero'd when the object is gone. It's weird but kinda makes sense?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I don't think C# work will be done anytime soon if ever. Maybe I misunderstand but [Weak] readonly Foo f = Other.Value; can be set back to null after initialization and Other.Value as an unique reference is gone which just looks odd to me.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Hey Marek,
This PR is not the right medium to discuss semantics and scope. I support us discussing it all just not here.
|
commit apidiff |
|
@vargaz there are many tests failures could you investigate |
|
build |
|
@vargaz it's still failing with following on all lanes MONO_PATH=D:/j/workspace/x/mcs/class/lib/net_4_x /cygdrive/d/j/workspace/x/tools/pedump/pedump --verify error test-runner.exe |
Fields marked with the newly added [Weak] attribute will have weakref semantics, they will be null-ed out when the object they point to is collected. This is implemented as follows: - We check for fields with the attribute named WeakAttribute. To avoid repeated scans for custom attributes, we scan the customattr table once per image, collecting the field table indexes of weak fields. - We clear the gc bit of these fields so they are not considered gc references. - When an object with weak fields is allocated, we create a gc handle for it with type WEAK_FIELDS. - When processing gc handles, we process handles of type WEAK_FIELDS by checking that the weak fields point to a live object. If not, we set them to null.
… smaller. Fix leaking a MonoError.
…criptor (), only computed klass->has_weak_fields in mono_class_layout_fields ().
…he 'normal' GCHandleType values so it matches the managed definition.
…allback is not set, happens in pedump.
|
@monojenkins build Linux x64 Acceptance Tests |
It was introduced by #5972 Filed bug: https://bugzilla.xamarin.com/show_bug.cgi?id=60973 Reviewed manually by @lewurm.
It was introduced by mono#5972 Filed bug: https://bugzilla.xamarin.com/show_bug.cgi?id=60973 Reviewed manually by @lewurm.
Commit migrated from mono/mono@5bdaef7
It was introduced by mono/mono#5972 Filed bug: https://bugzilla.xamarin.com/show_bug.cgi?id=60973 Reviewed manually by @lewurm. Commit migrated from mono/mono@58618de
Fields marked with the newly added [Weak] attribute will have weakref semantics, they will be null-ed out when
the object they point to is collected.
This is implemented as follows:
To avoid repeated scans for custom attributes, we scan the customattr table once per image,
collecting the field table indexes of weak fields.
the weak fields point to a live object. If not, we set them to null.