2009-04-28 Rodrigo Kumpera <rkumpera@novell.com>
[mono.git] / mono / metadata / sgen-gc.c
index bf4ef445b6e51a40ff61e6994ddd5121299098df..3607182b8366f0637c4eaf21e098707866d401c2 100644 (file)
@@ -99,7 +99,7 @@
     Multi-dim arrays have the same issue for rank == 1 for the bounds data.
  *) implement a card table as the write barrier instead of remembered sets?
  *) some sort of blacklist support?
- *) fin_ready_list is part of the root set, too
+ *) fin_ready_list and critical_fin_list are part of the root set, too
  *) consider lowering the large object min size to 16/32KB or so and benchmark
  *) once mark-compact is implemented we could still keep the
     copying collector for the old generation and use it if we think
@@ -528,13 +528,25 @@ typedef struct _FinalizeEntry FinalizeEntry;
 struct _FinalizeEntry {
        FinalizeEntry *next;
        void *object;
-       void *data; /* can be a disappearing link or the data for the finalizer */
-       /* Note we could use just one pointer if we don't support multiple callbacks
-        * for finalizers and per-finalizer data and if we store the obj pointers
-        * in the link like libgc does
-        */
 };
 
+typedef struct _DisappearingLink DisappearingLink;
+struct _DisappearingLink {
+       DisappearingLink *next;
+       void **link;
+};
+
+/*
+ * 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.
@@ -542,8 +554,8 @@ struct _FinalizeEntry {
 static FinalizeEntry **finalizable_hash = NULL;
 /* objects that are ready to be finalized */
 static FinalizeEntry *fin_ready_list = NULL;
-/* disappearing links use the same structure but a different list */
-static FinalizeEntry **disappearing_link_hash = 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;
 
@@ -662,6 +674,9 @@ static GCMemSection *to_space_section = NULL;
 /* objects bigger then this go into the large object space */
 #define MAX_SMALL_OBJ_SIZE 0xffff
 
+/* Functions supplied by the runtime to be called by the GC */
+static MonoGCCallbacks gc_callbacks;
+
 /*
  * ######################################################################
  * ########  Macros and function declarations.
@@ -693,7 +708,7 @@ static G_GNUC_UNUSED void  report_internal_mem_usage (void);
 
 static int stop_world (void);
 static int restart_world (void);
-static void pin_thread_data (void *start_nursery, void *end_nursery);
+static void scan_thread_data (void *start_nursery, void *end_nursery, gboolean precise);
 static void scan_from_remsets (void *start_nursery, void *end_nursery);
 static void find_pinning_ref_from_thread (char *obj, size_t size);
 static void update_current_thread_stack (void *start);
@@ -861,7 +876,7 @@ mono_gc_make_descr_for_object (gsize *bitmap, int numbits, size_t obj_size)
        stored_size += ALLOC_ALIGN - 1;
        stored_size &= ~(ALLOC_ALIGN - 1);
        for (i = 0; i < numbits; ++i) {
-               if (bitmap [i / GC_BITS_PER_WORD] & (1 << (i % GC_BITS_PER_WORD))) {
+               if (bitmap [i / GC_BITS_PER_WORD] & ((gsize)1 << (i % GC_BITS_PER_WORD))) {
                        if (first_set < 0)
                                first_set = i;
                        last_set = i;
@@ -907,7 +922,7 @@ mono_gc_make_descr_for_array (int vector, gsize *elem_bitmap, int numbits, size_
        int first_set = -1, num_set = 0, last_set = -1, i;
        mword desc = vector? DESC_TYPE_VECTOR: DESC_TYPE_ARRAY;
        for (i = 0; i < numbits; ++i) {
-               if (elem_bitmap [i / GC_BITS_PER_WORD] & (1 << (i % GC_BITS_PER_WORD))) {
+               if (elem_bitmap [i / GC_BITS_PER_WORD] & ((gsize)1 << (i % GC_BITS_PER_WORD))) {
                        if (first_set < 0)
                                first_set = i;
                        last_set = i;
@@ -935,6 +950,41 @@ mono_gc_make_descr_for_array (int vector, gsize *elem_bitmap, int numbits, size_
        return (void*) desc;
 }
 
+/* Return the bitmap encoded by a descriptor */
+gsize*
+mono_gc_get_bitmap_for_descr (void *descr, int *numbits)
+{
+       mword d = (mword)descr;
+       gsize *bitmap;
+
+       switch (d & 0x7) {
+       case DESC_TYPE_RUN_LENGTH: {            
+               int first_set = (d >> 16) & 0xff;
+               int num_set = (d >> 16) & 0xff;
+               int i;
+
+               bitmap = g_new0 (gsize, (first_set + num_set + 7) / 8);
+
+               for (i = first_set; i < first_set + num_set; ++i)
+                       bitmap [i / GC_BITS_PER_WORD] |= ((gsize)1 << (i % GC_BITS_PER_WORD));
+
+               *numbits = first_set + num_set;
+
+               return bitmap;
+       }
+       case DESC_TYPE_SMALL_BITMAP:
+               bitmap = g_new0 (gsize, 1);
+
+               bitmap [0] = (d >> SMALL_BITMAP_SHIFT) << OBJECT_HEADER_WORDS;
+
+           *numbits = GC_BITS_PER_WORD;
+               
+               return bitmap;
+       default:
+               g_assert_not_reached ();
+       }
+}
+
 /* helper macros to scan and traverse objects, macros because we resue them in many functions */
 #define STRING_SIZE(size,str) do {     \
                (size) = sizeof (MonoString) + 2 * (mono_string_length ((MonoString*)(str)) + 1);       \
