Merge pull request #439 from mono-soc-2012/garyb/iconfix
[mono.git] / mono / metadata / sgen-fin-weak-hash.c
index 62de14c867b251a5659e610f81db1e4e9bf1dfe4..cdea45583f93ca8cc1c527a330457c897350cb26 100644 (file)
@@ -1,11 +1,47 @@
+#define DISLINK_OBJECT(l)      (REVEAL_POINTER (*(void**)(l)))
+#define DISLINK_TRACK(l)       ((~(gulong)(*(void**)(l))) & 1)
+
 /*
  * The finalizable hash has the object as the key, the 
  * disappearing_link hash, has the link address as key.
  *
  * Copyright 2011 Xamarin Inc.
  */
-static SgenHashTable minor_finalizable_hash = SGEN_HASH_TABLE_INIT (INTERNAL_MEM_FIN_TABLE, INTERNAL_MEM_FINALIZE_ENTRY, 0, (SgenHashFunc)mono_object_hash);
-static SgenHashTable major_finalizable_hash = SGEN_HASH_TABLE_INIT (INTERNAL_MEM_FIN_TABLE, INTERNAL_MEM_FINALIZE_ENTRY, 0, (SgenHashFunc)mono_object_hash);
+
+#define TAG_MASK ((mword)0x1)
+
+static inline MonoObject*
+tagged_object_get_object (MonoObject *object)
+{
+       return (MonoObject*)(((mword)object) & ~TAG_MASK);
+}
+
+static inline int
+tagged_object_get_tag (MonoObject *object)
+{
+       return ((mword)object) & TAG_MASK;
+}
+
+static inline MonoObject*
+tagged_object_apply (void *object, int tag_bits)
+{
+       return (MonoObject*)((mword)object | (mword)tag_bits);
+}
+
+static int
+tagged_object_hash (MonoObject *o)
+{
+       return mono_object_hash (tagged_object_get_object (o));
+}
+
+static gboolean
+tagged_object_equals (MonoObject *a, MonoObject *b)
+{
+       return tagged_object_get_object (a) == tagged_object_get_object (b);
+}
+
+static SgenHashTable minor_finalizable_hash = SGEN_HASH_TABLE_INIT (INTERNAL_MEM_FIN_TABLE, INTERNAL_MEM_FINALIZE_ENTRY, 0, (GHashFunc)tagged_object_hash, (GEqualFunc)tagged_object_equals);
+static SgenHashTable major_finalizable_hash = SGEN_HASH_TABLE_INIT (INTERNAL_MEM_FIN_TABLE, INTERNAL_MEM_FINALIZE_ENTRY, 0, (GHashFunc)tagged_object_hash, (GEqualFunc)tagged_object_equals);
 
 static SgenHashTable*
 get_finalize_entry_hash_table (int generation)
@@ -17,6 +53,72 @@ get_finalize_entry_hash_table (int generation)
        }
 }
 
