Merge pull request #3716 from vargaz/unbox-stobj-null
[mono.git] / mono / sgen / sgen-gc.c
index b510a4fc43b77c222100da152262c457f507d79e..2fdd86c2bf0613f7e75129c417d5a3341f82c1a4 100644 (file)
@@ -214,8 +214,8 @@ guint32 verify_before_allocs = 0;
 guint32 collect_before_allocs = 0;
 /* If set, do a whole heap check before each collection */
 static gboolean whole_heap_check_before_collection = FALSE;
-/* If set, do a heap consistency check before each minor collection */
-static gboolean consistency_check_at_minor_collection = FALSE;
+/* If set, do a remset consistency check at various opportunities */
+static gboolean remset_consistency_checks = FALSE;
 /* If set, do a mod union consistency check before each finishing collection pause */
 static gboolean mod_union_consistency_check = FALSE;
 /* If set, check whether mark bits are consistent after major collections */
@@ -288,11 +288,10 @@ static guint64 time_max = 0;
 static SGEN_TV_DECLARE (time_major_conc_collection_start);
 static SGEN_TV_DECLARE (time_major_conc_collection_end);
 
-static SGEN_TV_DECLARE (last_minor_collection_start_tv);
-static SGEN_TV_DECLARE (last_minor_collection_end_tv);
-
 int gc_debug_level = 0;
 FILE* gc_debug_file;
+static char* gc_params_options;
+static char* gc_debug_options;
 
 /*
 void
@@ -399,8 +398,6 @@ static mword objects_pinned;
  * ######################################################################
  */
 
-typedef SgenGrayQueue GrayQueue;
-
 /* forward declarations */
 static void scan_from_registered_roots (char *addr_start, char *addr_end, int root_type, ScanCopyContext ctx);
 
@@ -410,51 +407,29 @@ static void finish_gray_stack (int generation, ScanCopyContext ctx);
 
 SgenMajorCollector major_collector;
 SgenMinorCollector sgen_minor_collector;
-/* FIXME: get rid of this */
-static GrayQueue gray_queue;
 
 static SgenRememberedSet remset;
 
-/* The gray queue to use from the main collection thread. */
-#define WORKERS_DISTRIBUTE_GRAY_QUEUE  (&gray_queue)
-
 /*
  * The gray queue a worker job must use.  If we're not parallel or
  * concurrent, we use the main gray queue.
  */
 static SgenGrayQueue*
-sgen_workers_get_job_gray_queue (WorkerData *worker_data)
+sgen_workers_get_job_gray_queue (WorkerData *worker_data, SgenGrayQueue *default_gray_queue)
 {
-       return worker_data ? &worker_data->private_gray_queue : WORKERS_DISTRIBUTE_GRAY_QUEUE;
-}
-
-static void
-gray_queue_redirect (SgenGrayQueue *queue)
-{
-       gboolean wake = FALSE;
-
-       for (;;) {
-               GrayQueueSection *section = sgen_gray_object_dequeue_section (queue);
-               if (!section)
-                       break;
-               sgen_section_gray_queue_enqueue ((SgenSectionGrayQueue *)queue->alloc_prepare_data, section);
-               wake = TRUE;
-       }
-
-       if (wake) {
-               g_assert (concurrent_collection_in_progress);
-               sgen_workers_ensure_awake ();
-       }
+       if (worker_data)
+               return &worker_data->private_gray_queue;
+       SGEN_ASSERT (0, default_gray_queue, "Why don't we have a default gray queue when we're not running in a worker thread?");
+       return default_gray_queue;
 }
 
 static void
 gray_queue_enable_redirect (SgenGrayQueue *queue)
 {
-       if (!concurrent_collection_in_progress)
-               return;
+       SGEN_ASSERT (0, concurrent_collection_in_progress, "Where are we redirecting the gray queue to, without a concurrent collection?");
 
-       sgen_gray_queue_set_alloc_prepare (queue, gray_queue_redirect, sgen_workers_get_distribute_section_gray_queue ());
-       gray_queue_redirect (queue);
+       sgen_gray_queue_set_alloc_prepare (queue, sgen_workers_take_from_queue_and_awake);
+       sgen_workers_take_from_queue_and_awake (queue);
 }
 
 void