@@ -1361,7 +1411,7 @@ add_to_global_remset (gpointer ptr, gboolean root)
        global_remset = rs;
        if (root) {
                *(global_remset->store_next++) = (mword)ptr | REMSET_OTHER;
-               *(global_remset->store_next++) = (mword)REMSET_LOCATION;
+               *(global_remset->store_next++) = (mword)REMSET_ROOT_LOCATION;
        } else {
                *(global_remset->store_next++) = (mword)ptr;
        }
@@ -1945,7 +1995,7 @@ pin_from_roots (void *start_nursery, void *end_nursery)
         * *) the _last_ managed stack frame
         * *) pointers slots in managed frames
         */
-       pin_thread_data (start_nursery, end_nursery);
+       scan_thread_data (start_nursery, end_nursery, FALSE);
 }
 
 /* Copy function called from user defined mark functions */
@@ -2098,6 +2148,18 @@ alloc_nursery (void)
        /* FIXME: frag here is lost */
 }
 
+static void
+scan_finalizer_entries (FinalizeEntry *list, char *start, char *end) {
+       FinalizeEntry *fin;
+
+       for (fin = list; fin; fin = fin->next) {
+               if (!fin->object)
+                       continue;
+               DEBUG (5, fprintf (gc_debug_file, "Scan of fin ready object: %p (%s)\n", fin->object, safe_name (fin->object)));
+               fin->object = copy_object (fin->object, start, end);
+       }
+}
+
 /*
  * Update roots in the old generation. Since we currently don't have the
  * info from the write barriers, we just scan all the objects.
@@ -2106,7 +2168,6 @@ static G_GNUC_UNUSED void
 scan_old_generation (char *start, char* end)
 {
        GCMemSection *section;
-       FinalizeEntry *fin;
        LOSObject *big_object;
        char *p;
        
@@ -2131,10 +2192,8 @@ scan_old_generation (char *start, char* end)
                scan_object (big_object->data, start, end);
        }
        /* scan the list of objects ready for finalization */
-       for (fin = fin_ready_list; fin; fin = fin->next) {
-               DEBUG (5, fprintf (gc_debug_file, "Scan of fin ready object: %p (%s)\n", fin->object, safe_name (fin->object)));
-               fin->object = copy_object (fin->object, start, end);
-       }
+       scan_finalizer_entries (fin_ready_list, start, end);
+       scan_finalizer_entries (critical_fin_list, start, end);
 }
 
 static mword fragment_total = 0;
