#include "metadata/mono-gc.h"
#include "metadata/method-builder.h"
#include "metadata/profiler-private.h"
+#include "metadata/monitor.h"
#include "utils/mono-mmap.h"
#ifdef HAVE_VALGRIND_MEMCHECK_H
void *object;
};
+typedef struct _FinalizeEntryHashTable FinalizeEntryHashTable;
+struct _FinalizeEntryHashTable {
+ FinalizeEntry **table;
+ mword size;
+ int num_registered;
+};
+
typedef struct _DisappearingLink DisappearingLink;
struct _DisappearingLink {
DisappearingLink *next;
void **link;
};
-#define HIDE_POINTER(p) ((gpointer)(~(gulong)(p)))
-#define REVEAL_POINTER(p) HIDE_POINTER ((p))
+typedef struct _DisappearingLinkHashTable DisappearingLinkHashTable;
+struct _DisappearingLinkHashTable {
+ DisappearingLink **table;
+ mword size;
+ int num_links;
+};
+
+enum {
+ GENERATION_NURSERY,
+ GENERATION_OLD,
+ GENERATION_MAX
+};
+
+/*
+ * The link pointer is hidden by negating each bit. We use the lowest
+ * bit of the link (before negation) to store whether it needs
+ * resurrection tracking.
+ */
+#define HIDE_POINTER(p,t) ((gpointer)(~((gulong)(p)|((t)?1:0))))
+#define REVEAL_POINTER(p) ((gpointer)((~(gulong)(p))&~3L))
#define DISLINK_OBJECT(d) (REVEAL_POINTER (*(d)->link))
+#define DISLINK_TRACK(d) ((~(gulong)(*(d)->link)) & 1)
/*
* The finalizable hash has the object as the key, the
* disappearing_link hash, has the link address as key.
*/
-static FinalizeEntry **finalizable_hash = NULL;
+static FinalizeEntryHashTable minor_finalizable_hash;
+static FinalizeEntryHashTable major_finalizable_hash;
/* objects that are ready to be finalized */
static FinalizeEntry *fin_ready_list = NULL;
static FinalizeEntry *critical_fin_list = NULL;
-static DisappearingLink **disappearing_link_hash = NULL;
-static mword disappearing_link_hash_size = 0;
-static mword finalizable_hash_size = 0;
-static int num_registered_finalizers = 0;
+static DisappearingLinkHashTable minor_disappearing_link_hash;
+static DisappearingLinkHashTable major_disappearing_link_hash;
+
static int num_ready_finalizers = 0;
-static int num_disappearing_links = 0;
static int no_finalize = 0;
/* keep each size a multiple of ALLOC_ALIGN */
return FALSE;
}
+static int slot_for_size (size_t size);
+
+static void
+free_pinned_object (PinnedChunk *chunk, char *obj, size_t size)
+{
+ void **p = (void**)obj;
+ int slot = slot_for_size (size);
+
+ g_assert (obj >= (char*)chunk->start_data && obj < ((char*)chunk + chunk->num_pages * FREELIST_PAGESIZE));
+ *p = chunk->free_list [slot];
+ chunk->free_list [slot] = p;
+}
+
enum {
ROOT_TYPE_NORMAL = 0, /* "normal" roots */
ROOT_TYPE_PINNED = 1, /* roots without a GC descriptor */
static void find_pinning_ref_from_thread (char *obj, size_t size);
static void update_current_thread_stack (void *start);
static GCMemSection* alloc_section (size_t size);
-static void finalize_in_range (char *start, char *end);
-static void null_link_in_range (char *start, char *end);
+static void finalize_in_range (char *start, char *end, int generation);
+static void add_or_remove_disappearing_link (MonoObject *obj, void **link, gboolean track,
+ DisappearingLinkHashTable *hash);
+static void null_link_in_range (char *start, char *end, int generation);
+static void null_links_for_domain (MonoDomain *domain, int generation);
static gboolean search_fragment_for_size (size_t size);
static void mark_pinned_from_addresses (PinnedChunk *chunk, void **start, void **end);
static void clear_remsets (void);
static void clear_tlabs (void);
static char *find_tlab_next_from_address (char *addr);
+static void scan_pinned_objects (void (*callback) (PinnedChunk*, char*, size_t, void*), void *callback_data);
static void sweep_pinned_objects (void);
static void scan_from_pinned_objects (char *addr_start, char *addr_end);
static void free_large_object (LOSObject *obj);
static void free_mem_section (GCMemSection *section);
+static void mono_gc_register_disappearing_link (MonoObject *obj, void **link, gboolean track);
+
void describe_ptr (char *ptr);
void check_consistency (void);
char* check_object (char *start);
* This section of code deals with detecting the objects no longer in use
* and reclaiming the memory.
*/
-#if 0
static void __attribute__((noinline))
scan_area (char *start, char *end)
{
type_rlen++;
continue;
} else if (type == DESC_TYPE_VECTOR) { // includes ARRAY, too
- skip_size = (vt->desc >> LOW_TYPE_BITS) & MAX_ELEMENT_SIZE;
- skip_size *= mono_array_length ((MonoArray*)start);
- skip_size += sizeof (MonoArray);
+ skip_size = safe_object_get_size ((MonoObject*)start);
skip_size += (ALLOC_ALIGN - 1);
skip_size &= ~(ALLOC_ALIGN - 1);
OBJ_VECTOR_FOREACH_PTR (vt, start);
type_str, type_rlen, type_vector, type_bitmap, type_lbit, type_complex);*/
}
+static gboolean
+need_remove_object_for_domain (char *start, MonoDomain *domain)
+{
+ GCVTable *vt = (GCVTable*)LOAD_VTABLE (start);
+ if (mono_object_domain (start) == domain) {
+ DEBUG (1, fprintf (gc_debug_file, "Need to cleanup object %p, (%s)\n", start, safe_name (start)));
+ return TRUE;
+ }
+ return FALSE;
+}
+
+static void
+process_object_for_domain_clearing (char *start, MonoDomain *domain)
+{
+ GCVTable *vt = (GCVTable*)LOAD_VTABLE (start);
+ /* The object could be a proxy for an object in the domain
+ we're deleting. */
+ if (mono_class_has_parent (vt->klass, mono_defaults.real_proxy_class)) {
+ MonoObject *server = ((MonoRealProxy*)start)->unwrapped_server;
+
+ /* The server could already have been zeroed out, so
+ we need to check for that, too. */
+ if (server && (!LOAD_VTABLE (server) || mono_object_domain (server) == domain)) {
+ DEBUG (1, fprintf (gc_debug_file, "Cleaning up remote pointer in %p to object %p (%s)\n",
+ start, server, LOAD_VTABLE (server) ? safe_name (server) : "null"));
+ ((MonoRealProxy*)start)->unwrapped_server = NULL;
+ }
+ }
+}
+
static void __attribute__((noinline))
scan_area_for_domain (MonoDomain *domain, char *start, char *end)
{
GCVTable *vt;
size_t skip_size;
- int type, remove;
+ int type;
+ gboolean remove;
mword desc;
while (start < end) {
continue;
}
vt = (GCVTable*)LOAD_VTABLE (start);
- /* handle threads someway (maybe insert the root domain vtable?) */
- if (mono_object_domain (start) == domain && vt->klass != mono_defaults.thread_class) {
- DEBUG (1, fprintf (gc_debug_file, "Need to cleanup object %p, (%s)\n", start, safe_name (start)));
- remove = 1;
- } else {
- remove = 0;
+ process_object_for_domain_clearing (start, domain);
+ remove = need_remove_object_for_domain (start, domain);
+ if (remove && ((MonoObject*)start)->synchronisation) {
+ void **dislink = mono_monitor_get_object_monitor_weak_link ((MonoObject*)start);
+ if (dislink)
+ mono_gc_register_disappearing_link (NULL, dislink, FALSE);
}
desc = vt->desc;
type = desc & 0x7;
start += skip_size;
continue;
} else if (type == DESC_TYPE_VECTOR) { // includes ARRAY, too
- skip_size = (vt->desc >> LOW_TYPE_BITS) & MAX_ELEMENT_SIZE;
- skip_size *= mono_array_length ((MonoArray*)start);
- skip_size += sizeof (MonoArray);
+ skip_size = safe_object_get_size ((MonoObject*)start);
skip_size += (ALLOC_ALIGN - 1);
skip_size &= ~(ALLOC_ALIGN - 1);
if (type == DESC_TYPE_ARRAY) {
}
}
+static void
+clear_domain_process_pinned_object_callback (PinnedChunk *chunk, char *obj, size_t size, MonoDomain *domain)
+{
+ process_object_for_domain_clearing (obj, domain);
+}
+
+static void
+clear_domain_free_pinned_object_callback (PinnedChunk *chunk, char *obj, size_t size, MonoDomain *domain)
+{
+ if (need_remove_object_for_domain (obj, domain))
+ free_pinned_object (chunk, obj, size);
+}
+
/*
* When appdomains are unloaded we can easily remove objects that have finalizers,
* but all the others could still be present in random places on the heap.
mono_gc_clear_domain (MonoDomain * domain)
{
GCMemSection *section;
+ LOSObject *bigobj, *prev;
+ Fragment *frag;
+ int i;
+
LOCK_GC;
+ /* Clear all remaining nursery fragments */
+ if (nursery_clear_policy == CLEAR_AT_TLAB_CREATION) {
+ g_assert (nursery_next <= nursery_frag_real_end);
+ memset (nursery_next, 0, nursery_frag_real_end - nursery_next);
+ for (frag = nursery_fragments; frag; frag = frag->next) {
+ memset (frag->fragment_start, 0, frag->fragment_end - frag->fragment_start);
+ }
+ }
+
for (section = section_list; section; section = section->next) {
scan_area_for_domain (domain, section->data, section->end_data);
}
- /* FIXME: handle big and fixed objects (we remove, don't clear in this case) */
+
+ /* We need two passes over pinned and large objects because
+ freeing such an object gives its memory back to the OS (in
+ the case of large objects) or obliterates its vtable
+ (pinned objects), but we might need to dereference a
+ pointer from an object to another object if the first
+ object is a proxy. */
+ scan_pinned_objects (clear_domain_process_pinned_object_callback, domain);
+ for (bigobj = los_object_list; bigobj; bigobj = bigobj->next)
+ process_object_for_domain_clearing (bigobj->data, domain);
+
+ prev = NULL;
+ for (bigobj = los_object_list; bigobj;) {
+ if (need_remove_object_for_domain (bigobj->data, domain)) {
+ LOSObject *to_free = bigobj;
+ if (prev)
+ prev->next = bigobj->next;
+ else
+ los_object_list = bigobj->next;
+ bigobj = bigobj->next;
+ DEBUG (1, fprintf (gc_debug_file, "Freeing large object %p (%s)\n",
+ bigobj->data, safe_name (bigobj->data)));
+ free_large_object (to_free);
+ continue;
+ }
+ prev = bigobj;
+ bigobj = bigobj->next;
+ }
+ scan_pinned_objects (clear_domain_free_pinned_object_callback, domain);
+
+ for (i = GENERATION_NURSERY; i < GENERATION_MAX; ++i)
+ null_links_for_domain (domain, i);
+
UNLOCK_GC;
}
-#endif
/*
* add_to_global_remset:
return count;
}
+static DisappearingLinkHashTable*
+get_dislink_hash_table (int generation)
+{
+ switch (generation) {
+ case GENERATION_NURSERY: return &minor_disappearing_link_hash;
+ case GENERATION_OLD: return &major_disappearing_link_hash;
+ default: g_assert_not_reached ();
+ }
+}
+
+static FinalizeEntryHashTable*
+get_finalize_entry_hash_table (int generation)
+{
+ switch (generation) {
+ case GENERATION_NURSERY: return &minor_finalizable_hash;
+ case GENERATION_OLD: return &major_finalizable_hash;
+ default: g_assert_not_reached ();
+ }
+}
+
static void
-finish_gray_stack (char *start_addr, char *end_addr)
+finish_gray_stack (char *start_addr, char *end_addr, int generation)
{
TV_DECLARE (atv);
TV_DECLARE (btv);
*/
do {
fin_ready = num_ready_finalizers;
- finalize_in_range (start_addr, end_addr);
+ finalize_in_range (start_addr, end_addr, generation);
+ if (generation == GENERATION_OLD)
+ finalize_in_range (nursery_start, nursery_real_end, GENERATION_NURSERY);
bigo_scanned_num = scan_needed_big_objects (start_addr, end_addr);
/* drain the new stack that might have been created */
* GC a finalized object my lose the monitor because it is cleared before the finalizer is
* called.
*/
- null_link_in_range (start_addr, end_addr);
+ null_link_in_range (start_addr, end_addr, generation);
TV_GETTIME (btv);
DEBUG (2, fprintf (gc_debug_file, "Finalize queue handling scan: %d usecs\n", TV_ELAPSED (atv, btv)));
}
TV_GETTIME (btv);
DEBUG (2, fprintf (gc_debug_file, "Root scan: %d usecs\n", TV_ELAPSED (atv, btv)));
- finish_gray_stack (nursery_start, nursery_next);
+ finish_gray_stack (nursery_start, nursery_next, GENERATION_NURSERY);
/* walk the pin_queue, build up the fragment list of free memory, unmark
* pinned objects as we go, memzero() the empty fragments so they are ready for the
*/
scan_needed_big_objects (heap_start, heap_end);
/* all the objects in the heap */
- finish_gray_stack (heap_start, heap_end);
+ finish_gray_stack (heap_start, heap_end, GENERATION_OLD);
+ null_link_in_range (nursery_start, nursery_real_end, GENERATION_NURSERY);
/* sweep the big objects list */
prevbo = NULL;
}
static void
-sweep_pinned_objects (void)
+scan_pinned_objects (void (*callback) (PinnedChunk*, char*, size_t, void*), void *callback_data)
{
PinnedChunk *chunk;
int i, obj_size;
void *end_chunk;
for (chunk = pinned_chunk_list; chunk; chunk = chunk->next) {
end_chunk = (char*)chunk + chunk->num_pages * FREELIST_PAGESIZE;
- DEBUG (6, fprintf (gc_debug_file, "Sweeping pinned chunk %p (range: %p-%p)\n", chunk, chunk->start_data, end_chunk));
+ DEBUG (6, fprintf (gc_debug_file, "Scanning pinned chunk %p (range: %p-%p)\n", chunk, chunk->start_data, end_chunk));
for (i = 0; i < chunk->num_pages; ++i) {
obj_size = chunk->page_sizes [i];
if (!obj_size)
ptr = (void**)p;
DEBUG (9, fprintf (gc_debug_file, "Considering %p (vtable: %p)\n", ptr, *ptr));
/* if the first word (the vtable) is outside the chunk we have an object */
- if (*ptr && (*ptr < (void*)chunk || *ptr >= end_chunk)) {
- if (object_is_pinned (ptr)) {
- unpin_object (ptr);
- DEBUG (6, fprintf (gc_debug_file, "Unmarked pinned object %p (%s)\n", ptr, safe_name (ptr)));
- } else {
- /* FIXME: add to freelist */
- DEBUG (6, fprintf (gc_debug_file, "Going to free unmarked pinned object %p (%s)\n", ptr, safe_name (ptr)));
- }
- }
+ if (*ptr && (*ptr < (void*)chunk || *ptr >= end_chunk))
+ callback (chunk, (char*)ptr, obj_size, callback_data);
p += obj_size;
}
}
}
static void
-scan_from_pinned_objects (char *addr_start, char *addr_end)
+sweep_pinned_objects_callback (PinnedChunk *chunk, char *ptr, size_t size, void *data)
{
- PinnedChunk *chunk;
- int i, obj_size;
- char *p, *endp;
- void **ptr;
- void *end_chunk;
- for (chunk = pinned_chunk_list; chunk; chunk = chunk->next) {
- end_chunk = (char*)chunk + chunk->num_pages * FREELIST_PAGESIZE;
- DEBUG (6, fprintf (gc_debug_file, "Scanning pinned chunk %p (range: %p-%p)\n", chunk, chunk->start_data, end_chunk));
- for (i = 0; i < chunk->num_pages; ++i) {
- obj_size = chunk->page_sizes [i];
- if (!obj_size)
- continue;
- p = i? (char*)chunk + i * FREELIST_PAGESIZE: chunk->start_data;
- endp = i? p + FREELIST_PAGESIZE: (char*)chunk + FREELIST_PAGESIZE;
- DEBUG (6, fprintf (gc_debug_file, "Page %d (size: %d, range: %p-%p)\n", i, obj_size, p, endp));
- while (p + obj_size <= endp) {
- ptr = (void**)p;
- DEBUG (9, fprintf (gc_debug_file, "Considering %p (vtable: %p)\n", ptr, *ptr));
- /* if the first word (the vtable) is outside the chunk we have an object */
- if (*ptr && (*ptr < (void*)chunk || *ptr >= end_chunk)) {
- DEBUG (6, fprintf (gc_debug_file, "Precise object scan %d of alloc_pinned %p (%s)\n", i, ptr, safe_name (ptr)));
- // FIXME: Put objects without references into separate chunks
- // which do not need to be scanned
- scan_object ((char*)ptr, addr_start, addr_end);
- }
- p += obj_size;
- }
- }
+ if (object_is_pinned (ptr)) {
+ unpin_object (ptr);
+ DEBUG (6, fprintf (gc_debug_file, "Unmarked pinned object %p (%s)\n", ptr, safe_name (ptr)));
+ } else {
+ DEBUG (6, fprintf (gc_debug_file, "Freeing unmarked pinned object %p (%s)\n", ptr, safe_name (ptr)));
+ free_pinned_object (chunk, ptr, size);
}
}
+static void
+sweep_pinned_objects (void)
+{
+ scan_pinned_objects (sweep_pinned_objects_callback, NULL);
+}
+
+static void
+scan_object_callback (PinnedChunk *chunk, char *ptr, size_t size, char **data)
+{
+ DEBUG (6, fprintf (gc_debug_file, "Precise object scan of alloc_pinned %p (%s)\n", ptr, safe_name (ptr)));
+ /* FIXME: Put objects without references into separate chunks
+ which do not need to be scanned */
+ scan_object (ptr, data [0], data [1]);
+}
+
+static void
+scan_from_pinned_objects (char *addr_start, char *addr_end)
+{
+ char *data [2] = { addr_start, addr_end };
+ scan_pinned_objects (scan_object_callback, data);
+}
+
/*
* Find the slot number in the freelist for memory chunks that
* can contain @size objects.
}
}
+/* LOCKING: requires that the GC lock is held */
+static void
+rehash_fin_table (FinalizeEntryHashTable *hash_table)
+{
+ FinalizeEntry **finalizable_hash = hash_table->table;
+ mword finalizable_hash_size = hash_table->size;
+ int i;
+ unsigned int hash;
+ FinalizeEntry **new_hash;
+ FinalizeEntry *entry, *next;
+ int new_size = g_spaced_primes_closest (hash_table->num_registered);
+
+ new_hash = get_internal_mem (new_size * sizeof (FinalizeEntry*));
+ for (i = 0; i < finalizable_hash_size; ++i) {
+ for (entry = finalizable_hash [i]; entry; entry = next) {
+ hash = mono_object_hash (entry->object) % new_size;
+ next = entry->next;
+ entry->next = new_hash [hash];
+ new_hash [hash] = entry;
+ }
+ }
+ free_internal_mem (finalizable_hash);
+ hash_table->table = new_hash;
+ hash_table->size = new_size;
+}
+
+/* LOCKING: requires that the GC lock is held */
+static void
+rehash_fin_table_if_necessary (FinalizeEntryHashTable *hash_table)
+{
+ if (hash_table->num_registered >= hash_table->size * 2)
+ rehash_fin_table (hash_table);
+}
+
+/* LOCKING: requires that the GC lock is held */
static void
-finalize_in_range (char *start, char *end)
+finalize_in_range (char *start, char *end, int generation)
{
+ FinalizeEntryHashTable *hash_table = get_finalize_entry_hash_table (generation);
FinalizeEntry *entry, *prev;
int i;
+ FinalizeEntry **finalizable_hash = hash_table->table;
+ mword finalizable_hash_size = hash_table->size;
+
if (no_finalize)
return;
for (i = 0; i < finalizable_hash_size; ++i) {
prev = NULL;
for (entry = finalizable_hash [i]; entry;) {
if ((char*)entry->object >= start && (char*)entry->object < end && ((char*)entry->object < to_space || (char*)entry->object >= to_space_end)) {
- if (object_is_fin_ready (entry->object)) {
+ gboolean is_fin_ready = object_is_fin_ready (entry->object);
+ char *copy = copy_object (entry->object, start, end);
+ if (is_fin_ready) {
char *from;
FinalizeEntry *next;
/* remove and put in fin_ready_list */
finalizable_hash [i] = entry->next;
next = entry->next;
num_ready_finalizers++;
- num_registered_finalizers--;
+ hash_table->num_registered--;
queue_finalization_entry (entry);
/* Make it survive */
from = entry->object;
- entry->object = copy_object (entry->object, start, end);
- DEBUG (5, fprintf (gc_debug_file, "Queueing object for finalization: %p (%s) (was at %p) (%d/%d)\n", entry->object, safe_name (entry->object), from, num_ready_finalizers, num_registered_finalizers));
+ entry->object = copy;
+ DEBUG (5, fprintf (gc_debug_file, "Queueing object for finalization: %p (%s) (was at %p) (%d/%d)\n", entry->object, safe_name (entry->object), from, num_ready_finalizers, hash_table->num_registered));
entry = next;
continue;
} else {
- /* update pointer */
- DEBUG (5, fprintf (gc_debug_file, "Updating object for finalization: %p (%s)\n", entry->object, safe_name (entry->object)));
- entry->object = copy_object (entry->object, start, end);
+ char *from = entry->object;
+ if (hash_table == &minor_finalizable_hash && !ptr_in_nursery (copy)) {
+ FinalizeEntry *next = entry->next;
+ unsigned int major_hash;
+ /* remove from the list */
+ if (prev)
+ prev->next = entry->next;
+ else
+ finalizable_hash [i] = entry->next;
+ hash_table->num_registered--;
+
+ entry->object = copy;
+
+ /* insert it into the major hash */
+ rehash_fin_table_if_necessary (&major_finalizable_hash);
+ major_hash = mono_object_hash ((MonoObject*) copy) %
+ major_finalizable_hash.size;
+ entry->next = major_finalizable_hash.table [major_hash];
+ major_finalizable_hash.table [major_hash] = entry;
+ major_finalizable_hash.num_registered++;
+
+ DEBUG (5, fprintf (gc_debug_file, "Promoting finalization of object %p (%s) (was at %p) to major table\n", copy, safe_name (copy), from));
+
+ entry = next;
+ continue;
+ } else {
+ /* update pointer */
+ DEBUG (5, fprintf (gc_debug_file, "Updating object for finalization: %p (%s) (was at %p)\n", entry->object, safe_name (entry->object), from));
+ entry->object = copy;
+ }
}
}
prev = entry;
}
}
+/* LOCKING: requires that the GC lock is held */
static void
-null_link_in_range (char *start, char *end)
+null_link_in_range (char *start, char *end, int generation)
{
+ DisappearingLinkHashTable *hash = get_dislink_hash_table (generation);
+ DisappearingLink **disappearing_link_hash = hash->table;
+ int disappearing_link_hash_size = hash->size;
DisappearingLink *entry, *prev;
int i;
+ if (!hash->num_links)
+ return;
for (i = 0; i < disappearing_link_hash_size; ++i) {
prev = NULL;
for (entry = disappearing_link_hash [i]; entry;) {
char *object = DISLINK_OBJECT (entry);
if (object >= start && object < end && (object < to_space || object >= to_space_end)) {
- if (object_is_fin_ready (object)) {
+ gboolean track = DISLINK_TRACK (entry);
+ if (!track && object_is_fin_ready (object)) {
void **p = entry->link;
DisappearingLink *old;
*p = NULL;
old = entry->next;
free_internal_mem (entry);
entry = old;
- num_disappearing_links--;
+ hash->num_links--;
continue;
} else {
- /* update pointer if it's moved
+ char *copy = copy_object (object, start, end);
+
+ /* Update pointer if it's moved. If the object
+ * has been moved out of the nursery, we need to
+ * remove the link from the minor hash table to
+ * the major one.
+ *
* FIXME: what if an object is moved earlier?
*/
- *entry->link = HIDE_POINTER (copy_object (object, start, end));
- DEBUG (5, fprintf (gc_debug_file, "Updated dislink at %p to %p\n", entry->link, DISLINK_OBJECT (entry)));
+
+ if (hash == &minor_disappearing_link_hash && !ptr_in_nursery (copy)) {
+ void **link = entry->link;
+ DisappearingLink *old;
+ /* remove from list */
+ if (prev)
+ prev->next = entry->next;
+ else
+ disappearing_link_hash [i] = entry->next;
+ old = entry->next;
+ free_internal_mem (entry);
+ entry = old;
+ hash->num_links--;
+
+ add_or_remove_disappearing_link ((MonoObject*)copy, link, track,
+ &major_disappearing_link_hash);
+
+ DEBUG (5, fprintf (gc_debug_file, "Upgraded dislink at %p to major because object %p moved to %p\n", link, object, copy));
+
+ continue;
+ } else {
+ /* We set the track resurrection bit to
+ * FALSE if the object is to be finalized
+ * so that the object can be collected in
+ * the next cycle (i.e. after it was
+ * finalized).
+ */
+ *entry->link = HIDE_POINTER (copy,
+ object_is_fin_ready (object) ? FALSE : track);
+ DEBUG (5, fprintf (gc_debug_file, "Updated dislink at %p to %p\n", entry->link, DISLINK_OBJECT (entry)));
+ }
}
}
prev = entry;
}
}
-/**
- * mono_gc_finalizers_for_domain:
- * @domain: the unloading appdomain
- * @out_array: output array
- * @out_size: size of output array
- *
- * Store inside @out_array up to @out_size objects that belong to the unloading
- * appdomain @domain. Returns the number of stored items. Can be called repeteadly
- * until it returns 0.
- * The items are removed from the finalizer data structure, so the caller is supposed
- * to finalize them.
- * @out_array should be on the stack to allow the GC to know the objects are still alive.
- */
-int
-mono_gc_finalizers_for_domain (MonoDomain *domain, MonoObject **out_array, int out_size)
+static const char*
+dislink_table_name (DisappearingLinkHashTable *table)
+{
+ if (table == &minor_disappearing_link_hash)
+ return "minor table";
+ return "major table";
+}
+
+/* LOCKING: requires that the GC lock is held */
+static void
+null_links_for_domain (MonoDomain *domain, int generation)
+{
+ DisappearingLinkHashTable *hash = get_dislink_hash_table (generation);
+ DisappearingLink **disappearing_link_hash = hash->table;
+ int disappearing_link_hash_size = hash->size;
+ DisappearingLink *entry, *prev;
+ int i;
+ for (i = 0; i < disappearing_link_hash_size; ++i) {
+ prev = NULL;
+ for (entry = disappearing_link_hash [i]; entry; ) {
+ char *object = DISLINK_OBJECT (entry);
+ /* FIXME: actually there should be no object
+ left in the domain with a non-null vtable
+ (provided we remove the Thread special
+ case) */
+ if (object && (!((MonoObject*)object)->vtable || mono_object_domain (object) == domain)) {
+ DisappearingLink *next = entry->next;
+
+ if (prev)
+ prev->next = next;
+ else
+ disappearing_link_hash [i] = next;
+
+ if (*(entry->link)) {
+ *(entry->link) = NULL;
+ g_warning ("Disappearing link %p not freed", entry->link);
+ } else {
+ free_internal_mem (entry);
+ }
+
+ entry = next;
+ continue;
+ }
+ prev = entry;
+ entry = entry->next;
+ }
+ }
+}
+
+/* LOCKING: requires that the GC lock is held */
+static int
+finalizers_for_domain (MonoDomain *domain, MonoObject **out_array, int out_size,
+ FinalizeEntryHashTable *hash_table)
{
+ FinalizeEntry **finalizable_hash = hash_table->table;
+ mword finalizable_hash_size = hash_table->size;
FinalizeEntry *entry, *prev;
int i, count;
+
if (no_finalize || !out_size || !out_array)
return 0;
count = 0;
- LOCK_GC;
for (i = 0; i < finalizable_hash_size; ++i) {
prev = NULL;
for (entry = finalizable_hash [i]; entry;) {
else
finalizable_hash [i] = entry->next;
next = entry->next;
- num_registered_finalizers--;
+ hash_table->num_registered--;
out_array [count ++] = entry->object;
- DEBUG (5, fprintf (gc_debug_file, "Collecting object for finalization: %p (%s) (%d/%d)\n", entry->object, safe_name (entry->object), num_ready_finalizers, num_registered_finalizers));
+ DEBUG (5, fprintf (gc_debug_file, "Collecting object for finalization: %p (%s) (%d/%d)\n", entry->object, safe_name (entry->object), num_ready_finalizers, hash_table->num_registered));
entry = next;
- if (count == out_size) {
- UNLOCK_GC;
+ if (count == out_size)
return count;
- }
continue;
}
prev = entry;
entry = entry->next;
}
}
- UNLOCK_GC;
return count;
}
-static void
-rehash_fin_table (void)
+/**
+ * mono_gc_finalizers_for_domain:
+ * @domain: the unloading appdomain
+ * @out_array: output array
+ * @out_size: size of output array
+ *
+ * Store inside @out_array up to @out_size objects that belong to the unloading
+ * appdomain @domain. Returns the number of stored items. Can be called repeteadly
+ * until it returns 0.
+ * The items are removed from the finalizer data structure, so the caller is supposed
+ * to finalize them.
+ * @out_array should be on the stack to allow the GC to know the objects are still alive.
+ */
+int
+mono_gc_finalizers_for_domain (MonoDomain *domain, MonoObject **out_array, int out_size)
{
- int i;
- unsigned int hash;
- FinalizeEntry **new_hash;
- FinalizeEntry *entry, *next;
- int new_size = g_spaced_primes_closest (num_registered_finalizers);
+ int result;
- new_hash = get_internal_mem (new_size * sizeof (FinalizeEntry*));
- for (i = 0; i < finalizable_hash_size; ++i) {
- for (entry = finalizable_hash [i]; entry; entry = next) {
- hash = mono_object_hash (entry->object) % new_size;
- next = entry->next;
- entry->next = new_hash [hash];
- new_hash [hash] = entry;
- }
+ LOCK_GC;
+ result = finalizers_for_domain (domain, out_array, out_size, &minor_finalizable_hash);
+ if (result < out_size) {
+ result += finalizers_for_domain (domain, out_array + result, out_size - result,
+ &major_finalizable_hash);
}
- free_internal_mem (finalizable_hash);
- finalizable_hash = new_hash;
- finalizable_hash_size = new_size;
+ UNLOCK_GC;
+
+ return result;
}
-void
-mono_gc_register_for_finalization (MonoObject *obj, void *user_data)
+static void
+register_for_finalization (MonoObject *obj, void *user_data, FinalizeEntryHashTable *hash_table)
{
+ FinalizeEntry **finalizable_hash;
+ mword finalizable_hash_size;
FinalizeEntry *entry, *prev;
unsigned int hash;
if (no_finalize)
g_assert (user_data == NULL || user_data == mono_gc_run_finalize);
hash = mono_object_hash (obj);
LOCK_GC;
- if (num_registered_finalizers >= finalizable_hash_size * 2)
- rehash_fin_table ();
+ rehash_fin_table_if_necessary (hash_table);
+ finalizable_hash = hash_table->table;
+ finalizable_hash_size = hash_table->size;
hash %= finalizable_hash_size;
prev = NULL;
for (entry = finalizable_hash [hash]; entry; entry = entry->next) {
prev->next = entry->next;
else
finalizable_hash [hash] = entry->next;
- num_registered_finalizers--;
- DEBUG (5, fprintf (gc_debug_file, "Removed finalizer %p for object: %p (%s) (%d)\n", entry, obj, obj->vtable->klass->name, num_registered_finalizers));
+ hash_table->num_registered--;
+ DEBUG (5, fprintf (gc_debug_file, "Removed finalizer %p for object: %p (%s) (%d)\n", entry, obj, obj->vtable->klass->name, hash_table->num_registered));
free_internal_mem (entry);
}
UNLOCK_GC;
entry->object = obj;
entry->next = finalizable_hash [hash];
finalizable_hash [hash] = entry;
- num_registered_finalizers++;
- DEBUG (5, fprintf (gc_debug_file, "Added finalizer %p for object: %p (%s) (%d)\n", entry, obj, obj->vtable->klass->name, num_registered_finalizers));
+ hash_table->num_registered++;
+ DEBUG (5, fprintf (gc_debug_file, "Added finalizer %p for object: %p (%s) (%d)\n", entry, obj, obj->vtable->klass->name, hash_table->num_registered));
UNLOCK_GC;
}
+void
+mono_gc_register_for_finalization (MonoObject *obj, void *user_data)
+{
+ if (ptr_in_nursery (obj))
+ register_for_finalization (obj, user_data, &minor_finalizable_hash);
+ else
+ register_for_finalization (obj, user_data, &minor_finalizable_hash);
+}
+
static void
-rehash_dislink (void)
+rehash_dislink (DisappearingLinkHashTable *hash_table)
{
+ DisappearingLink **disappearing_link_hash = hash_table->table;
+ int disappearing_link_hash_size = hash_table->size;
int i;
unsigned int hash;
DisappearingLink **new_hash;
DisappearingLink *entry, *next;
- int new_size = g_spaced_primes_closest (num_disappearing_links);
+ int new_size = g_spaced_primes_closest (hash_table->num_links);
new_hash = get_internal_mem (new_size * sizeof (DisappearingLink*));
for (i = 0; i < disappearing_link_hash_size; ++i) {
}
}
free_internal_mem (disappearing_link_hash);
- disappearing_link_hash = new_hash;
- disappearing_link_hash_size = new_size;
+ hash_table->table = new_hash;
+ hash_table->size = new_size;
}
+/* LOCKING: assumes the GC lock is held */
static void
-mono_gc_register_disappearing_link (MonoObject *obj, void **link)
+add_or_remove_disappearing_link (MonoObject *obj, void **link, gboolean track,
+ DisappearingLinkHashTable *hash_table)
{
DisappearingLink *entry, *prev;
unsigned int hash;
- LOCK_GC;
+ DisappearingLink **disappearing_link_hash = hash_table->table;
+ int disappearing_link_hash_size = hash_table->size;
- if (num_disappearing_links >= disappearing_link_hash_size * 2)
- rehash_dislink ();
+ if (hash_table->num_links >= disappearing_link_hash_size * 2) {
+ rehash_dislink (hash_table);
+ disappearing_link_hash = hash_table->table;
+ disappearing_link_hash_size = hash_table->size;
+ }
/* FIXME: add check that link is not in the heap */
hash = mono_aligned_addr_hash (link) % disappearing_link_hash_size;
entry = disappearing_link_hash [hash];
prev->next = entry->next;
else
disappearing_link_hash [hash] = entry->next;
- num_disappearing_links--;
- DEBUG (5, fprintf (gc_debug_file, "Removed dislink %p (%d)\n", entry, num_disappearing_links));
+ hash_table->num_links--;
+ DEBUG (5, fprintf (gc_debug_file, "Removed dislink %p (%d) from %s\n", entry, hash_table->num_links, dislink_table_name (hash_table)));
free_internal_mem (entry);
*link = NULL;
} else {
- *link = HIDE_POINTER (obj); /* we allow the change of object */
+ *link = HIDE_POINTER (obj, track); /* we allow the change of object */
}
- UNLOCK_GC;
return;
}
prev = entry;
}
+ if (obj == NULL)
+ return;
entry = get_internal_mem (sizeof (DisappearingLink));
- *link = HIDE_POINTER (obj);
+ *link = HIDE_POINTER (obj, track);
entry->link = link;
entry->next = disappearing_link_hash [hash];
disappearing_link_hash [hash] = entry;
- num_disappearing_links++;
- DEBUG (5, fprintf (gc_debug_file, "Added dislink %p for object: %p (%s) at %p\n", entry, obj, obj->vtable->klass->name, link));
- UNLOCK_GC;
+ hash_table->num_links++;
+ DEBUG (5, fprintf (gc_debug_file, "Added dislink %p for object: %p (%s) at %p to %s\n", entry, obj, obj->vtable->klass->name, link, dislink_table_name (hash_table)));
+}
+
+/* LOCKING: assumes the GC lock is held */
+static void
+mono_gc_register_disappearing_link (MonoObject *obj, void **link, gboolean track)
+{
+ add_or_remove_disappearing_link (NULL, link, FALSE, &minor_disappearing_link_hash);
+ add_or_remove_disappearing_link (NULL, link, FALSE, &major_disappearing_link_hash);
+ if (obj) {
+ if (ptr_in_nursery (obj))
+ add_or_remove_disappearing_link (obj, link, track, &minor_disappearing_link_hash);
+ else
+ add_or_remove_disappearing_link (obj, link, track, &major_disappearing_link_hash);
+ }
}
int
type_rlen++;
continue;
} else if (type == DESC_TYPE_VECTOR) { // includes ARRAY, too
- skip_size = (vt->desc >> LOW_TYPE_BITS) & MAX_ELEMENT_SIZE;
- skip_size *= mono_array_length ((MonoArray*)start);
- skip_size += sizeof (MonoArray);
+ skip_size = safe_object_get_size ((MonoObject*)start);
skip_size += (ALLOC_ALIGN - 1);
skip_size &= ~(ALLOC_ALIGN - 1);
OBJ_VECTOR_FOREACH_PTR (vt, start);
}
void
-mono_gc_weak_link_add (void **link_addr, MonoObject *obj)
+mono_gc_weak_link_add (void **link_addr, MonoObject *obj, gboolean track)
{
- mono_gc_register_disappearing_link (obj, link_addr);
+ LOCK_GC;
+ mono_gc_register_disappearing_link (obj, link_addr, track);
+ UNLOCK_GC;
}
void
mono_gc_weak_link_remove (void **link_addr)
{
- mono_gc_register_disappearing_link (NULL, link_addr);
+ LOCK_GC;
+ mono_gc_register_disappearing_link (NULL, link_addr, FALSE);
+ UNLOCK_GC;
}
MonoObject*
mono_gc_weak_link_get (void **link_addr)
{
- MonoObject *obj = REVEAL_POINTER (*link_addr);
-
- if (obj == HIDE_POINTER (NULL))
+ if (!*link_addr)
return NULL;
- return obj;
+ return (MonoObject*) REVEAL_POINTER (*link_addr);
}
void*