@@ -537,7 +512,7 @@ gboolean
 sgen_drain_gray_stack (ScanCopyContext ctx)
 {
        ScanObjectFunc scan_func = ctx.ops->scan_object;
-       GrayQueue *queue = ctx.queue;
+       SgenGrayQueue *queue = ctx.queue;
 
        if (ctx.ops->drain_gray_stack)
                return ctx.ops->drain_gray_stack (queue);
@@ -734,7 +709,7 @@ pin_objects_in_nursery (gboolean do_scan_objects, ScanCopyContext ctx)
  * when we can't promote an object because we're out of memory.
  */
 void
-sgen_pin_object (GCObject *object, GrayQueue *queue)
+sgen_pin_object (GCObject *object, SgenGrayQueue *queue)
 {
        SGEN_ASSERT (0, sgen_ptr_in_nursery (object), "We're only supposed to use this for pinning nursery objects when out of memory.");
 
@@ -1017,6 +992,24 @@ mono_gc_get_logfile (void)
        return gc_debug_file;
 }
 
+void
+mono_gc_params_set (const char* options)
+{
+       if (gc_params_options)
+               g_free (gc_params_options);
+
+       gc_params_options = g_strdup (options);
+}
+
+void
+mono_gc_debug_set (const char* options)
+{
+       if (gc_debug_options)
+               g_free (gc_debug_options);
+
+       gc_debug_options = g_strdup (options);
+}
+
 static void
 scan_finalizer_entries (SgenPointerQueue *fin_queue, ScanCopyContext ctx)
 {
@@ -1196,7 +1189,6 @@ finish_gray_stack (int generation, ScanCopyContext ctx)
 
        g_assert (sgen_gray_object_queue_is_empty (queue));
 
-       sgen_gray_object_queue_trim_free_list (queue);
        binary_protocol_finish_gray_stack_end (sgen_timestamp (), generation);
 }
 
@@ -1329,20 +1321,25 @@ sgen_concurrent_collection_in_progress (void)
 typedef struct {
        SgenThreadPoolJob job;
        SgenObjectOperations *ops;
+       SgenGrayQueue *gc_thread_gray_queue;
 } ScanJob;
 
+static ScanCopyContext
+scan_copy_context_for_scan_job (void *worker_data_untyped, ScanJob *job)
+{
+       WorkerData *worker_data = (WorkerData *)worker_data_untyped;
+
+       return CONTEXT_FROM_OBJECT_OPERATIONS (job->ops, sgen_workers_get_job_gray_queue (worker_data, job->gc_thread_gray_queue));
+}
+
 static void
 job_remembered_set_scan (void *worker_data_untyped, SgenThreadPoolJob *job)
 {
-       WorkerData *worker_data = (WorkerData *)worker_data_untyped;
-       ScanJob *job_data = (ScanJob*)job;
-       ScanCopyContext ctx = CONTEXT_FROM_OBJECT_OPERATIONS (job_data->ops, sgen_workers_get_job_gray_queue (worker_data));
-       remset.scan_remsets (ctx);
+       remset.scan_remsets (scan_copy_context_for_scan_job (worker_data_untyped, (ScanJob*)job));
 }
 
 typedef struct {
-       SgenThreadPoolJob job;
-       SgenObjectOperations *ops;
+       ScanJob scan_job;
        char *heap_start;
        char *heap_end;
        int root_type;
@@ -1351,16 +1348,14 @@ typedef struct {
 static void
 job_scan_from_registered_roots (void *worker_data_untyped, SgenThreadPoolJob *job)
 {
-       WorkerData *worker_data = (WorkerData *)worker_data_untyped;
        ScanFromRegisteredRootsJob *job_data = (ScanFromRegisteredRootsJob*)job;
-       ScanCopyContext ctx = CONTEXT_FROM_OBJECT_OPERATIONS (job_data->ops, sgen_workers_get_job_gray_queue (worker_data));
+       ScanCopyContext ctx = scan_copy_context_for_scan_job (worker_data_untyped, &job_data->scan_job);
 
        scan_from_registered_roots (job_data->heap_start, job_data->heap_end, job_data->root_type, ctx);
 }
 
 typedef struct {
-       SgenThreadPoolJob job;
-       SgenObjectOperations *ops;
+       ScanJob scan_job;
        char *heap_start;
        char *heap_end;
 } ScanThreadDataJob;
@@ -1368,25 +1363,22 @@ typedef struct {
 static void
 job_scan_thread_data (void *worker_data_untyped, SgenThreadPoolJob *job)
 {
-       WorkerData *worker_data = (WorkerData *)worker_data_untyped;
        ScanThreadDataJob *job_data = (ScanThreadDataJob*)job;
-       ScanCopyContext ctx = CONTEXT_FROM_OBJECT_OPERATIONS (job_data->ops, sgen_workers_get_job_gray_queue (worker_data));
+       ScanCopyContext ctx = scan_copy_context_for_scan_job (worker_data_untyped, &job_data->scan_job);
 
        sgen_client_scan_thread_data (job_data->heap_start, job_data->heap_end, TRUE, ctx);
 }
 
 typedef struct {
-       SgenThreadPoolJob job;
-       SgenObjectOperations *ops;
+       ScanJob scan_job;
        SgenPointerQueue *queue;
 } ScanFinalizerEntriesJob;
 
 static void
 job_scan_finalizer_entries (void *worker_data_untyped, SgenThreadPoolJob *job)
 {
-       WorkerData *worker_data = (WorkerData *)worker_data_untyped;
        ScanFinalizerEntriesJob *job_data = (ScanFinalizerEntriesJob*)job;
-       ScanCopyContext ctx = CONTEXT_FROM_OBJECT_OPERATIONS (job_data->ops, sgen_workers_get_job_gray_queue (worker_data));
+       ScanCopyContext ctx = scan_copy_context_for_scan_job (worker_data_untyped, &job_data->scan_job);
 
        scan_finalizer_entries (job_data->queue, ctx);
 }
@@ -1394,9 +1386,8 @@ job_scan_finalizer_entries (void *worker_data_untyped, SgenThreadPoolJob *job)
 static void
 job_scan_major_mod_union_card_table (void *worker_data_untyped, SgenThreadPoolJob *job)
 {
-       WorkerData *worker_data = (WorkerData *)worker_data_untyped;
        ScanJob *job_data = (ScanJob*)job;
-       ScanCopyContext ctx = CONTEXT_FROM_OBJECT_OPERATIONS (job_data->ops, sgen_workers_get_job_gray_queue (worker_data));
+       ScanCopyContext ctx = scan_copy_context_for_scan_job (worker_data_untyped, job_data);
 
        g_assert (concurrent_collection_in_progress);
        major_collector.scan_card_table (CARDTABLE_SCAN_MOD_UNION, ctx);
@@ -1405,9 +1396,8 @@ job_scan_major_mod_union_card_table (void *worker_data_untyped, SgenThreadPoolJo
 static void
 job_scan_los_mod_union_card_table (void *worker_data_untyped, SgenThreadPoolJob *job)
 {
-       WorkerData *worker_data = (WorkerData *)worker_data_untyped;
        ScanJob *job_data = (ScanJob*)job;
-       ScanCopyContext ctx = CONTEXT_FROM_OBJECT_OPERATIONS (job_data->ops, sgen_workers_get_job_gray_queue (worker_data));
+       ScanCopyContext ctx = scan_copy_context_for_scan_job (worker_data_untyped, job_data);
 
        g_assert (concurrent_collection_in_progress);
        sgen_los_scan_card_table (CARDTABLE_SCAN_MOD_UNION, ctx);
@@ -1416,9 +1406,8 @@ job_scan_los_mod_union_card_table (void *worker_data_untyped, SgenThreadPoolJob
 static void
 job_mod_union_preclean (void *worker_data_untyped, SgenThreadPoolJob *job)
 {
-       WorkerData *worker_data = (WorkerData *)worker_data_untyped;
        ScanJob *job_data = (ScanJob*)job;
-       ScanCopyContext ctx = CONTEXT_FROM_OBJECT_OPERATIONS (job_data->ops, sgen_workers_get_job_gray_queue (worker_data));
+       ScanCopyContext ctx = scan_copy_context_for_scan_job (worker_data_untyped, job_data);
 
        g_assert (concurrent_collection_in_progress);
 
@@ -1429,15 +1418,15 @@ job_mod_union_preclean (void *worker_data_untyped, SgenThreadPoolJob *job)
 }
 
 static void
-init_gray_queue (gboolean use_workers)
+init_gray_queue (SgenGrayQueue *gc_thread_gray_queue, gboolean use_workers)
 {
        if (use_workers)
                sgen_workers_init_distribute_gray_queue ();
-       sgen_gray_object_queue_init (&gray_queue, NULL);
+       sgen_gray_object_queue_init (gc_thread_gray_queue, NULL, TRUE);
 }
 
 static void
-enqueue_scan_from_roots_jobs (char *heap_start, char *heap_end, SgenObjectOperations *ops, gboolean enqueue)
+enqueue_scan_from_roots_jobs (SgenGrayQueue *gc_thread_gray_queue, char *heap_start, char *heap_end, SgenObjectOperations *ops, gboolean enqueue)
 {
        ScanFromRegisteredRootsJob *scrrj;
        ScanThreadDataJob *stdj;
@@ -1446,38 +1435,43 @@ enqueue_scan_from_roots_jobs (char *heap_start, char *heap_end, SgenObjectOperat
        /* registered roots, this includes static fields */
 
        scrrj = (ScanFromRegisteredRootsJob*)sgen_thread_pool_job_alloc ("scan from registered roots normal", job_scan_from_registered_roots, sizeof (ScanFromRegisteredRootsJob));
-       scrrj->ops = ops;
+       scrrj->scan_job.ops = ops;
+       scrrj->scan_job.gc_thread_gray_queue = gc_thread_gray_queue;
        scrrj->heap_start = heap_start;
        scrrj->heap_end = heap_end;
        scrrj->root_type = ROOT_TYPE_NORMAL;
-       sgen_workers_enqueue_job (&scrrj->job, enqueue);
+       sgen_workers_enqueue_job (&scrrj->scan_job.job, enqueue);
 
        scrrj = (ScanFromRegisteredRootsJob*)sgen_thread_pool_job_alloc ("scan from registered roots wbarrier", job_scan_from_registered_roots, sizeof (ScanFromRegisteredRootsJob));
-       scrrj->ops = ops;
+       scrrj->scan_job.ops = ops;
+       scrrj->scan_job.gc_thread_gray_queue = gc_thread_gray_queue;
        scrrj->heap_start = heap_start;
        scrrj->heap_end = heap_end;
        scrrj->root_type = ROOT_TYPE_WBARRIER;
-       sgen_workers_enqueue_job (&scrrj->job, enqueue);
+       sgen_workers_enqueue_job (&scrrj->scan_job.job, enqueue);
 
        /* Threads */
 
        stdj = (ScanThreadDataJob*)sgen_thread_pool_job_alloc ("scan thread data", job_scan_thread_data, sizeof (ScanThreadDataJob));
-       stdj->ops = ops;
+       stdj->scan_job.ops = ops;
+       stdj->scan_job.gc_thread_gray_queue = gc_thread_gray_queue;
        stdj->heap_start = heap_start;
        stdj->heap_end = heap_end;
-       sgen_workers_enqueue_job (&stdj->job, enqueue);
+       sgen_workers_enqueue_job (&stdj->scan_job.job, enqueue);
 
        /* Scan the list of objects ready for finalization. */
 
        sfej = (ScanFinalizerEntriesJob*)sgen_thread_pool_job_alloc ("scan finalizer entries", job_scan_finalizer_entries, sizeof (ScanFinalizerEntriesJob));
+       sfej->scan_job.ops = ops;
+       sfej->scan_job.gc_thread_gray_queue = gc_thread_gray_queue;
        sfej->queue = &fin_ready_queue;
-       sfej->ops = ops;
-       sgen_workers_enqueue_job (&sfej->job, enqueue);
+       sgen_workers_enqueue_job (&sfej->scan_job.job, enqueue);
 
        sfej = (ScanFinalizerEntriesJob*)sgen_thread_pool_job_alloc ("scan critical finalizer entries", job_scan_finalizer_entries, sizeof (ScanFinalizerEntriesJob));
+       sfej->scan_job.ops = ops;
+       sfej->scan_job.gc_thread_gray_queue = gc_thread_gray_queue;
        sfej->queue = &critical_fin_queue;
-       sfej->ops = ops;
-       sgen_workers_enqueue_job (&sfej->job, enqueue);
+       sgen_workers_enqueue_job (&sfej->scan_job.job, enqueue);
 }
 
 /*
@@ -1486,17 +1480,20 @@ enqueue_scan_from_roots_jobs (char *heap_start, char *heap_end, SgenObjectOperat
  * Return whether any objects were late-pinned due to being out of memory.
  */
 static gboolean
-collect_nursery (const char *reason, gboolean is_overflow, SgenGrayQueue *unpin_queue, gboolean finish_up_concurrent_mark)
+collect_nursery (const char *reason, gboolean is_overflow, SgenGrayQueue *unpin_queue)
 {
        gboolean needs_major;
        size_t max_garbage_amount;
        char *nursery_next;
        mword fragment_total;
        ScanJob *sj;
-       SgenObjectOperations *object_ops = &sgen_minor_collector.serial_ops;
-       ScanCopyContext ctx = CONTEXT_FROM_OBJECT_OPERATIONS (object_ops, &gray_queue);
+       SgenGrayQueue gc_thread_gray_queue;
+       SgenObjectOperations *object_ops;
+       ScanCopyContext ctx;
        TV_DECLARE (atv);
        TV_DECLARE (btv);
+       SGEN_TV_DECLARE (last_minor_collection_start_tv);
+       SGEN_TV_DECLARE (last_minor_collection_end_tv);
 
        if (disable_minor_collections)
                return TRUE;
@@ -1506,6 +1503,11 @@ collect_nursery (const char *reason, gboolean is_overflow, SgenGrayQueue *unpin_
 
        binary_protocol_collection_begin (gc_stats.minor_gc_count, GENERATION_NURSERY);
 
+       if (sgen_concurrent_collection_in_progress ())
+               object_ops = &sgen_minor_collector.serial_ops_with_concurrent_major;
+       else
+               object_ops = &sgen_minor_collector.serial_ops;
+
        if (do_verify_nursery || do_dump_nursery_content)
                sgen_debug_verify_nursery (do_dump_nursery_content);
 
@@ -1541,17 +1543,11 @@ collect_nursery (const char *reason, gboolean is_overflow, SgenGrayQueue *unpin_
 
        sgen_memgov_minor_collection_start ();
 
-       init_gray_queue (FALSE);
+       init_gray_queue (&gc_thread_gray_queue, FALSE);
+       ctx = CONTEXT_FROM_OBJECT_OPERATIONS (object_ops, &gc_thread_gray_queue);
 
        gc_stats.minor_gc_count ++;
 
-       if (whole_heap_check_before_collection) {
-               sgen_clear_nursery_fragments ();
-               sgen_check_whole_heap (finish_up_concurrent_mark);
-       }
-       if (consistency_check_at_minor_collection)
-               sgen_check_consistency ();
-
        sgen_process_fin_stage_entries ();
 
        /* pin from pinned handles */
@@ -1567,6 +1563,14 @@ collect_nursery (const char *reason, gboolean is_overflow, SgenGrayQueue *unpin_
        pin_objects_in_nursery (FALSE, ctx);
        sgen_pinning_trim_queue_to_section (nursery_section);
 
+       if (remset_consistency_checks)
+               sgen_check_remset_consistency ();
+
+       if (whole_heap_check_before_collection) {
+               sgen_clear_nursery_fragments ();
+               sgen_check_whole_heap (FALSE);
+       }
+
        TV_GETTIME (atv);
        time_minor_pinning += TV_ELAPSED (btv, atv);
        SGEN_LOG (2, "Finding pinned pointers: %zd in %lld usecs", sgen_get_pinned_count (), (long long)TV_ELAPSED (btv, atv));
@@ -1574,6 +1578,7 @@ collect_nursery (const char *reason, gboolean is_overflow, SgenGrayQueue *unpin_
 
        sj = (ScanJob*)sgen_thread_pool_job_alloc ("scan remset", job_remembered_set_scan, sizeof (ScanJob));
        sj->ops = object_ops;
+       sj->gc_thread_gray_queue = &gc_thread_gray_queue;
        sgen_workers_enqueue_job (&sj->job, FALSE);
 
        /* we don't have complete write barrier yet, so we scan all the old generation sections */
@@ -1589,7 +1594,7 @@ collect_nursery (const char *reason, gboolean is_overflow, SgenGrayQueue *unpin_
        TV_GETTIME (atv);
        time_minor_scan_pinned += TV_ELAPSED (btv, atv);
 
-       enqueue_scan_from_roots_jobs (sgen_get_nursery_start (), nursery_next, object_ops, FALSE);
+       enqueue_scan_from_roots_jobs (&gc_thread_gray_queue, sgen_get_nursery_start (), nursery_next, object_ops, FALSE);
 
        TV_GETTIME (btv);
        time_minor_scan_roots += TV_ELAPSED (atv, btv);
@@ -1605,6 +1610,13 @@ collect_nursery (const char *reason, gboolean is_overflow, SgenGrayQueue *unpin_
                sgen_pinning_setup_section (nursery_section);
        }
 
+       /*
+        * This is the latest point at which we can do this check, because
+        * sgen_build_nursery_fragments() unpins nursery objects again.
+        */
+       if (remset_consistency_checks)
+               sgen_check_remset_consistency ();
+
        /* 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
         * next allocations.
@@ -1622,7 +1634,7 @@ collect_nursery (const char *reason, gboolean is_overflow, SgenGrayQueue *unpin_
        time_minor_fragment_creation += TV_ELAPSED (atv, btv);
        SGEN_LOG (2, "Fragment creation: %lld usecs, %lu bytes available", (long long)TV_ELAPSED (atv, btv), (unsigned long)fragment_total);
 
-       if (consistency_check_at_minor_collection)
+       if (remset_consistency_checks)
                sgen_check_major_refs ();
 
        major_collector.finish_nursery_collection ();
@@ -1642,7 +1654,7 @@ collect_nursery (const char *reason, gboolean is_overflow, SgenGrayQueue *unpin_
        /* clear cemented hash */
        sgen_cement_clear_below_threshold ();
 
-       g_assert (sgen_gray_object_queue_is_empty (&gray_queue));
+       sgen_gray_object_queue_dispose (&gc_thread_gray_queue);
 
        remset.finish_minor_collection ();
 
@@ -1672,7 +1684,7 @@ typedef enum {
 } CopyOrMarkFromRootsMode;
 
 static void
-major_copy_or_mark_from_roots (size_t *old_next_pin_slot, CopyOrMarkFromRootsMode mode, SgenObjectOperations *object_ops)
+major_copy_or_mark_from_roots (SgenGrayQueue *gc_thread_gray_queue, size_t *old_next_pin_slot, CopyOrMarkFromRootsMode mode, SgenObjectOperations *object_ops)
 {
        LOSObject *bigobj;
        TV_DECLARE (atv);
@@ -1682,7 +1694,7 @@ major_copy_or_mark_from_roots (size_t *old_next_pin_slot, CopyOrMarkFromRootsMod
         */
        char *heap_start = NULL;
        char *heap_end = (char*)-1;
-       ScanCopyContext ctx = CONTEXT_FROM_OBJECT_OPERATIONS (object_ops, WORKERS_DISTRIBUTE_GRAY_QUEUE);
+       ScanCopyContext ctx = CONTEXT_FROM_OBJECT_OPERATIONS (object_ops, gc_thread_gray_queue);
        gboolean concurrent = mode != COPY_OR_MARK_FROM_ROOTS_SERIAL;
 
        SGEN_ASSERT (0, !!concurrent == !!concurrent_collection_in_progress, "We've been called with the wrong mode.");
@@ -1698,15 +1710,13 @@ major_copy_or_mark_from_roots (size_t *old_next_pin_slot, CopyOrMarkFromRootsMod
                sgen_nursery_alloc_prepare_for_major ();
        }
 
-       init_gray_queue (mode == COPY_OR_MARK_FROM_ROOTS_START_CONCURRENT);
-
        TV_GETTIME (atv);
 
        /* Pinning depends on this */
        sgen_clear_nursery_fragments ();
 
        if (whole_heap_check_before_collection)
-               sgen_check_whole_heap (mode == COPY_OR_MARK_FROM_ROOTS_FINISH_CONCURRENT);
+               sgen_check_whole_heap (TRUE);
 
        TV_GETTIME (btv);
        time_major_pre_collection_fragment_clear += TV_ELAPSED (atv, btv);
@@ -1777,7 +1787,7 @@ major_copy_or_mark_from_roots (size_t *old_next_pin_slot, CopyOrMarkFromRootsMod
                        }
                        sgen_los_pin_object (bigobj->data);
                        if (SGEN_OBJECT_HAS_REFERENCES (bigobj->data))
-                               GRAY_OBJECT_ENQUEUE (WORKERS_DISTRIBUTE_GRAY_QUEUE, bigobj->data, sgen_obj_get_descriptor ((GCObject*)bigobj->data));
+                               GRAY_OBJECT_ENQUEUE (gc_thread_gray_queue, bigobj->data, sgen_obj_get_descriptor ((GCObject*)bigobj->data));
                        sgen_pin_stats_register_object (bigobj->data, GENERATION_OLD);
                        SGEN_LOG (6, "Marked large object %p (%s) size: %lu from roots", bigobj->data,
                                        sgen_client_vtable_get_name (SGEN_LOAD_VTABLE (bigobj->data)),
@@ -1791,7 +1801,7 @@ major_copy_or_mark_from_roots (size_t *old_next_pin_slot, CopyOrMarkFromRootsMod
        if (check_nursery_objects_pinned && !sgen_minor_collector.is_split)
                sgen_check_nursery_objects_pinned (mode != COPY_OR_MARK_FROM_ROOTS_START_CONCURRENT);
 
-       major_collector.pin_objects (WORKERS_DISTRIBUTE_GRAY_QUEUE);
+       major_collector.pin_objects (gc_thread_gray_queue);
        if (old_next_pin_slot)
                *old_next_pin_slot = sgen_get_pinned_count ();
 
@@ -1825,7 +1835,7 @@ major_copy_or_mark_from_roots (size_t *old_next_pin_slot, CopyOrMarkFromRootsMod
 
        sgen_client_collecting_major_3 (&fin_ready_queue, &critical_fin_queue);
 
-       enqueue_scan_from_roots_jobs (heap_start, heap_end, object_ops, FALSE);
+       enqueue_scan_from_roots_jobs (gc_thread_gray_queue, heap_start, heap_end, object_ops, FALSE);
 
        TV_GETTIME (btv);
        time_major_scan_roots += TV_ELAPSED (atv, btv);
@@ -1841,11 +1851,12 @@ major_copy_or_mark_from_roots (size_t *old_next_pin_slot, CopyOrMarkFromRootsMod
                        /* Mod union preclean job */
                        sj = (ScanJob*)sgen_thread_pool_job_alloc ("preclean mod union cardtable", job_mod_union_preclean, sizeof (ScanJob));
                        sj->ops = object_ops;
+                       sj->gc_thread_gray_queue = NULL;
                        sgen_workers_start_all_workers (object_ops, &sj->job);
                } else {
                        sgen_workers_start_all_workers (object_ops, NULL);
                }
-               gray_queue_enable_redirect (WORKERS_DISTRIBUTE_GRAY_QUEUE);
+               gray_queue_enable_redirect (gc_thread_gray_queue);
        }
 
        if (mode == COPY_OR_MARK_FROM_ROOTS_FINISH_CONCURRENT) {
@@ -1854,10 +1865,12 @@ major_copy_or_mark_from_roots (size_t *old_next_pin_slot, CopyOrMarkFromRootsMod
                /* Mod union card table */
                sj = (ScanJob*)sgen_thread_pool_job_alloc ("scan mod union cardtable", job_scan_major_mod_union_card_table, sizeof (ScanJob));
                sj->ops = object_ops;
+               sj->gc_thread_gray_queue = gc_thread_gray_queue;
                sgen_workers_enqueue_job (&sj->job, FALSE);
 
                sj = (ScanJob*)sgen_thread_pool_job_alloc ("scan LOS mod union cardtable", job_scan_los_mod_union_card_table, sizeof (ScanJob));
                sj->ops = object_ops;
+               sj->gc_thread_gray_queue = gc_thread_gray_queue;
                sgen_workers_enqueue_job (&sj->job, FALSE);
 
                TV_GETTIME (atv);
@@ -1865,11 +1878,7 @@ major_copy_or_mark_from_roots (size_t *old_next_pin_slot, CopyOrMarkFromRootsMod
        }
 
        sgen_pin_stats_report ();
-}
 
-static void
-major_finish_copy_or_mark (CopyOrMarkFromRootsMode mode)
-{
        if (mode == COPY_OR_MARK_FROM_ROOTS_START_CONCURRENT) {
                sgen_finish_pinning ();
 
@@ -1881,7 +1890,7 @@ major_finish_copy_or_mark (CopyOrMarkFromRootsMode mode)
 }
 
 static void
-major_start_collection (const char *reason, gboolean concurrent, size_t *old_next_pin_slot)
+major_start_collection (SgenGrayQueue *gc_thread_gray_queue, const char *reason, gboolean concurrent, size_t *old_next_pin_slot)
 {
        SgenObjectOperations *object_ops;
 
@@ -1889,7 +1898,7 @@ major_start_collection (const char *reason, gboolean concurrent, size_t *old_nex
 
        current_collection_generation = GENERATION_OLD;
 
-       g_assert (sgen_section_gray_queue_is_empty (sgen_workers_get_distribute_section_gray_queue ()));
+       sgen_workers_assert_gray_queue_is_empty ();
 
        if (!concurrent)
                sgen_cement_reset ();
@@ -1919,12 +1928,11 @@ major_start_collection (const char *reason, gboolean concurrent, size_t *old_nex
        if (major_collector.start_major_collection)
                major_collector.start_major_collection ();
 
-       major_copy_or_mark_from_roots (old_next_pin_slot, concurrent ? COPY_OR_MARK_FROM_ROOTS_START_CONCURRENT : COPY_OR_MARK_FROM_ROOTS_SERIAL, object_ops);
-       major_finish_copy_or_mark (concurrent ? COPY_OR_MARK_FROM_ROOTS_START_CONCURRENT : COPY_OR_MARK_FROM_ROOTS_SERIAL);
+       major_copy_or_mark_from_roots (gc_thread_gray_queue, old_next_pin_slot, concurrent ? COPY_OR_MARK_FROM_ROOTS_START_CONCURRENT : COPY_OR_MARK_FROM_ROOTS_SERIAL, object_ops);
 }
 
 static void
-major_finish_collection (const char *reason, gboolean is_overflow, size_t old_next_pin_slot, gboolean forced)
+major_finish_collection (SgenGrayQueue *gc_thread_gray_queue, const char *reason, gboolean is_overflow, size_t old_next_pin_slot, gboolean forced)
 {
        ScannedObjectCounts counts;
        SgenObjectOperations *object_ops;
@@ -1937,9 +1945,7 @@ major_finish_collection (const char *reason, gboolean is_overflow, size_t old_ne
        if (concurrent_collection_in_progress) {
                object_ops = &major_collector.major_ops_concurrent_finish;
 
-               major_copy_or_mark_from_roots (NULL, COPY_OR_MARK_FROM_ROOTS_FINISH_CONCURRENT, object_ops);
-
-               major_finish_copy_or_mark (COPY_OR_MARK_FROM_ROOTS_FINISH_CONCURRENT);
+               major_copy_or_mark_from_roots (gc_thread_gray_queue, NULL, COPY_OR_MARK_FROM_ROOTS_FINISH_CONCURRENT, object_ops);
 
 #ifdef SGEN_DEBUG_INTERNAL_ALLOC
                main_gc_thread = NULL;
@@ -1948,10 +1954,9 @@ major_finish_collection (const char *reason, gboolean is_overflow, size_t old_ne
                object_ops = &major_collector.major_ops_serial;
        }
 
-       g_assert (sgen_section_gray_queue_is_empty (sgen_workers_get_distribute_section_gray_queue ()));
+       sgen_workers_assert_gray_queue_is_empty ();
 
-       /* all the objects in the heap */
-       finish_gray_stack (GENERATION_OLD, CONTEXT_FROM_OBJECT_OPERATIONS (object_ops, &gray_queue));
+       finish_gray_stack (GENERATION_OLD, CONTEXT_FROM_OBJECT_OPERATIONS (object_ops, gc_thread_gray_queue));
        TV_GETTIME (atv);
        time_major_finish_gray_stack += TV_ELAPSED (btv, atv);
 
@@ -2035,15 +2040,13 @@ major_finish_collection (const char *reason, gboolean is_overflow, size_t old_ne
                sgen_client_finalize_notify ();
        }
 
-       g_assert (sgen_gray_object_queue_is_empty (&gray_queue));
-
        sgen_memgov_major_collection_end (forced, concurrent_collection_in_progress, reason, is_overflow);
        current_collection_generation = -1;
 
        memset (&counts, 0, sizeof (ScannedObjectCounts));
        major_collector.finish_major_collection (&counts);
 
-       g_assert (sgen_section_gray_queue_is_empty (sgen_workers_get_distribute_section_gray_queue ()));
+       sgen_workers_assert_gray_queue_is_empty ();
 
        SGEN_ASSERT (0, sgen_workers_all_done (), "Can't have workers working after major collection has finished");
        if (concurrent_collection_in_progress)
@@ -2064,6 +2067,7 @@ major_do_collection (const char *reason, gboolean is_overflow, gboolean forced)
        TV_DECLARE (time_start);
        TV_DECLARE (time_end);
        size_t old_next_pin_slot;
+       SgenGrayQueue gc_thread_gray_queue;
 
        if (disable_major_collections)
                return FALSE;
@@ -2076,8 +2080,10 @@ major_do_collection (const char *reason, gboolean is_overflow, gboolean forced)
        /* world must be stopped already */
        TV_GETTIME (time_start);
 
-       major_start_collection (reason, FALSE, &old_next_pin_slot);
-       major_finish_collection (reason, is_overflow, old_next_pin_slot, forced);
+       init_gray_queue (&gc_thread_gray_queue, FALSE);
+       major_start_collection (&gc_thread_gray_queue, reason, FALSE, &old_next_pin_slot);
+       major_finish_collection (&gc_thread_gray_queue, reason, is_overflow, old_next_pin_slot, forced);
+       sgen_gray_object_queue_dispose (&gc_thread_gray_queue);
 
        TV_GETTIME (time_end);
        gc_stats.major_gc_time += TV_ELAPSED (time_start, time_end);
@@ -2095,6 +2101,7 @@ major_start_concurrent_collection (const char *reason)
        TV_DECLARE (time_start);
        TV_DECLARE (time_end);
        long long num_objects_marked;
+       SgenGrayQueue gc_thread_gray_queue;
 
        if (disable_major_collections)
                return;
@@ -2107,10 +2114,10 @@ major_start_concurrent_collection (const char *reason)
 
        binary_protocol_concurrent_start ();
 
+       init_gray_queue (&gc_thread_gray_queue, TRUE);
        // FIXME: store reason and pass it when finishing
-       major_start_collection (reason, TRUE, NULL);
-
-       gray_queue_redirect (&gray_queue);
+       major_start_collection (&gc_thread_gray_queue, reason, TRUE, NULL);
+       sgen_gray_object_queue_dispose (&gc_thread_gray_queue);
 
        num_objects_marked = major_collector.get_and_reset_num_major_objects_marked ();
 
@@ -2126,7 +2133,6 @@ major_start_concurrent_collection (const char *reason)
 static gboolean
 major_should_finish_concurrent_collection (void)
 {
-       SGEN_ASSERT (0, sgen_gray_object_queue_is_empty (&gray_queue), "Why is the gray queue not empty before we have started doing anything?");
        return sgen_workers_all_done ();
 }
 
@@ -2150,6 +2156,7 @@ major_update_concurrent_collection (void)
 static void
 major_finish_concurrent_collection (gboolean forced)
 {
+       SgenGrayQueue gc_thread_gray_queue;
        TV_DECLARE (total_start);
        TV_DECLARE (total_end);
 
@@ -2175,13 +2182,12 @@ major_finish_concurrent_collection (gboolean forced)
 
        current_collection_generation = GENERATION_OLD;
        sgen_cement_reset ();
-       major_finish_collection ("finishing", FALSE, -1, forced);
-
-       if (whole_heap_check_before_collection)
-               sgen_check_whole_heap (FALSE);
+       init_gray_queue (&gc_thread_gray_queue, FALSE);
+       major_finish_collection (&gc_thread_gray_queue, "finishing", FALSE, -1, forced);
+       sgen_gray_object_queue_dispose (&gc_thread_gray_queue);
 
        TV_GETTIME (total_end);
-       gc_stats.major_gc_time += TV_ELAPSED (total_start, total_end) - TV_ELAPSED (last_minor_collection_start_tv, last_minor_collection_end_tv);
+       gc_stats.major_gc_time += TV_ELAPSED (total_start, total_end);
 
        current_collection_generation = -1;
 }
@@ -2260,7 +2266,7 @@ sgen_perform_collection (size_t requested_size, int generation_to_collect, const
                if (concurrent_collection_in_progress)
                        major_update_concurrent_collection ();
 
-               if (collect_nursery (reason, FALSE, NULL, FALSE) && !concurrent_collection_in_progress) {
+               if (collect_nursery (reason, FALSE, NULL) && !concurrent_collection_in_progress) {
                        overflow_generation_to_collect = GENERATION_OLD;
                        overflow_reason = "Minor overflow";
                }
@@ -2270,7 +2276,7 @@ sgen_perform_collection (size_t requested_size, int generation_to_collect, const
        } else {
                SGEN_ASSERT (0, generation_to_collect == GENERATION_OLD, "We should have handled nursery collections above");
                if (major_collector.is_concurrent && !wait_to_finish) {
-                       collect_nursery ("Concurrent start", FALSE, NULL, FALSE);
+                       collect_nursery ("Concurrent start", FALSE, NULL);
                        major_start_concurrent_collection (reason);
                        oldest_generation_collected = GENERATION_NURSERY;
                } else if (major_do_collection (reason, FALSE, wait_to_finish)) {
@@ -2288,7 +2294,7 @@ sgen_perform_collection (size_t requested_size, int generation_to_collect, const
                 */
 
                if (overflow_generation_to_collect == GENERATION_NURSERY)
-                       collect_nursery (overflow_reason, TRUE, NULL, FALSE);
+                       collect_nursery (overflow_reason, TRUE, NULL);
                else
                        major_do_collection (overflow_reason, TRUE, wait_to_finish);
 
@@ -2305,8 +2311,6 @@ sgen_perform_collection (size_t requested_size, int generation_to_collect, const
                degraded_mode = 1;
        }
 
-       g_assert (sgen_gray_object_queue_is_empty (&gray_queue));
-
        TV_GETTIME (gc_total_end);
        time_max = MAX (time_max, TV_ELAPSED (gc_total_start, gc_total_end));
 
@@ -2408,6 +2412,13 @@ sgen_object_is_live (GCObject *obj)
  */
 
 static volatile gboolean pending_unqueued_finalizer = FALSE;
+volatile gboolean sgen_suspend_finalizers = FALSE;
+
+void
+sgen_set_suspend_finalizers (void)
+{
+       sgen_suspend_finalizers = TRUE;
+}
 
 int
 sgen_gc_invoke_finalizers (void)
@@ -2463,6 +2474,8 @@ sgen_gc_invoke_finalizers (void)
 gboolean
 sgen_have_pending_finalizers (void)
 {
+       if (sgen_suspend_finalizers)
+               return FALSE;
        return pending_unqueued_finalizer || !sgen_pointer_queue_is_empty (&fin_ready_queue) || !sgen_pointer_queue_is_empty (&critical_fin_queue);
 }
 
@@ -2541,11 +2554,7 @@ sgen_get_current_collection_generation (void)
 void*
 sgen_thread_register (SgenThreadInfo* info, void *stack_bottom_fallback)
 {
-#ifndef HAVE_KW_THREAD
        info->tlab_start = info->tlab_next = info->tlab_temp_end = info->tlab_real_end = NULL;
-#endif
-
-       sgen_init_tlab_info (info);
 
        sgen_client_thread_register (info, stack_bottom_fallback);
 
@@ -2746,6 +2755,8 @@ sgen_gc_init (void)
        char **opts, **ptr;
        char *major_collector_opt = NULL;
        char *minor_collector_opt = NULL;
+       char *params_opts = NULL;
+       char *debug_opts = NULL;
        size_t max_heap = 0;
        size_t soft_limit = 0;
        int result;
@@ -2783,8 +2794,12 @@ sgen_gc_init (void)
 
        mono_coop_mutex_init (&sgen_interruption_mutex);
 
-       if ((env = g_getenv (MONO_GC_PARAMS_NAME))) {
-               opts = g_strsplit (env, ",", -1);
+       if ((env = g_getenv (MONO_GC_PARAMS_NAME)) || gc_params_options) {
+               params_opts = g_strdup_printf ("%s,%s", gc_params_options ? gc_params_options : "", env ? env : "");
+       }
+
+       if (params_opts) {
+               opts = g_strsplit (params_opts, ",", -1);
                for (ptr = opts; *ptr; ++ptr) {
                        char *opt = *ptr;
                        if (g_str_has_prefix (opt, "major=")) {
@@ -2986,15 +3001,22 @@ sgen_gc_init (void)
        if (minor_collector_opt)
                g_free (minor_collector_opt);
 
+       if (params_opts)
+               g_free (params_opts);
+
        alloc_nursery ();
 
        sgen_pinning_init ();
        sgen_cement_init (cement_enabled);
 
-       if ((env = g_getenv (MONO_GC_DEBUG_NAME))) {
+       if ((env = g_getenv (MONO_GC_DEBUG_NAME)) || gc_debug_options) {
+               debug_opts = g_strdup_printf ("%s,%s", gc_debug_options ? gc_debug_options  : "", env ? env : "");
+       }
+
+       if (debug_opts) {
                gboolean usage_printed = FALSE;
 
-               opts = g_strsplit (env, ",", -1);
+               opts = g_strsplit (debug_opts, ",", -1);
                for (ptr = opts; ptr && *ptr; ptr ++) {
                        char *opt = *ptr;
                        if (!strcmp (opt, ""))
@@ -3031,8 +3053,8 @@ sgen_gc_init (void)
                                collect_before_allocs = atoi (arg);
                        } else if (!strcmp (opt, "verify-before-collections")) {
                                whole_heap_check_before_collection = TRUE;
-                       } else if (!strcmp (opt, "check-at-minor-collections")) {
-                               consistency_check_at_minor_collection = TRUE;
+                       } else if (!strcmp (opt, "check-remset-consistency")) {
+                               remset_consistency_checks = TRUE;
                                nursery_clear_policy = CLEAR_AT_GC;
                        } else if (!strcmp (opt, "mod-union-consistency-check")) {
                                if (!major_collector.is_concurrent) {
@@ -3098,7 +3120,7 @@ sgen_gc_init (void)
                                fprintf (stderr, "Valid <option>s are:\n");
                                fprintf (stderr, "  collect-before-allocs[=<n>]\n");
                                fprintf (stderr, "  verify-before-allocs[=<n>]\n");
-                               fprintf (stderr, "  check-at-minor-collections\n");
+                               fprintf (stderr, "  check-remset-consistency\n");
                                fprintf (stderr, "  check-mark-bits\n");
                                fprintf (stderr, "  check-nursery-pinned\n");
                                fprintf (stderr, "  verify-before-collections\n");
@@ -3125,6 +3147,9 @@ sgen_gc_init (void)
                g_strfreev (opts);
        }
 
+       if (debug_opts)
+               g_free (debug_opts);
+
        if (check_mark_bits_after_major_collection)
                nursery_clear_policy = CLEAR_AT_GC;
 
@@ -3142,9 +3167,15 @@ sgen_gc_init (void)
 
        sgen_register_root (NULL, 0, sgen_make_user_root_descriptor (sgen_mark_normal_gc_handles), ROOT_TYPE_NORMAL, MONO_ROOT_SOURCE_GC_HANDLE, "normal gc handles");
 
-       sgen_init_bridge_processor();
-
        gc_initialized = 1;
+
+       sgen_init_bridge ();
+}
+
+gboolean
+sgen_gc_initialized ()
+{
+       return gc_initialized > 0;
 }
 
 NurseryClearPolicy
@@ -3253,7 +3284,7 @@ sgen_check_whole_heap_stw (void)
 {
        sgen_stop_world (0);
        sgen_clear_nursery_fragments ();
-       sgen_check_whole_heap (FALSE);
+       sgen_check_whole_heap (TRUE);
        sgen_restart_world (0);
 }