+#define BRIDGE_OBJECT_MARKED 0x1
+
+/* LOCKING: requires that the GC lock is held */
+void
+sgen_mark_bridge_object (MonoObject *obj)
+{
+       SgenHashTable *hash_table = get_finalize_entry_hash_table (ptr_in_nursery (obj) ? GENERATION_NURSERY : GENERATION_OLD);
+
+       sgen_hash_table_set_key (hash_table, obj, tagged_object_apply (obj, BRIDGE_OBJECT_MARKED));
+}
+
+/* LOCKING: requires that the GC lock is held */
+static void
+collect_bridge_objects (CopyOrMarkObjectFunc copy_func, char *start, char *end, int generation, GrayQueue *queue)
+{
+       SgenHashTable *hash_table = get_finalize_entry_hash_table (generation);
+       MonoObject *object;
+       gpointer dummy;
+       char *copy;
+
+       if (no_finalize)
+               return;
+
+       SGEN_HASH_TABLE_FOREACH (hash_table, object, dummy) {
+               int tag = tagged_object_get_tag (object);
+               object = tagged_object_get_object (object);
+
+               /* Bridge code told us to ignore this one */
+               if (tag == BRIDGE_OBJECT_MARKED)
+                       continue;
+
+               /* Object is a bridge object and major heap says it's dead  */
+               if (!((char*)object >= start && (char*)object < end && !major_collector.is_object_live ((char*)object)))
+                       continue;
+
+               /* Nursery says the object is dead. */
+               if (!sgen_gc_is_object_ready_for_finalization (object))
+                       continue;
+
+               if (!sgen_is_bridge_object (object))
+                       continue;
+
+               copy = (char*)object;
+               copy_func ((void**)&copy, queue);
+
+               sgen_bridge_register_finalized_object ((MonoObject*)copy);
+               
+               if (hash_table == &minor_finalizable_hash && !ptr_in_nursery (copy)) {
+                       /* remove from the list */
+                       SGEN_HASH_TABLE_FOREACH_REMOVE (TRUE);
+
+                       /* insert it into the major hash */
+                       sgen_hash_table_replace (&major_finalizable_hash, tagged_object_apply (copy, tag), NULL, NULL);
+
+                       DEBUG (5, fprintf (gc_debug_file, "Promoting finalization of object %p (%s) (was at %p) to major table\n", copy, safe_name (copy), object));
+
+                       continue;
+               } else {
+                       /* update pointer */
+                       DEBUG (5, fprintf (gc_debug_file, "Updating object for finalization: %p (%s) (was at %p)\n", copy, safe_name (copy), object));
+                       SGEN_HASH_TABLE_FOREACH_SET_KEY (tagged_object_apply (copy, tag));
+               }
+       } SGEN_HASH_TABLE_FOREACH_END;
+}
+
+
 /* LOCKING: requires that the GC lock is held */
 static void
 finalize_in_range (CopyOrMarkObjectFunc copy_func, char *start, char *end, int generation, GrayQueue *queue)