@@ -2457,6 +2516,7 @@ collect_nursery (size_t requested_size)
        }
        /* registered roots, this includes static fields */
        scan_from_registered_roots (nursery_start, nursery_next, ROOT_TYPE_NORMAL);
+       scan_thread_data (nursery_start, nursery_next, TRUE);
        /* alloc_pinned objects */
        scan_from_pinned_objects (nursery_start, nursery_next);
        TV_GETTIME (btv);
@@ -2478,7 +2538,7 @@ collect_nursery (size_t requested_size)
        /* prepare the pin queue for the next collection */
        last_num_pinned = next_pin_slot;
        next_pin_slot = 0;
-       if (fin_ready_list) {
+       if (fin_ready_list || critical_fin_list) {
                DEBUG (4, fprintf (gc_debug_file, "Finalizer-thread wakeup: ready %d\n", num_ready_finalizers));
                mono_gc_finalize_notify ();
        }
@@ -2491,7 +2551,6 @@ major_collection (void)
        LOSObject *bigobj, *prevbo;
        int i;
        PinnedChunk *chunk;
-       FinalizeEntry *fin;
        Fragment *frag;
        int count;
        TV_DECLARE (all_atv);
@@ -2601,13 +2660,13 @@ major_collection (void)
        /* registered roots, this includes static fields */
        scan_from_registered_roots (heap_start, heap_end, ROOT_TYPE_NORMAL);
        scan_from_registered_roots (heap_start, heap_end, ROOT_TYPE_WBARRIER);
+       /* Threads */
+       scan_thread_data (heap_start, heap_end, TRUE);
        /* alloc_pinned objects */
        scan_from_pinned_objects (heap_start, heap_end);
        /* scan the list of objects ready for finalization */
-       for (fin = fin_ready_list; fin; fin = fin->next) {
-               DEBUG (5, fprintf (gc_debug_file, "Scan of fin ready object: %p (%s)\n", fin->object, safe_name (fin->object)));
-               fin->object = copy_object (fin->object, heap_start, heap_end);
-       }
+       scan_finalizer_entries (fin_ready_list, heap_start, heap_end);
+       scan_finalizer_entries (critical_fin_list, heap_start, heap_end);
        TV_GETTIME (atv);
        DEBUG (2, fprintf (gc_debug_file, "Root scan: %d usecs\n", TV_ELAPSED (btv, atv)));
 
@@ -2681,7 +2740,7 @@ major_collection (void)
        mono_stats.major_gc_time_usecs += TV_ELAPSED (all_atv, all_btv);
        /* prepare the pin queue for the next collection */
        next_pin_slot = 0;
-       if (fin_ready_list) {
+       if (fin_ready_list || critical_fin_list) {
                DEBUG (4, fprintf (gc_debug_file, "Finalizer-thread wakeup: ready %d\n", num_ready_finalizers));
                mono_gc_finalize_notify ();
        }
@@ -3511,6 +3570,32 @@ mono_gc_alloc_pinned_obj (MonoVTable *vtable, size_t size)
  */
 #define object_is_fin_ready(obj) (!object_is_pinned (obj) && !object_is_forwarded (obj))
 
+static gboolean
+is_critical_finalizer (FinalizeEntry *entry)
+{
+       MonoObject *obj;
+       MonoClass *class;
+
+       if (!mono_defaults.critical_finalizer_object)
+               return FALSE;
+
+       obj = entry->object;
+       class = ((MonoVTable*)LOAD_VTABLE (obj))->klass;
+
+       return mono_class_has_parent (class, mono_defaults.critical_finalizer_object);
+}
+
+static void
+queue_finalization_entry (FinalizeEntry *entry) {
+       if (is_critical_finalizer (entry)) {
+               entry->next = critical_fin_list;
+               critical_fin_list = entry;
+       } else {
+               entry->next = fin_ready_list;
+               fin_ready_list = entry;
+       }
+}
+
 static void
 finalize_in_range (char *start, char *end)
 {
@@ -3533,8 +3618,7 @@ finalize_in_range (char *start, char *end)
                                        next = entry->next;
                                        num_ready_finalizers++;
                                        num_registered_finalizers--;
-                                       entry->next = fin_ready_list;
-                                       fin_ready_list = entry;
+                                       queue_finalization_entry (entry);
                                        /* Make it survive */
                                        from = entry->object;
                                        entry->object = copy_object (entry->object, start, end);
@@ -3556,36 +3640,41 @@ finalize_in_range (char *start, char *end)
 static void
 null_link_in_range (char *start, char *end)
 {
-       FinalizeEntry *entry, *prev;
+       DisappearingLink *entry, *prev;
        int i;
        for (i = 0; i < disappearing_link_hash_size; ++i) {
                prev = NULL;
                for (entry = disappearing_link_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)) {
-                                       void **p = entry->data;
-                                       FinalizeEntry *old;
+                       char *object = DISLINK_OBJECT (entry);
+                       if (object >= start && object < end && (object < to_space || object >= to_space_end)) {
+                               if (!DISLINK_TRACK (entry) && 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, entry->object));
+                                       DEBUG (5, fprintf (gc_debug_file, "Dislink nullified at %p to GCed object %p\n", p, object));
                                        old = entry->next;
                                        free_internal_mem (entry);
                                        entry = old;
                                        num_disappearing_links--;
                                        continue;
                                } else {
-                                       void **link;
                                        /* update pointer if it's moved
                                         * FIXME: what if an object is moved earlier?
                                         */
-                                       entry->object = copy_object (entry->object, start, end);
-                                       DEBUG (5, fprintf (gc_debug_file, "Updated dislink at %p to %p\n", entry->data, entry->object));
-                                       link = entry->data;
-                                       *link = entry->object;
+                                       /* We set the track
+                                        * resurrection bit to FALSE
+                                        * here so that the object can
+                                        * be collected in the next
+                                        * cycle (i.e. after it was
+                                        * finalized).
+                                        */
+                                       *entry->link = HIDE_POINTER (copy_object (object, start, end), FALSE);
+                                       DEBUG (5, fprintf (gc_debug_file, "Updated dislink at %p to %p\n", entry->link, DISLINK_OBJECT (entry)));
                                }
                        }
                        prev = entry;
@@ -3675,6 +3764,7 @@ mono_gc_register_for_finalization (MonoObject *obj, void *user_data)
        unsigned int hash;
        if (no_finalize)
                return;
+       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)
@@ -3683,9 +3773,7 @@ mono_gc_register_for_finalization (MonoObject *obj, void *user_data)
        prev = NULL;
        for (entry = finalizable_hash [hash]; entry; entry = entry->next) {
                if (entry->object == obj) {
-                       if (user_data) {
-                               entry->data = user_data;
-                       } else {
+                       if (!user_data) {
                                /* remove from the list */
                                if (prev)
                                        prev->next = entry->next;
@@ -3707,7 +3795,6 @@ mono_gc_register_for_finalization (MonoObject *obj, void *user_data)
        }
        entry = get_internal_mem (sizeof (FinalizeEntry));
        entry->object = obj;
-       entry->data = user_data;
        entry->next = finalizable_hash [hash];
        finalizable_hash [hash] = entry;
        num_registered_finalizers++;
@@ -3720,14 +3807,14 @@ rehash_dislink (void)
 {
        int i;
        unsigned int hash;
-       FinalizeEntry **new_hash;
-       FinalizeEntry *entry, *next;
+       DisappearingLink **new_hash;
+       DisappearingLink *entry, *next;
        int new_size = g_spaced_primes_closest (num_disappearing_links);
 
-       new_hash = get_internal_mem (new_size * sizeof (FinalizeEntry*));
+       new_hash = get_internal_mem (new_size * sizeof (DisappearingLink*));
        for (i = 0; i < disappearing_link_hash_size; ++i) {
                for (entry = disappearing_link_hash [i]; entry; entry = next) {
-                       hash = mono_aligned_addr_hash (entry->data) % new_size;
+                       hash = mono_aligned_addr_hash (entry->link) % new_size;
                        next = entry->next;
                        entry->next = new_hash [hash];
                        new_hash [hash] = entry;
@@ -3739,9 +3826,9 @@ rehash_dislink (void)
 }
 
 static void
-mono_gc_register_disappearing_link (MonoObject *obj, void *link)
+mono_gc_register_disappearing_link (MonoObject *obj, void **link, gboolean track)
 {
-       FinalizeEntry *entry, *prev;
+       DisappearingLink *entry, *prev;
        unsigned int hash;
        LOCK_GC;
 
@@ -3753,7 +3840,7 @@ mono_gc_register_disappearing_link (MonoObject *obj, void *link)
        prev = NULL;
        for (; entry; entry = entry->next) {
                /* link already added */
-               if (link == entry->data) {
+               if (link == entry->link) {
                        /* NULL obj means remove */
                        if (obj == NULL) {
                                if (prev)
@@ -3763,17 +3850,18 @@ mono_gc_register_disappearing_link (MonoObject *obj, void *link)
                                num_disappearing_links--;
                                DEBUG (5, fprintf (gc_debug_file, "Removed dislink %p (%d)\n", entry, num_disappearing_links));
                                free_internal_mem (entry);
+                               *link = NULL;
                        } else {
-                               entry->object = obj; /* we allow the change of object */
+                               *link = HIDE_POINTER (obj, track); /* we allow the change of object */
                        }
                        UNLOCK_GC;
                        return;
                }
                prev = entry;
        }
-       entry = get_internal_mem (sizeof (FinalizeEntry));
-       entry->object = obj;
-       entry->data = link;
+       entry = get_internal_mem (sizeof (DisappearingLink));
+       *link = HIDE_POINTER (obj, track);
+       entry->link = link;
        entry->next = disappearing_link_hash [hash];
        disappearing_link_hash [hash] = entry;
        num_disappearing_links++;
@@ -3784,38 +3872,70 @@ mono_gc_register_disappearing_link (MonoObject *obj, void *link)
 int
 mono_gc_invoke_finalizers (void)
 {
-       FinalizeEntry *entry;
+       FinalizeEntry *entry = NULL;
+       gboolean entry_is_critical;
        int count = 0;
        void *obj;
        /* FIXME: batch to reduce lock contention */
-       while (fin_ready_list) {
+       while (fin_ready_list || critical_fin_list) {
                LOCK_GC;
-               entry = fin_ready_list;
+
+               if (entry) {
+                       FinalizeEntry **list = entry_is_critical ? &critical_fin_list : &fin_ready_list;
+
+                       /* We have finalized entry in the last
+                          interation, now we need to remove it from
+                          the list. */
+                       if (*list == entry)
+                               *list = entry->next;
+                       else {
+                               FinalizeEntry *e = *list;
+                               while (e->next != entry)
+                                       e = e->next;
+                               e->next = entry->next;
+                       }
+                       free_internal_mem (entry);
+                       entry = NULL;
+               }
+
+               /* Now look for the first non-null entry. */
+               for (entry = fin_ready_list; entry && !entry->object; entry = entry->next)
+                       ;
                if (entry) {
-                       fin_ready_list = entry->next;
+                       entry_is_critical = FALSE;
+               } else {
+                       entry_is_critical = TRUE;
+                       for (entry = critical_fin_list; entry && !entry->object; entry = entry->next)
+                               ;
+               }
+
+               if (entry) {
+                       g_assert (entry->object);
                        num_ready_finalizers--;
                        obj = entry->object;
+                       entry->object = NULL;
                        DEBUG (7, fprintf (gc_debug_file, "Finalizing object %p (%s)\n", obj, safe_name (obj)));
                }
+
                UNLOCK_GC;
-               if (entry) {
-                       void (*callback)(void *, void*) = entry->data;
-                       entry->next = NULL;
-                       obj = entry->object;
-                       count++;
-                       /* the object is on the stack so it is pinned */
-                       /*g_print ("Calling finalizer for object: %p (%s)\n", entry->object, safe_name (entry->object));*/
-                       callback (obj, NULL);
-                       free_internal_mem (entry);
-               }
+
+               if (!entry)
+                       break;
+
+               g_assert (entry->object == NULL);
+               count++;
+               /* the object is on the stack so it is pinned */
+               /*g_print ("Calling finalizer for object: %p (%s)\n", entry->object, safe_name (entry->object));*/
+               mono_gc_run_finalize (obj, NULL);
        }
+       g_assert (!entry);
        return count;
 }
 
 gboolean
 mono_gc_pending_finalizers (void)
 {
-       return fin_ready_list != NULL;
+       return fin_ready_list || critical_fin_list;
 }
 
 /* Negative value to remove */
@@ -3987,6 +4107,7 @@ struct _SgenThreadInfo {
        char **tlab_temp_end_addr;
        char **tlab_real_end_addr;
        RememberedSet *remset;
+       gpointer runtime_data;
 };
 
 /* FIXME: handle large/small config */
@@ -4025,6 +4146,8 @@ update_current_thread_stack (void *start)
        SgenThreadInfo *info = thread_info_lookup (ARCH_GET_THREAD ());
        info->stack_start = align_pointer (&ptr);
        ARCH_STORE_REGS (ptr);
+       if (gc_callbacks.thread_suspend_func)
+               gc_callbacks.thread_suspend_func (info->runtime_data, NULL);
 }
 
 static const char*
@@ -4078,7 +4201,7 @@ thread_handshake (int signum)
 
 /* LOCKING: assumes the GC lock is held (by the stopping thread) */
 static void
-suspend_handler (int sig)
+suspend_handler (int sig, siginfo_t *siginfo, void *context)
 {
        SgenThreadInfo *info;
        pthread_t id;
@@ -4102,6 +4225,10 @@ suspend_handler (int sig)
         */
        info->stack_start = align_pointer (&id);
 
+       /* Notify the JIT */
+       if (gc_callbacks.thread_suspend_func)
+               gc_callbacks.thread_suspend_func (info->runtime_data, context);
+
        /* notify the waiting thread */
        sem_post (&suspend_ack_semaphore);
        info->stop_count = stop_count;
@@ -4165,15 +4292,39 @@ restart_world (void)
 
 #endif /* USE_SIGNAL_BASED_START_STOP_WORLD */
 
+void
+mono_gc_set_gc_callbacks (MonoGCCallbacks *callbacks)
+{
+       gc_callbacks = *callbacks;
+}
+
+/* Variables holding start/end nursery so it won't have to be passed at every call */
+static void *scan_area_arg_start, *scan_area_arg_end;
+
+void
+mono_gc_conservatively_scan_area (void *start, void *end)
+{
+       conservatively_pin_objects_from (start, end, scan_area_arg_start, scan_area_arg_end);
+}
+
+void*
+mono_gc_scan_object (void *obj)
+{
+       return copy_object (obj, scan_area_arg_start, scan_area_arg_end);
+}
+       
 /*
- * Identify objects pinned in a thread stack and its registers.
+ * Mark from thread stacks and registers.
  */
 static void
-pin_thread_data (void *start_nursery, void *end_nursery)
+scan_thread_data (void *start_nursery, void *end_nursery, gboolean precise)
 {
        int i;
        SgenThreadInfo *info;
 
+       scan_area_arg_start = start_nursery;
+       scan_area_arg_end = end_nursery;
+
        for (i = 0; i < THREAD_HASH_SIZE; ++i) {
                for (info = thread_table [i]; info; info = info->next) {
                        if (info->skip) {
@@ -4181,11 +4332,15 @@ pin_thread_data (void *start_nursery, void *end_nursery)
                                continue;
                        }
                        DEBUG (2, fprintf (gc_debug_file, "Scanning thread %p, range: %p-%p, size: %zd, pinned=%d\n", info, info->stack_start, info->stack_end, (char*)info->stack_end - (char*)info->stack_start, next_pin_slot));
-                       conservatively_pin_objects_from (info->stack_start, info->stack_end, start_nursery, end_nursery);
+                       if (gc_callbacks.thread_mark_func)
+                               gc_callbacks.thread_mark_func (info->runtime_data, info->stack_start, info->stack_end, precise);
+                       else if (!precise)
+                               conservatively_pin_objects_from (info->stack_start, info->stack_end, start_nursery, end_nursery);
                }
        }
        DEBUG (2, fprintf (gc_debug_file, "Scanning current thread registers, pinned=%d\n", next_pin_slot));
-       conservatively_pin_objects_from ((void*)cur_thread_regs, (void*)(cur_thread_regs + ARCH_NUM_REGS), start_nursery, end_nursery);
+       if (!precise)
+               conservatively_pin_objects_from ((void*)cur_thread_regs, (void*)(cur_thread_regs + ARCH_NUM_REGS), start_nursery, end_nursery);
 }
 
 static void
@@ -4490,6 +4645,10 @@ gc_register_current_thread (void *addr)
        remembered_set = info->remset = alloc_remset (DEFAULT_REMSET_SIZE, info);
        pthread_setspecific (remembered_set_key, remembered_set);
        DEBUG (3, fprintf (gc_debug_file, "registered thread %p (%p) (hash: %d)\n", info, (gpointer)info->id, hash));
+
+       if (gc_callbacks.thread_attach_func)
+               info->runtime_data = gc_callbacks.thread_attach_func ();
+
        return info;
 }
 
@@ -5271,23 +5430,23 @@ mono_gc_enable_events (void)
 }
 
 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);
-       *link_addr = obj;
+       mono_gc_register_disappearing_link (obj, link_addr, track);
 }
 
 void
 mono_gc_weak_link_remove (void **link_addr)
 {
-       mono_gc_register_disappearing_link (NULL, link_addr);
-       *link_addr = NULL;
+       mono_gc_register_disappearing_link (NULL, link_addr, FALSE);
 }
 
 MonoObject*
 mono_gc_weak_link_get (void **link_addr)
 {
-       return *link_addr;
+       if (!*link_addr)
+               return NULL;
+       return (MonoObject*) REVEAL_POINTER (*link_addr);
 }
 
 void*
@@ -5379,10 +5538,12 @@ mono_gc_base_init (void)
                                collect_before_allocs = TRUE;
                        } else if (!strcmp (opt, "check-at-minor-collections")) {
                                consistency_check_at_minor_collection = TRUE;
+                       } else if (!strcmp (opt, "clear-at-gc")) {
+                               nursery_clear_policy = CLEAR_AT_GC;
                        } else {
                                fprintf (stderr, "Invalid format for the MONO_GC_DEBUG env variable: '%s'\n", env);
                                fprintf (stderr, "The format is: MONO_GC_DEBUG=[l[:filename]|<option>]+ where l is a debug level 0-9.\n");
-                               fprintf (stderr, "Valid options are: collect-before-allocs, check-at-minor-collections.\n");
+                               fprintf (stderr, "Valid options are: collect-before-allocs, check-at-minor-collections, clear-at-gc.\n");
                                exit (1);
                        }
                }
@@ -5393,7 +5554,7 @@ mono_gc_base_init (void)
 
        sigfillset (&sinfo.sa_mask);
        sinfo.sa_flags = SA_RESTART | SA_SIGINFO;
-       sinfo.sa_handler = suspend_handler;
+       sinfo.sa_sigaction = suspend_handler;
        if (sigaction (suspend_signal_num, &sinfo, NULL) != 0) {
                g_error ("failed sigaction");
        }