diff --git a/external/api-snapshot b/external/api-snapshot index be0a9a9bc303..8482d12f298a 160000 --- a/external/api-snapshot +++ b/external/api-snapshot @@ -1 +1 @@ -Subproject commit be0a9a9bc303b5c5b225c49b2502582589573d8f +Subproject commit 8482d12f298a01284c2d54921ae21b4487997a54 diff --git a/mcs/class/corlib/System/WeakAttribute.cs b/mcs/class/corlib/System/WeakAttribute.cs new file mode 100644 index 000000000000..72de431d1727 --- /dev/null +++ b/mcs/class/corlib/System/WeakAttribute.cs @@ -0,0 +1,12 @@ +using System; + +#if MOBILE +namespace System { + +[AttributeUsage(AttributeTargets.Field)] +public sealed class WeakAttribute : Attribute +{ +} + +} +#endif diff --git a/mcs/class/corlib/corlib.dll.sources b/mcs/class/corlib/corlib.dll.sources index 6bb7532991a7..fa940530add6 100644 --- a/mcs/class/corlib/corlib.dll.sources +++ b/mcs/class/corlib/corlib.dll.sources @@ -151,6 +151,7 @@ System/UIntPtr.cs System/ValueType.cs System/Variant.cs System/Void.cs +System/WeakAttribute.cs System/WeakReference.cs System/WeakReference_T.cs System/WindowsConsoleDriver.cs diff --git a/mono/metadata/boehm-gc.c b/mono/metadata/boehm-gc.c index 2bc3052ae690..0f3832ce0e15 100644 --- a/mono/metadata/boehm-gc.c +++ b/mono/metadata/boehm-gc.c @@ -2003,6 +2003,13 @@ mono_gchandle_free_domain (MonoDomain *domain) } } + +void +mono_gc_register_obj_with_weak_fields (void *obj) +{ + g_error ("Weak fields not supported by boehm gc"); +} + #else MONO_EMPTY_SOURCE_FILE (boehm_gc); diff --git a/mono/metadata/class-accessors.c b/mono/metadata/class-accessors.c index cb13369b44fc..189cfbba3fb3 100644 --- a/mono/metadata/class-accessors.c +++ b/mono/metadata/class-accessors.c @@ -16,7 +16,8 @@ typedef enum { PROP_PROPERTY_INFO = 5, /* MonoClassPropertyInfo* */ PROP_EVENT_INFO = 6, /* MonoClassEventInfo* */ PROP_FIELD_DEF_VALUES = 7, /* MonoFieldDefaultValue* */ - PROP_DECLSEC_FLAGS = 8 /* guint32 */ + PROP_DECLSEC_FLAGS = 8, /* guint32 */ + PROP_WEAK_BITMAP = 9 } InfrequentDataKind; /* Accessors based on class kind*/ @@ -390,3 +391,31 @@ mono_class_gtd_get_canonical_inst (MonoClass *klass) g_assert (mono_class_is_gtd (klass)); return &((MonoClassGtd*)klass)->canonical_inst; } + +typedef struct { + MonoPropertyBagItem head; + + int nbits; + gsize *bits; +} WeakBitmapData; + +void +mono_class_set_weak_bitmap (MonoClass *klass, int nbits, gsize *bits) +{ + WeakBitmapData *info = mono_class_alloc (klass, sizeof (WeakBitmapData)); + info->nbits = nbits; + info->bits = bits; + + info->head.tag = PROP_WEAK_BITMAP; + mono_property_bag_add (&klass->infrequent_data, info); +} + +gsize* +mono_class_get_weak_bitmap (MonoClass *klass, int *nbits) +{ + WeakBitmapData *prop = mono_property_bag_get (&klass->infrequent_data, PROP_WEAK_BITMAP); + + g_assert (prop); + *nbits = prop->nbits; + return prop->bits; +} diff --git a/mono/metadata/class-internals.h b/mono/metadata/class-internals.h index a789b2089651..b5769cd16617 100644 --- a/mono/metadata/class-internals.h +++ b/mono/metadata/class-internals.h @@ -328,6 +328,7 @@ struct _MonoClass { guint has_finalize_inited : 1; /* has_finalize is initialized */ guint fields_inited : 1; /* setup_fields () has finished */ guint has_failure : 1; /* See mono_class_get_exception_data () for a MonoErrorBoxed with the details */ + guint has_weak_fields : 1; /* class has weak reference fields */ MonoClass *parent; MonoClass *nested_in; @@ -700,6 +701,7 @@ typedef struct MonoCachedClassInfo { guint has_static_refs : 1; guint no_special_static_fields : 1; guint is_generic_container : 1; + guint has_weak_fields : 1; guint32 cctor_token; MonoImage *finalize_image; guint32 finalize_token; @@ -1529,6 +1531,12 @@ mono_class_set_declsec_flags (MonoClass *klass, guint32 value); void mono_class_set_is_com_object (MonoClass *klass); +void +mono_class_set_weak_bitmap (MonoClass *klass, int nbits, gsize *bits); + +gsize* +mono_class_get_weak_bitmap (MonoClass *klass, int *nbits); + /*Now that everything has been defined, let's include the inline functions */ #include diff --git a/mono/metadata/class.c b/mono/metadata/class.c index ce2e1532faf2..4c39099b40ec 100644 --- a/mono/metadata/class.c +++ b/mono/metadata/class.c @@ -40,6 +40,7 @@ #include #include #include +#include #include #include #include @@ -1666,6 +1667,7 @@ init_sizes_with_info (MonoClass *klass, MonoCachedClassInfo *cached_info) klass->has_references = cached_info->has_references; klass->has_static_refs = cached_info->has_static_refs; klass->no_special_static_fields = cached_info->no_special_static_fields; + klass->has_weak_fields = cached_info->has_weak_fields; mono_loader_unlock (); } else { @@ -2204,18 +2206,40 @@ mono_class_layout_fields (MonoClass *klass, int base_instance_size, int packing_ mono_class_set_type_load_failure (klass, "Value type instance size (%d) cannot be zero, negative, or bigger than 1Mb", klass->instance_size); } + // Weak field support + // + // FIXME: + // - generic instances + // - Disallow on structs/static fields/nonref fields + gboolean has_weak_fields = FALSE; + + if (mono_class_has_static_metadata (klass)) { + for (MonoClass *p = klass; p != NULL; p = p->parent) { + gpointer iter = NULL; + guint32 first_field_idx = mono_class_get_first_field_idx (p); + + 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)) { + 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); + } + } + } + } + /* Publish the data */ mono_loader_lock (); if (!klass->rank) klass->sizes.class_size = class_size; klass->has_static_refs = has_static_refs; + klass->has_weak_fields = has_weak_fields; for (i = 0; i < top; ++i) { field = &klass->fields [i]; if (field->type->attrs & FIELD_ATTRIBUTE_STATIC) field->offset = field_offsets [i]; } - mono_memory_barrier (); klass->fields_inited = 1; mono_loader_unlock (); diff --git a/mono/metadata/custom-attrs-internals.h b/mono/metadata/custom-attrs-internals.h index 1f9150c33e36..a848c0b0e376 100644 --- a/mono/metadata/custom-attrs-internals.h +++ b/mono/metadata/custom-attrs-internals.h @@ -16,4 +16,10 @@ typedef gboolean (*MonoAssemblyMetadataCustomAttrIterFunc) (MonoImage *image, gu void mono_assembly_metadata_foreach_custom_attr (MonoAssembly *assembly, MonoAssemblyMetadataCustomAttrIterFunc func, gpointer user_data); +gboolean +mono_assembly_is_weak_field (MonoImage *image, guint32 field_idx); + +void +mono_assembly_init_weak_fields (MonoImage *image); + #endif /* __MONO_METADATA_REFLECTION_CUSTOM_ATTRS_INTERNALS_H__ */ diff --git a/mono/metadata/custom-attrs.c b/mono/metadata/custom-attrs.c index 7c160a848671..e58f0fedfe2f 100644 --- a/mono/metadata/custom-attrs.c +++ b/mono/metadata/custom-attrs.c @@ -2155,3 +2155,181 @@ mono_assembly_metadata_foreach_custom_attr (MonoAssembly *assembly, MonoAssembly stop_iterating = func (image, assembly_token, nspace, name, mtoken, user_data); } } + +static void +init_weak_fields_inner (MonoImage *image, GHashTable *indexes) +{ + MonoTableInfo *tdef; + MonoError error; + MonoClass *klass = NULL; + guint32 memberref_index = -1; + int first_method_idx = -1; + int method_count = -1; + + if (image == mono_get_corlib ()) { + /* Typedef */ + klass = mono_class_from_name_checked (image, "System", "WeakAttribute", &error); + if (!is_ok (&error)) { + mono_error_cleanup (&error); + return; + } + if (!klass) + return; + first_method_idx = mono_class_get_first_method_idx (klass); + method_count = mono_class_get_method_count (klass); + + tdef = &image->tables [MONO_TABLE_CUSTOMATTRIBUTE]; + guint32 parent, field_idx, col, mtoken, idx; + for (int i = 0; i < tdef->rows; ++i) { + parent = mono_metadata_decode_row_col (tdef, i, MONO_CUSTOM_ATTR_PARENT); + if ((parent & MONO_CUSTOM_ATTR_MASK) != MONO_CUSTOM_ATTR_FIELDDEF) + continue; + + col = mono_metadata_decode_row_col (tdef, i, MONO_CUSTOM_ATTR_TYPE); + mtoken = col >> MONO_CUSTOM_ATTR_TYPE_BITS; + /* 1 based index */ + idx = mtoken - 1; + if ((col & MONO_CUSTOM_ATTR_TYPE_MASK) == MONO_CUSTOM_ATTR_TYPE_METHODDEF) { + field_idx = parent >> MONO_CUSTOM_ATTR_BITS; + if (idx >= first_method_idx && idx < first_method_idx + method_count) + g_hash_table_insert (indexes, GUINT_TO_POINTER (field_idx), GUINT_TO_POINTER (1)); + } + } + } else { + /* Memberref pointing to a typeref */ + tdef = &image->tables [MONO_TABLE_MEMBERREF]; + + /* Check whenever the assembly references the WeakAttribute type */ + gboolean found = FALSE; + tdef = &image->tables [MONO_TABLE_TYPEREF]; + for (int i = 0; i < tdef->rows; ++i) { + guint32 string_offset = mono_metadata_decode_row_col (tdef, i, MONO_TYPEREF_NAME); + const char *name = mono_metadata_string_heap (image, string_offset); + if (!strcmp (name, "WeakAttribute")) { + found = TRUE; + break; + } + } + + if (!found) + return; + + /* Find the memberref pointing to a typeref */ + tdef = &image->tables [MONO_TABLE_MEMBERREF]; + for (int i = 0; i < tdef->rows; ++i) { + guint32 cols [MONO_MEMBERREF_SIZE]; + const char *sig; + + mono_metadata_decode_row (tdef, i, cols, MONO_MEMBERREF_SIZE); + sig = mono_metadata_blob_heap (image, cols [MONO_MEMBERREF_SIGNATURE]); + mono_metadata_decode_blob_size (sig, &sig); + + guint32 nindex = cols [MONO_MEMBERREF_CLASS] >> MONO_MEMBERREF_PARENT_BITS; + guint32 class_index = cols [MONO_MEMBERREF_CLASS] & MONO_MEMBERREF_PARENT_MASK; + const char *fname = mono_metadata_string_heap (image, cols [MONO_MEMBERREF_NAME]); + + if (!strcmp (fname, ".ctor") && class_index == MONO_MEMBERREF_PARENT_TYPEREF) { + MonoTableInfo *typeref_table = &image->tables [MONO_TABLE_TYPEREF]; + guint32 cols [MONO_TYPEREF_SIZE]; + + mono_metadata_decode_row (typeref_table, nindex - 1, cols, MONO_TYPEREF_SIZE); + + const char *name = mono_metadata_string_heap (image, cols [MONO_TYPEREF_NAME]); + const char *nspace = mono_metadata_string_heap (image, cols [MONO_TYPEREF_NAMESPACE]); + + if (!strcmp (nspace, "System") && !strcmp (name, "WeakAttribute")) { + MonoClass *klass = mono_class_from_typeref (image, MONO_TOKEN_TYPE_REF | nindex); + g_assert (!strcmp (klass->name, "WeakAttribute")); + /* Allow a testing dll as well since some profiles don't have WeakAttribute */ + if (klass && (klass->image == mono_get_corlib () || strstr (klass->image->name, "Mono.Runtime.Testing"))) { + /* Sanity check that it only has 1 ctor */ + gpointer iter = NULL; + int count = 0; + MonoMethod *method; + while ((method = mono_class_get_methods (klass, &iter))) { + if (!strcmp (method->name, ".ctor")) + count ++; + } + count ++; + memberref_index = i; + break; + } + } + } + } + if (memberref_index == -1) + return; + + tdef = &image->tables [MONO_TABLE_CUSTOMATTRIBUTE]; + guint32 parent, field_idx, col, mtoken, idx; + for (int i = 0; i < tdef->rows; ++i) { + parent = mono_metadata_decode_row_col (tdef, i, MONO_CUSTOM_ATTR_PARENT); + if ((parent & MONO_CUSTOM_ATTR_MASK) != MONO_CUSTOM_ATTR_FIELDDEF) + continue; + + col = mono_metadata_decode_row_col (tdef, i, MONO_CUSTOM_ATTR_TYPE); + mtoken = col >> MONO_CUSTOM_ATTR_TYPE_BITS; + /* 1 based index */ + idx = mtoken - 1; + field_idx = parent >> MONO_CUSTOM_ATTR_BITS; + if ((col & MONO_CUSTOM_ATTR_TYPE_MASK) == MONO_CUSTOM_ATTR_TYPE_MEMBERREF) { + if (idx == memberref_index) + g_hash_table_insert (indexes, GUINT_TO_POINTER (field_idx), GUINT_TO_POINTER (1)); + } + } + } +} + +/* + * mono_assembly_init_weak_fields: + * + * Initialize the image->weak_field_indexes hash. + */ +void +mono_assembly_init_weak_fields (MonoImage *image) +{ + if (image->weak_fields_inited) + return; + + GHashTable *indexes = NULL; + + if (mono_get_runtime_callbacks ()->get_weak_field_indexes) + indexes = mono_get_runtime_callbacks ()->get_weak_field_indexes (image); + if (!indexes) { + indexes = g_hash_table_new (NULL, NULL); + + /* + * To avoid lookups for every field, we scan the customattr table for entries whose + * parent is a field and whose type is WeakAttribute. + */ + init_weak_fields_inner (image, indexes); + } + + mono_image_lock (image); + if (!image->weak_fields_inited) { + image->weak_field_indexes = indexes; + mono_memory_barrier (); + image->weak_fields_inited = TRUE; + } else { + g_hash_table_destroy (indexes); + } + mono_image_unlock (image); +} + +/* + * mono_assembly_is_weak_field: + * + * Return whenever the FIELD table entry with the 1-based index FIELD_IDX has + * a [Weak] attribute. + */ +gboolean +mono_assembly_is_weak_field (MonoImage *image, guint32 field_idx) +{ + if (image->dynamic) + return FALSE; + + mono_assembly_init_weak_fields (image); + + /* The hash is not mutated, no need to lock */ + return g_hash_table_lookup (image->weak_field_indexes, GINT_TO_POINTER (field_idx)) != NULL; +} diff --git a/mono/metadata/gc-internals.h b/mono/metadata/gc-internals.h index d87746dee363..2a19c3049454 100644 --- a/mono/metadata/gc-internals.h +++ b/mono/metadata/gc-internals.h @@ -138,6 +138,8 @@ void* mono_gc_alloc_string (MonoVTable *vtable, size_t size, gint32 len); void* mono_gc_alloc_mature (MonoVTable *vtable, size_t size); MonoGCDescriptor mono_gc_make_descr_for_string (gsize *bitmap, int numbits); +void mono_gc_register_obj_with_weak_fields (void *obj); + void mono_gc_register_for_finalization (MonoObject *obj, void *user_data); void mono_gc_add_memory_pressure (gint64 value); MONO_API int mono_gc_register_root (char *start, size_t size, MonoGCDescriptor descr, MonoGCRootSource source, const char *msg); diff --git a/mono/metadata/image.c b/mono/metadata/image.c index 31f5d237c60e..548e52715e12 100644 --- a/mono/metadata/image.c +++ b/mono/metadata/image.c @@ -2084,6 +2084,7 @@ mono_image_close_except_pools (MonoImage *image) free_hash (image->pinvoke_scope_filenames); free_hash (image->native_func_wrapper_cache); mono_conc_hashtable_destroy (image->typespec_cache); + free_hash (image->weak_field_indexes); mono_wrapper_caches_free (&image->wrapper_caches); diff --git a/mono/metadata/metadata-internals.h b/mono/metadata/metadata-internals.h index 2b2e414f01e2..e849706e19e9 100644 --- a/mono/metadata/metadata-internals.h +++ b/mono/metadata/metadata-internals.h @@ -416,6 +416,10 @@ struct _MonoImage { MonoGenericContainer *anonymous_generic_class_container; MonoGenericContainer *anonymous_generic_method_container; + gboolean weak_fields_inited; + /* Contains 1 based indexes */ + GHashTable *weak_field_indexes; + /* * No other runtime locks must be taken while holding this lock. * It's meant to be used only to mutate and query structures part of this image. diff --git a/mono/metadata/object-internals.h b/mono/metadata/object-internals.h index f5c6ef3ccd19..cb42761d6c20 100644 --- a/mono/metadata/object-internals.h +++ b/mono/metadata/object-internals.h @@ -630,6 +630,7 @@ typedef struct { gpointer (*create_remoting_trampoline) (MonoDomain *domain, MonoMethod *method, MonoRemotingTarget target, MonoError *error); gpointer (*create_delegate_trampoline) (MonoDomain *domain, MonoClass *klass); gpointer (*interp_get_remoting_invoke) (gpointer imethod, MonoError *error); + GHashTable *(*get_weak_field_indexes) (MonoImage *image); } MonoRuntimeCallbacks; typedef gboolean (*MonoInternalStackWalk) (MonoStackFrameInfo *frame, MonoContext *ctx, gpointer data); diff --git a/mono/metadata/object.c b/mono/metadata/object.c index e23a3f9b6e67..5378f08afc0d 100644 --- a/mono/metadata/object.c +++ b/mono/metadata/object.c @@ -40,6 +40,7 @@ #include #include #include +#include #include #include #include @@ -47,6 +48,7 @@ #include #include #include +#include #include "cominterop.h" #include #include @@ -1001,7 +1003,14 @@ ves_icall_string_alloc (int length) return str; } +#define BITMAP_EL_SIZE (sizeof (gsize) * 8) + /* LOCKING: Acquires the loader lock */ +/* + * Sets the following fields in KLASS: + * - gc_desc + * - gc_descr_inited + */ void mono_class_compute_gc_descriptor (MonoClass *klass) { @@ -1034,24 +1043,59 @@ mono_class_compute_gc_descriptor (MonoClass *klass) gc_descr = mono_gc_make_descr_for_array (klass->byval_arg.type == MONO_TYPE_SZARRAY, bitmap, mono_array_element_size (klass) / sizeof (gpointer), mono_array_element_size (klass)); /*printf ("new vt array descriptor: 0x%x for %s.%s\n", class->gc_descr, class->name_space, class->name);*/ - if (bitmap != default_bitmap) - g_free (bitmap); } } else { /*static int count = 0; if (count++ > 58) return;*/ bitmap = compute_class_bitmap (klass, default_bitmap, sizeof (default_bitmap) * 8, 0, &max_set, FALSE); - gc_descr = mono_gc_make_descr_for_object (bitmap, max_set + 1, klass->instance_size); /* if (class->gc_descr == MONO_GC_DESCRIPTOR_NULL) g_print ("disabling typed alloc (%d) for %s.%s\n", max_set, class->name_space, class->name); */ /*printf ("new descriptor: %p 0x%x for %s.%s\n", class->gc_descr, bitmap [0], class->name_space, class->name);*/ - if (bitmap != default_bitmap) - g_free (bitmap); + + if (klass->has_weak_fields) { + gsize *weak_bitmap = NULL; + int weak_bitmap_nbits = 0; + + weak_bitmap = (gsize *)mono_class_alloc0 (klass, klass->instance_size / sizeof (gsize)); + if (mono_class_has_static_metadata (klass)) { + for (MonoClass *p = klass; p != NULL; p = p->parent) { + gpointer iter = NULL; + guint32 first_field_idx = mono_class_get_first_field_idx (p); + MonoClassField *field; + + 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)) { + int pos = field->offset / sizeof (gpointer); + if (pos + 1 > weak_bitmap_nbits) + weak_bitmap_nbits = pos + 1; + weak_bitmap [pos / BITMAP_EL_SIZE] |= ((gsize)1) << (pos % BITMAP_EL_SIZE); + } + } + } + } + + for (int pos = 0; pos < weak_bitmap_nbits; ++pos) { + if (weak_bitmap [pos / BITMAP_EL_SIZE] & ((gsize)1) << (pos % BITMAP_EL_SIZE)) { + /* Clear the normal bitmap so these refs don't keep an object alive */ + bitmap [pos / BITMAP_EL_SIZE] &= ~((gsize)1) << (pos % BITMAP_EL_SIZE); + } + } + + mono_loader_lock (); + mono_class_set_weak_bitmap (klass, weak_bitmap_nbits, weak_bitmap); + mono_loader_unlock (); + } + + gc_descr = mono_gc_make_descr_for_object (bitmap, max_set + 1, klass->instance_size); } + if (bitmap != default_bitmap) + g_free (bitmap); + /* Publish the data */ mono_loader_lock (); klass->gc_descr = gc_descr; @@ -5435,8 +5479,12 @@ mono_object_new_alloc_specific_checked (MonoVTable *vtable, MonoError *error) if (G_UNLIKELY (!o)) mono_error_set_out_of_memory (error, "Could not allocate %i bytes", vtable->klass->instance_size); - else if (G_UNLIKELY (vtable->klass->has_finalize)) - mono_object_register_finalizer (o); + else if (G_UNLIKELY (vtable->klass->has_finalize || vtable->klass->has_weak_fields)) { + if (vtable->klass->has_finalize) + mono_object_register_finalizer (o); + if (vtable->klass->has_weak_fields) + mono_gc_register_obj_with_weak_fields (o); + } return o; } diff --git a/mono/metadata/sgen-mono.c b/mono/metadata/sgen-mono.c index e16f2c869d2f..6ad19b1d76a8 100644 --- a/mono/metadata/sgen-mono.c +++ b/mono/metadata/sgen-mono.c @@ -1501,7 +1501,7 @@ mono_gc_get_managed_allocator (MonoClass *klass, gboolean for_box, gboolean know return NULL; if (known_instance_size && ALIGN_TO (klass->instance_size, SGEN_ALLOC_ALIGN) >= SGEN_MAX_SMALL_OBJ_SIZE) return NULL; - if (mono_class_has_finalizer (klass) || mono_class_is_marshalbyref (klass)) + if (mono_class_has_finalizer (klass) || mono_class_is_marshalbyref (klass) || klass->has_weak_fields) return NULL; if (klass->rank) return NULL; @@ -2643,6 +2643,12 @@ mono_gc_make_descr_for_string (gsize *bitmap, int numbits) return SGEN_DESC_STRING; } +void +mono_gc_register_obj_with_weak_fields (void *obj) +{ + return sgen_register_obj_with_weak_fields (obj); +} + void* mono_gc_get_nursery (int *shift_bits, size_t *size) { @@ -3095,4 +3101,12 @@ mono_gc_is_null (void) return FALSE; } +gsize * +sgen_client_get_weak_bitmap (MonoVTable *vt, int *nbits) +{ + MonoClass *klass = vt->klass; + + return mono_class_get_weak_bitmap (klass, nbits); +} + #endif diff --git a/mono/mini/aot-compiler.c b/mono/mini/aot-compiler.c index e3625b08a900..74740bb5ba44 100644 --- a/mono/mini/aot-compiler.c +++ b/mono/mini/aot-compiler.c @@ -49,6 +49,7 @@ #include #include #include +#include #include #include #include @@ -6543,7 +6544,7 @@ emit_klass_info (MonoAotCompile *acfg, guint32 token) } else { gboolean has_nested = mono_class_get_nested_classes_property (klass) != NULL; encode_value (klass->vtable_size, p, &p); - encode_value ((mono_class_is_gtd (klass) ? (1 << 8) : 0) | (no_special_static << 7) | (klass->has_static_refs << 6) | (klass->has_references << 5) | ((klass->blittable << 4) | (has_nested ? 1 : 0) << 3) | (klass->has_cctor << 2) | (klass->has_finalize << 1) | klass->ghcimpl, p, &p); + encode_value ((klass->has_weak_fields << 9) | (mono_class_is_gtd (klass) ? (1 << 8) : 0) | (no_special_static << 7) | (klass->has_static_refs << 6) | (klass->has_references << 5) | ((klass->blittable << 4) | (has_nested ? 1 : 0) << 3) | (klass->has_cctor << 2) | (klass->has_finalize << 1) | klass->ghcimpl, p, &p); if (klass->has_cctor) encode_method_ref (acfg, mono_class_get_cctor (klass), p, &p); if (klass->has_finalize) @@ -9888,6 +9889,32 @@ emit_image_table (MonoAotCompile *acfg) g_free (buf); } +static void +emit_weak_field_indexes (MonoAotCompile *acfg) +{ + char symbol [128]; + GHashTable *indexes; + GHashTableIter iter; + gpointer key, value; + + /* Emit a table of weak field indexes, since computing these at runtime is expensive */ + sprintf (symbol, "weak_field_indexes"); + emit_section_change (acfg, ".text", 0); + emit_alignment_code (acfg, 8); + emit_info_symbol (acfg, symbol); + + mono_assembly_init_weak_fields (acfg->image); + indexes = acfg->image->weak_field_indexes; + g_assert (indexes); + + emit_int32 (acfg, g_hash_table_size (indexes)); + g_hash_table_iter_init (&iter, indexes); + while (g_hash_table_iter_next (&iter, &key, &value)) { + guint32 index = GPOINTER_TO_UINT (key); + emit_int32 (acfg, index); + } +} + static void emit_got_info (MonoAotCompile *acfg, gboolean llvm) { @@ -10286,6 +10313,7 @@ emit_aot_file_info (MonoAotCompile *acfg, MonoAotFileInfo *info) symbols [sindex ++] = NULL; symbols [sindex ++] = NULL; } + symbols [sindex ++] = "weak_field_indexes"; g_assert (sindex == MONO_AOT_FILE_INFO_NUM_SYMBOLS); @@ -12764,6 +12792,8 @@ mono_compile_assembly (MonoAssembly *ass, guint32 opts, const char *aot_options, emit_image_table (acfg); + emit_weak_field_indexes (acfg); + emit_got (acfg); { diff --git a/mono/mini/aot-runtime.c b/mono/mini/aot-runtime.c index cd442ed7c1a7..baa8be474684 100644 --- a/mono/mini/aot-runtime.c +++ b/mono/mini/aot-runtime.c @@ -2424,6 +2424,7 @@ decode_cached_class_info (MonoAotModule *module, MonoCachedClassInfo *info, guin info->has_static_refs = (flags >> 6) & 0x1; info->no_special_static_fields = (flags >> 7) & 0x1; info->is_generic_container = (flags >> 8) & 0x1; + info->has_weak_fields = (flags >> 9) & 0x1; if (info->has_cctor) { res = decode_method_ref (module, &ref, buf, &buf, &error); @@ -2626,6 +2627,23 @@ mono_aot_get_class_from_name (MonoImage *image, const char *name_space, const ch return TRUE; } +GHashTable * +mono_aot_get_weak_field_indexes (MonoImage *image) +{ + MonoAotModule *amodule = (MonoAotModule *)image->aot_module; + + if (!amodule) + return NULL; + + /* Initialize weak field indexes from the cached copy */ + guint32 *indexes = amodule->info.weak_field_indexes; + int len = indexes [0]; + GHashTable *indexes_hash = g_hash_table_new (NULL, NULL); + for (int i = 0; i < len; ++i) + g_hash_table_insert (indexes_hash, GUINT_TO_POINTER (indexes [i + 1]), GUINT_TO_POINTER (1)); + return indexes_hash; +} + /* Compute the boundaries of the LLVM code for AMODULE. */ static void compute_llvm_code_range (MonoAotModule *amodule, guint8 **code_start, guint8 **code_end) diff --git a/mono/mini/aot-runtime.h b/mono/mini/aot-runtime.h index d593b6df7cb7..8be8862058e9 100644 --- a/mono/mini/aot-runtime.h +++ b/mono/mini/aot-runtime.h @@ -11,7 +11,7 @@ #include "mini.h" /* Version number of the AOT file format */ -#define MONO_AOT_FILE_VERSION 141 +#define MONO_AOT_FILE_VERSION 142 #define MONO_AOT_TRAMP_PAGE_SIZE 16384 @@ -148,7 +148,9 @@ typedef struct MonoAotFileInfo gpointer unbox_trampolines_end; /* Points to a table of unbox trampoline addresses/offsets */ gpointer unbox_trampoline_addresses; -#define MONO_AOT_FILE_INFO_LAST_SYMBOL unbox_trampoline_addresses + /* Points to an array of weak field indexes */ + gpointer weak_field_indexes; +#define MONO_AOT_FILE_INFO_LAST_SYMBOL weak_field_indexes /* Scalars */ /* The index of the first GOT slot used by the PLT */ @@ -234,6 +236,7 @@ void mono_aot_init_llvm_method (gpointer aot_module, guint32 method void mono_aot_init_gshared_method_this (gpointer aot_module, guint32 method_index, MonoObject *this_ins); void mono_aot_init_gshared_method_mrgctx (gpointer aot_module, guint32 method_index, MonoMethodRuntimeGenericContext *rgctx); void mono_aot_init_gshared_method_vtable (gpointer aot_module, guint32 method_index, MonoVTable *vtable); +GHashTable *mono_aot_get_weak_field_indexes (MonoImage *image); /* This is an exported function */ MONO_API void mono_aot_register_module (gpointer *aot_info); diff --git a/mono/mini/mini-runtime.c b/mono/mini/mini-runtime.c index fb44c2975fb1..9ffff0fef9b1 100644 --- a/mono/mini/mini-runtime.c +++ b/mono/mini/mini-runtime.c @@ -3869,6 +3869,7 @@ mini_init (const char *filename, const char *runtime_version) if (mono_use_interpreter) callbacks.interp_get_remoting_invoke = mono_interp_get_remoting_invoke; #endif + callbacks.get_weak_field_indexes = mono_aot_get_weak_field_indexes; mono_install_callbacks (&callbacks); diff --git a/mono/sgen/gc-internal-agnostic.h b/mono/sgen/gc-internal-agnostic.h index f1c887b5754f..1bc27c711c6a 100644 --- a/mono/sgen/gc-internal-agnostic.h +++ b/mono/sgen/gc-internal-agnostic.h @@ -45,12 +45,14 @@ #define MONO_GC_HANDLE_IS_OBJECT_POINTER(slot) (MONO_GC_HANDLE_TAG (slot) == (MONO_GC_HANDLE_OCCUPIED_MASK | MONO_GC_HANDLE_VALID_MASK)) #define MONO_GC_HANDLE_IS_METADATA_POINTER(slot) (MONO_GC_HANDLE_TAG (slot) == MONO_GC_HANDLE_OCCUPIED_MASK) +/* These should match System.Runtime.InteropServices.GCHandleType */ typedef enum { HANDLE_TYPE_MIN = 0, HANDLE_WEAK = HANDLE_TYPE_MIN, HANDLE_WEAK_TRACK, HANDLE_NORMAL, HANDLE_PINNED, + HANDLE_WEAK_FIELDS, HANDLE_TYPE_MAX } GCHandleType; diff --git a/mono/sgen/sgen-client.h b/mono/sgen/sgen-client.h index 4137cacc3c2a..a0045184bd1b 100644 --- a/mono/sgen/sgen-client.h +++ b/mono/sgen/sgen-client.h @@ -223,6 +223,11 @@ gpointer sgen_client_get_provenance (void); */ void sgen_client_describe_invalid_pointer (GCObject *ptr); +/* + * Return the weak bitmap for a class + */ +gsize *sgen_client_get_weak_bitmap (GCVTable vt, int *nbits); + /* * These client binary protocol functions are called from the respective binary protocol * functions. No action is necessary. We suggest implementing them as inline functions in diff --git a/mono/sgen/sgen-gc.c b/mono/sgen/sgen-gc.c index fb3be3507b18..0cfcf2904dfa 100644 --- a/mono/sgen/sgen-gc.c +++ b/mono/sgen/sgen-gc.c @@ -2656,21 +2656,6 @@ report_internal_mem_usage (void) * ###################################################################### */ -/* - * If the object has been forwarded it means it's still referenced from a root. - * If it is pinned it's still alive as well. - * A LOS object is only alive if we have pinned it. - * Return TRUE if @obj is ready to be finalized. - */ -static inline gboolean -sgen_is_object_alive (GCObject *object) -{ - if (ptr_in_nursery (object)) - return sgen_nursery_is_object_alive (object); - - return sgen_major_is_object_alive (object); -} - /* * This function returns true if @object is either alive and belongs to the * current collection - major collections are full heap, so old gen objects diff --git a/mono/sgen/sgen-gc.h b/mono/sgen/sgen-gc.h index 8cdec588b666..4850b3342310 100644 --- a/mono/sgen/sgen-gc.h +++ b/mono/sgen/sgen-gc.h @@ -794,6 +794,8 @@ gboolean sgen_object_is_live (GCObject *obj); void sgen_init_fin_weak_hash (void); +void sgen_register_obj_with_weak_fields (GCObject *obj); + /* FIXME: move the toggleref stuff out of here */ void sgen_mark_togglerefs (char *start, char *end, ScanCopyContext ctx); void sgen_clear_togglerefs (char *start, char *end, ScanCopyContext ctx); @@ -962,6 +964,23 @@ sgen_major_is_object_alive (GCObject *object) return major_collector.is_object_live (object); } + +/* + * If the object has been forwarded it means it's still referenced from a root. + * If it is pinned it's still alive as well. + * A LOS object is only alive if we have pinned it. + * Return TRUE if @obj is ready to be finalized. + */ +static inline gboolean +sgen_is_object_alive (GCObject *object) +{ + if (sgen_ptr_in_nursery (object)) + return sgen_nursery_is_object_alive (object); + + return sgen_major_is_object_alive (object); +} + + /* * This function returns true if @object is either alive or it belongs to the old gen * and we're currently doing a minor collection. diff --git a/mono/sgen/sgen-gchandles.c b/mono/sgen/sgen-gchandles.c index 4a140e53668e..7be066fb2482 100644 --- a/mono/sgen/sgen-gchandles.c +++ b/mono/sgen/sgen-gchandles.c @@ -103,7 +103,8 @@ static HandleData gc_handles [] = { { SGEN_ARRAY_LIST_INIT (NULL, is_slot_set, try_occupy_slot, -1), (HANDLE_WEAK) }, { SGEN_ARRAY_LIST_INIT (NULL, is_slot_set, try_occupy_slot, -1), (HANDLE_WEAK_TRACK) }, { SGEN_ARRAY_LIST_INIT (NULL, is_slot_set, try_occupy_slot, -1), (HANDLE_NORMAL) }, - { SGEN_ARRAY_LIST_INIT (bucket_alloc_callback, is_slot_set, try_occupy_slot, -1), (HANDLE_PINNED) } + { SGEN_ARRAY_LIST_INIT (bucket_alloc_callback, is_slot_set, try_occupy_slot, -1), (HANDLE_PINNED) }, + { SGEN_ARRAY_LIST_INIT (NULL, is_slot_set, try_occupy_slot, -1), (HANDLE_WEAK_FIELDS) }, }; static HandleData * @@ -394,11 +395,52 @@ null_link_if_necessary (gpointer hidden, GCHandleType handle_type, int max_gener return MONO_GC_HANDLE_OBJECT_POINTER (copy, is_weak); } +static gpointer +scan_for_weak (gpointer hidden, GCHandleType handle_type, int max_generation, gpointer user) +{ + const gboolean is_weak = GC_HANDLE_TYPE_IS_WEAK (handle_type); + ScanCopyContext *ctx = (ScanCopyContext *)user; + + if (!MONO_GC_HANDLE_VALID (hidden)) + return hidden; + + GCObject *obj = (GCObject *)MONO_GC_REVEAL_POINTER (hidden, is_weak); + + /* If the object is dead we free the gc handle */ + if (!sgen_is_object_alive (obj)) + return NULL; + + /* Relocate it */ + ctx->ops->copy_or_mark_object (&obj, ctx->queue); + + int nbits; + gsize *weak_bitmap = sgen_client_get_weak_bitmap (SGEN_LOAD_VTABLE (obj), &nbits); + for (int i = 0; i < nbits; ++i) { + if (weak_bitmap [i / (sizeof (gsize) * 8)] & ((gsize)1 << (i % (sizeof (gsize) * 8)))) { + GCObject **addr = (GCObject **)((char*)obj + (i * sizeof (gpointer))); + GCObject *field = *addr; + + /* if the object in the weak field is alive, we relocate it */ + if (field && sgen_is_object_alive (field)) + ctx->ops->copy_or_mark_object (addr, ctx->queue); + else + *addr = NULL; + } + } + + /* Update link if object was moved. */ + return MONO_GC_HANDLE_OBJECT_POINTER (obj, is_weak); +} + /* LOCKING: requires that the GC lock is held */ void sgen_null_link_in_range (int generation, ScanCopyContext ctx, gboolean track) { sgen_gchandle_iterate (track ? HANDLE_WEAK_TRACK : HANDLE_WEAK, generation, null_link_if_necessary, &ctx); + + //we're always called for gen zero. !track means short ref + if (generation == 0 && !track) + sgen_gchandle_iterate (HANDLE_WEAK_FIELDS, generation, scan_for_weak, &ctx); } typedef struct { @@ -435,6 +477,15 @@ sgen_null_links_if (SgenObjectPredicateFunc predicate, void *data, int generatio sgen_gchandle_iterate (track ? HANDLE_WEAK_TRACK : HANDLE_WEAK, generation, null_link_if, &closure); } +void +sgen_register_obj_with_weak_fields (GCObject *obj) +{ + // + // We use a gc handle to be able to do some processing for these objects at every gc + // + alloc_handle (gc_handles_for_type (HANDLE_WEAK_FIELDS), obj, FALSE); +} + void sgen_init_gchandles (void) { diff --git a/mono/tests/Makefile.am b/mono/tests/Makefile.am index ff7ac6eb445a..15cfe188a8a0 100755 --- a/mono/tests/Makefile.am +++ b/mono/tests/Makefile.am @@ -528,8 +528,8 @@ TESTS_CS_SRC= \ bug-58782-capture-and-throw.cs \ recursive-struct-arrays.cs \ bug-59281.cs \ - init_array_with_lazy_type.cs - + init_array_with_lazy_type.cs \ + weak-fields.cs if AMD64 TESTS_CS_SRC += async-exc-compilation.cs finally_guard.cs finally_block_ending_in_dead_bb.cs @@ -2071,5 +2071,13 @@ internalsvisibleto-correctcase-2-sign2048.dll internalsvisibleto-wrongcase-2-sig $(Q) $(MCS_NO_UNSAFE) -out:internalsvisibleto-wrongcase-2-sign2048.dll -target:library -d:WRONG_CASE -d:SIGN2048 internalsvisibleto-library.cs $(Q) $(MCS_NO_UNSAFE) -out:internalsvisibleto-compilertest-sign2048.exe -warn:0 -r:internalsvisibleto-correctcase-2-sign2048.dll -r:internalsvisibleto-wrongcase-2-sign2048.dll -d:SIGN2048 internalsvisibleto-compilertest.cs +EXTRA_DIST += weakattribute.cs + +# Contains copies of types which don't exist in the desktop profile so tests can use them +Mono.Runtime.Testing.dll: weakattribute.cs + $(MCS) -target:library -out:$@ $< + +weak-fields.exe: weak-fields.cs Mono.Runtime.Testing.dll + $(MCS) -r:Mono.Runtime.Testing.dll -r:$(CLASS)/System.dll -r:$(CLASS)/System.Xml.dll -r:$(CLASS)/System.Core.dll -r:TestDriver.dll $(TEST_DRIVER_HARD_KILL_FEATURE) -out:$@ $< CLEANFILES = $(TESTS_CS) $(TESTS_IL) $(TESTS_BENCH) $(TESTS_STRESS) $(TESTSAOT_CS) $(TESTSAOT_IL) $(TESTSAOT_BENCH) $(TESTSAOT_STRESS) *.dll *.stdout *.aotlog *.exe stest.dat LeafAssembly.dll MidAssembly.dll appdomain-marshalbyref-assemblyload2/*.dll diff --git a/mono/tests/weak-fields.cs b/mono/tests/weak-fields.cs new file mode 100644 index 000000000000..1bd8337c09b5 --- /dev/null +++ b/mono/tests/weak-fields.cs @@ -0,0 +1,71 @@ +using System; +using System.Threading; + +[AttributeUsage(AttributeTargets.Field)] +public sealed class Weak2Attribute : Attribute +{ +} + +public class Finalizable { + public int a; + + ~Finalizable () { + Console.WriteLine ("Finalized. {0}", a); + } +} + +public class OneField { + int x; +} +public class Tests +{ + static Finalizable retain; + + [Weak] + public object Obj; + [Weak2] + public object Obj3; + [Weak] + public object Obj2; + [Weak] + public Finalizable Obj4; + + public static int Main (String[] args) { + var t = new Tests (); + var thread = new Thread (delegate () { + t.Obj = new Finalizable (); + t.Obj2 = new Finalizable (); + t.Obj3 = new Finalizable (); + t.Obj4 = retain = new Finalizable (); + retain.a = 0x1029458; + }); + thread.Start (); + thread.Join (); + GC.Collect (0); + GC.Collect (); + GC.WaitForPendingFinalizers (); + GC.WaitForPendingFinalizers (); + if (t.Obj != null) + return 1; + if (t.Obj2 != null) + return 2; + if (t.Obj3 == null) + return 3; + //overflow the nursery, make sure we fill it + for (int i = 0; i < 1000 * 1000 * 10; ++i) + new OneField (); + + if (retain.a != 0x1029458) + return 4; + + retain = null; + GC.Collect (); + GC.WaitForPendingFinalizers (); + GC.WaitForPendingFinalizers (); + if (t.Obj4 != null) + return 5; + + return 0; + } + +} diff --git a/mono/tests/weakattribute.cs b/mono/tests/weakattribute.cs new file mode 100644 index 000000000000..ed1875cb9521 --- /dev/null +++ b/mono/tests/weakattribute.cs @@ -0,0 +1,10 @@ +using System; + +namespace System { + +[AttributeUsage(AttributeTargets.Field)] +public sealed class WeakAttribute : Attribute +{ +} + +}