@@ -28,26 +130,27 @@ finalize_in_range (CopyOrMarkObjectFunc copy_func, char *start, char *end, int g
        if (no_finalize)
                return;
        SGEN_HASH_TABLE_FOREACH (hash_table, object, dummy) {
+               int tag = tagged_object_get_tag (object);
+               object = tagged_object_get_object (object);
                if ((char*)object >= start && (char*)object < end && !major_collector.is_object_live ((char*)object)) {
-                       gboolean is_fin_ready = object_is_fin_ready (object);
+                       gboolean is_fin_ready = sgen_gc_is_object_ready_for_finalization (object);
                        MonoObject *copy = object;
                        copy_func ((void**)&copy, queue);
                        if (is_fin_ready) {
                                /* remove and put in fin_ready_list */
-                               SGEN_HASH_TABLE_FOREACH_REMOVE;
+                               SGEN_HASH_TABLE_FOREACH_REMOVE (TRUE);
                                num_ready_finalizers++;
                                queue_finalization_entry (copy);
-                               bridge_register_finalized_object (copy);
                                /* Make it survive */
-                               DEBUG (5, fprintf (gc_debug_file, "Queueing object for finalization: %p (%s) (was at %p) (%d/%d)\n", copy, safe_name (copy), object, num_ready_finalizers, mono_sgen_hash_table_num_entries (hash_table)));
+                               DEBUG (5, fprintf (gc_debug_file, "Queueing object for finalization: %p (%s) (was at %p) (%d/%d)\n", copy, safe_name (copy), object, num_ready_finalizers, sgen_hash_table_num_entries (hash_table)));
                                continue;
                        } else {
                                if (hash_table == &minor_finalizable_hash && !ptr_in_nursery (copy)) {
                                        /* remove from the list */
-                                       SGEN_HASH_TABLE_FOREACH_REMOVE;
+                                       SGEN_HASH_TABLE_FOREACH_REMOVE (TRUE);
 
                                        /* insert it into the major hash */
-                                       mono_sgen_hash_table_replace (&major_finalizable_hash, copy, NULL);
+                                       sgen_hash_table_replace (&major_finalizable_hash, tagged_object_apply (copy, tag), NULL, NULL);
 
                                        DEBUG (5, fprintf (gc_debug_file, "Promoting finalization of object %p (%s) (was at %p) to major table\n", copy, safe_name (copy), object));
 
@@ -55,7 +158,7 @@ finalize_in_range (CopyOrMarkObjectFunc copy_func, char *start, char *end, int g
                                } else {
                                        /* update pointer */
                                        DEBUG (5, fprintf (gc_debug_file, "Updating object for finalization: %p (%s) (was at %p)\n", copy, safe_name (copy), object));
-                                       SGEN_HASH_TABLE_FOREACH_SET_KEY (copy);
+                                       SGEN_HASH_TABLE_FOREACH_SET_KEY (tagged_object_apply (copy, tag));
                                }
                        }
                }
@@ -74,10 +177,10 @@ register_for_finalization (MonoObject *obj, void *user_data, int generation)
        g_assert (user_data == NULL || user_data == mono_gc_run_finalize);
 
        if (user_data) {
-               if (mono_sgen_hash_table_replace (hash_table, obj, NULL))
+               if (sgen_hash_table_replace (hash_table, obj, NULL, NULL))
                        DEBUG (5, fprintf (gc_debug_file, "Added finalizer for object: %p (%s) (%d) to %s table\n", obj, obj->vtable->klass->name, hash_table->num_entries, generation_name (generation)));
        } else {
-               if (mono_sgen_hash_table_remove (hash_table, obj, NULL))
+               if (sgen_hash_table_remove (hash_table, obj, NULL))
                        DEBUG (5, fprintf (gc_debug_file, "Removed finalizer for object: %p (%s) (%d)\n", obj, obj->vtable->klass->name, hash_table->num_entries));
        }
 }
@@ -203,11 +306,13 @@ finalizers_for_domain (MonoDomain *domain, MonoObject **out_array, int out_size,
                return 0;
        count = 0;
        SGEN_HASH_TABLE_FOREACH (hash_table, object, dummy) {
+               object = tagged_object_get_object (object);
+
                if (mono_object_domain (object) == domain) {
                        /* remove and put in out_array */
-                       SGEN_HASH_TABLE_FOREACH_REMOVE;
+                       SGEN_HASH_TABLE_FOREACH_REMOVE (TRUE);
                        out_array [count ++] = object;
-                       DEBUG (5, fprintf (gc_debug_file, "Collecting object for finalization: %p (%s) (%d/%d)\n", object, safe_name (object), num_ready_finalizers, mono_sgen_hash_table_num_entries (hash_table)));
+                       DEBUG (5, fprintf (gc_debug_file, "Collecting object for finalization: %p (%s) (%d/%d)\n", object, safe_name (object), num_ready_finalizers, sgen_hash_table_num_entries (hash_table)));
                        if (count == out_size)
                                return count;
                        continue;
@@ -246,10 +351,10 @@ mono_gc_finalizers_for_domain (MonoDomain *domain, MonoObject **out_array, int o
        return result;
 }
 
-static DisappearingLinkHashTable minor_disappearing_link_hash;
-static DisappearingLinkHashTable major_disappearing_link_hash;
+static SgenHashTable minor_disappearing_link_hash = SGEN_HASH_TABLE_INIT (INTERNAL_MEM_DISLINK_TABLE, INTERNAL_MEM_DISLINK, 0, mono_aligned_addr_hash, NULL);
+static SgenHashTable major_disappearing_link_hash = SGEN_HASH_TABLE_INIT (INTERNAL_MEM_DISLINK_TABLE, INTERNAL_MEM_DISLINK, 0, mono_aligned_addr_hash, NULL);
 
-static DisappearingLinkHashTable*
+static SgenHashTable*
 get_dislink_hash_table (int generation)
 {
        switch (generation) {
@@ -259,128 +364,54 @@ get_dislink_hash_table (int generation)
        }
 }
 
-/* LOCKING: assumes the GC lock is held */
-static 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 (hash_table->num_links);
-
-       new_hash = mono_sgen_alloc_internal_dynamic (new_size * sizeof (DisappearingLink*), INTERNAL_MEM_DISLINK_TABLE);
-       for (i = 0; i < disappearing_link_hash_size; ++i) {
-               for (entry = disappearing_link_hash [i]; entry; entry = next) {
-                       hash = mono_aligned_addr_hash (entry->link) % new_size;
-                       next = entry->next;
-                       entry->next = new_hash [hash];
-                       new_hash [hash] = entry;
-               }
-       }
-       mono_sgen_free_internal_dynamic (disappearing_link_hash,
-                       disappearing_link_hash_size * sizeof (DisappearingLink*), INTERNAL_MEM_DISLINK_TABLE);
-       hash_table->table = new_hash;
-       hash_table->size = new_size;
-}
-
 /* LOCKING: assumes the GC lock is held */
 static void
 add_or_remove_disappearing_link (MonoObject *obj, void **link, int generation)
 {
-       DisappearingLinkHashTable *hash_table = get_dislink_hash_table (generation);
-       DisappearingLink *entry, *prev;
-       unsigned int hash;
-       DisappearingLink **disappearing_link_hash = hash_table->table;
-       int disappearing_link_hash_size = hash_table->size;
-
-       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 = NULL;
-       for (; entry; entry = entry->next) {
-               /* link already added */
-               if (link == entry->link) {
-                       /* NULL obj means remove */
-                       if (obj == NULL) {
-                               if (prev)
-                                       prev->next = entry->next;
-                               else
-                                       disappearing_link_hash [hash] = entry->next;
-                               hash_table->num_links--;
-                               DEBUG (5, fprintf (gc_debug_file, "Removed dislink %p (%d) from %s table\n", entry, hash_table->num_links, generation_name (generation)));
-                               mono_sgen_free_internal (entry, INTERNAL_MEM_DISLINK);
-                       }
-                       return;
+       SgenHashTable *hash_table = get_dislink_hash_table (generation);
+
+       if (!obj) {
+               if (sgen_hash_table_remove (hash_table, link, NULL)) {
+                       DEBUG (5, fprintf (gc_debug_file, "Removed dislink %p (%d) from %s table\n",
+                                       link, hash_table->num_entries, generation_name (generation)));
                }
-               prev = entry;
-       }
-       if (obj == NULL)
                return;
-       entry = mono_sgen_alloc_internal (INTERNAL_MEM_DISLINK);
-       entry->link = link;
-       entry->next = disappearing_link_hash [hash];
-       disappearing_link_hash [hash] = entry;
-       hash_table->num_links++;
-       DEBUG (5, fprintf (gc_debug_file, "Added dislink %p for object: %p (%s) at %p to %s table\n", entry, obj, obj->vtable->klass->name, link, generation_name (generation)));
+       }
+
+       sgen_hash_table_replace (hash_table, link, NULL, NULL);
+       DEBUG (5, fprintf (gc_debug_file, "Added dislink for object: %p (%s) at %p to %s table\n",
+                       obj, obj->vtable->klass->name, link, generation_name (generation)));
 }
 
 /* LOCKING: requires that the GC lock is held */
 static void
 null_link_in_range (CopyOrMarkObjectFunc copy_func, char *start, char *end, int generation, gboolean before_finalization, GrayQueue *queue)
 {
-       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;
-                       gboolean track = DISLINK_TRACK (entry);
-
-                       /*
-                        * Tracked references are processed after
-                        * finalization handling whereas standard weak
-                        * references are processed before.  If an
-                        * object is still not marked after finalization
-                        * handling it means that it either doesn't have
-                        * a finalizer or the finalizer has already run,
-                        * so we must null a tracking reference.
-                        */
-                       if (track == before_finalization) {
-                               prev = entry;
-                               entry = entry->next;
-                               continue;
-                       }
+       void **link;
+       gpointer dummy;
+       SgenHashTable *hash = get_dislink_hash_table (generation);
+
+       SGEN_HASH_TABLE_FOREACH (hash, link, dummy) {
+               char *object;
+               gboolean track = DISLINK_TRACK (link);
 
-                       object = DISLINK_OBJECT (entry);
+               /*
+                * Tracked references are processed after
+                * finalization handling whereas standard weak
+                * references are processed before.  If an
+                * object is still not marked after finalization
+                * handling it means that it either doesn't have
+                * a finalizer or the finalizer has already run,
+                * so we must null a tracking reference.
+                */
+               if (track != before_finalization) {
+                       object = DISLINK_OBJECT (link);
 
                        if (object >= start && object < end && !major_collector.is_object_live (object)) {
-                               if (object_is_fin_ready (object)) {
-                                       void **p = entry->link;
-                                       DisappearingLink *old;
-                                       *p = NULL;
-                                       /* remove from list */
-                                       if (prev)
-                                               prev->next = entry->next;
-                                       else
-                                               disappearing_link_hash [i] = entry->next;
-                                       DEBUG (5, fprintf (gc_debug_file, "Dislink nullified at %p to GCed object %p\n", p, object));
-                                       old = entry->next;
-                                       mono_sgen_free_internal (entry, INTERNAL_MEM_DISLINK);
-                                       entry = old;
-                                       hash->num_links--;
+                               if (sgen_gc_is_object_ready_for_finalization (object)) {
+                                       *link = NULL;
+                                       DEBUG (5, fprintf (gc_debug_file, "Dislink nullified at %p to GCed object %p\n", link, object));
+                                       SGEN_HASH_TABLE_FOREACH_REMOVE (TRUE);
                                        continue;
                                } else {
                                        char *copy = object;
@@ -395,17 +426,7 @@ null_link_in_range (CopyOrMarkObjectFunc copy_func, char *start, char *end, int
                                         */
 
                                        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;
-                                               mono_sgen_free_internal (entry, INTERNAL_MEM_DISLINK);
-                                               entry = old;
-                                               hash->num_links--;
+                                               SGEN_HASH_TABLE_FOREACH_REMOVE (TRUE);
 
                                                g_assert (copy);
                                                *link = HIDE_POINTER (copy, track);
@@ -415,52 +436,61 @@ null_link_in_range (CopyOrMarkObjectFunc copy_func, char *start, char *end, int
 
                                                continue;
                                        } else {
-                                               *entry->link = HIDE_POINTER (copy, track);
-                                               DEBUG (5, fprintf (gc_debug_file, "Updated dislink at %p to %p\n", entry->link, DISLINK_OBJECT (entry)));
+                                               *link = HIDE_POINTER (copy, track);
+                                               DEBUG (5, fprintf (gc_debug_file, "Updated dislink at %p to %p\n", link, DISLINK_OBJECT (link)));
                                        }
                                }
                        }
-                       prev = entry;
-                       entry = entry->next;
                }
-       }
+       } SGEN_HASH_TABLE_FOREACH_END;
 }
 
 /* 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);
-                       if (object && !((MonoObject*)object)->vtable) {
-                               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 {
-                                       mono_sgen_free_internal (entry, INTERNAL_MEM_DISLINK);
-                               }
-
-                               entry = next;
-                               continue;
+       void **link;
+       gpointer dummy;
+       SgenHashTable *hash = get_dislink_hash_table (generation);
+       SGEN_HASH_TABLE_FOREACH (hash, link, dummy) {
+               char *object = DISLINK_OBJECT (link);
+               if (object && !((MonoObject*)object)->vtable) {
+                       gboolean free = TRUE;
+
+                       if (*link) {
+                               *link = NULL;
+                               free = FALSE;
+                               /*
+                                * This can happen if finalizers are not ran, i.e. Environment.Exit ()
+                                * is called from finalizer like in finalizer-abort.cs.
+                                */
+                               DEBUG (5, fprintf (gc_debug_file, "Disappearing link %p not freed", link));
                        }
-                       prev = entry;
-                       entry = entry->next;
+
+                       SGEN_HASH_TABLE_FOREACH_REMOVE (free);
+
+                       continue;
                }
-       }
+       } SGEN_HASH_TABLE_FOREACH_END;
+}
+
+static void
+remove_finalizers_for_domain (MonoDomain *domain, int generation)
+{
+       SgenHashTable *hash_table = get_finalize_entry_hash_table (generation);
+       MonoObject *object;
+       gpointer dummy;
+
+       SGEN_HASH_TABLE_FOREACH (hash_table, object, dummy) {
+               object = tagged_object_get_object (object);
+
+               if (mono_object_domain (object) == domain) {
+                       DEBUG (5, fprintf (gc_debug_file, "Unregistering finalizer for object: %p (%s)\n", object, safe_name (object)));
+
+                       SGEN_HASH_TABLE_FOREACH_REMOVE (TRUE);
+                       continue;
+               }
+       } SGEN_HASH_TABLE_FOREACH_END;  
 }
 
 /* LOCKING: requires that the GC lock is held */