[sgen] Implement work context for thread pool threads
[mono.git] / mono / sgen / sgen-gc.c
index 5213dda70ee2f457e478af3d58cc3862abec292c..a590153dd637f005f9bbfd55af8220908d4cbc3f 100644 (file)
@@ -236,6 +236,9 @@ static gboolean do_dump_nursery_content = FALSE;
 static gboolean enable_nursery_canaries = FALSE;
 
 static gboolean precleaning_enabled = TRUE;
+static gboolean dynamic_nursery = FALSE;
+static size_t min_nursery_size = 0;
+static size_t max_nursery_size = 0;
 
 #ifdef HEAVY_STATISTICS
 guint64 stat_objects_alloced_degraded = 0;
@@ -288,8 +291,8 @@ static guint64 time_major_fragment_creation = 0;
 
 static guint64 time_max = 0;
 
-static int sgen_max_pause_time = SGEN_MAX_PAUSE_TIME;
-static float sgen_max_pause_margin = SGEN_MAX_PAUSE_MARGIN;
+static int sgen_max_pause_time = SGEN_DEFAULT_MAX_PAUSE_TIME;
+static float sgen_max_pause_margin = SGEN_DEFAULT_MAX_PAUSE_MARGIN;
 
 static SGEN_TV_DECLARE (time_major_conc_collection_start);
 static SGEN_TV_DECLARE (time_major_conc_collection_end);
@@ -333,13 +336,32 @@ nursery_canaries_enabled (void)
 
 #if defined(HAVE_CONC_GC_AS_DEFAULT)
 /* Use concurrent major on deskstop platforms */
-#define DEFAULT_MAJOR_INIT sgen_marksweep_conc_init
-#define DEFAULT_MAJOR_NAME "marksweep-conc"
+#define DEFAULT_MAJOR SGEN_MAJOR_CONCURRENT
 #else
-#define DEFAULT_MAJOR_INIT sgen_marksweep_init
-#define DEFAULT_MAJOR_NAME "marksweep"
+#define DEFAULT_MAJOR SGEN_MAJOR_SERIAL
 #endif
 
+typedef enum {
+       SGEN_MAJOR_DEFAULT,
+       SGEN_MAJOR_SERIAL,
+       SGEN_MAJOR_CONCURRENT,
+       SGEN_MAJOR_CONCURRENT_PARALLEL
+} SgenMajor;
+
+typedef enum {
+       SGEN_MINOR_DEFAULT,
+       SGEN_MINOR_SIMPLE,
+       SGEN_MINOR_SIMPLE_PARALLEL,
+       SGEN_MINOR_SPLIT
+} SgenMinor;
+
+typedef enum {
+       SGEN_MODE_NONE,
+       SGEN_MODE_BALANCED,
+       SGEN_MODE_THROUGHPUT,
+       SGEN_MODE_PAUSE
+} SgenMode;
+
 /*
  * ######################################################################
  * ########  Global data.
@@ -432,7 +454,7 @@ sgen_workers_get_job_gray_queue (WorkerData *worker_data, SgenGrayQueue *default
 static void
 gray_queue_redirect (SgenGrayQueue *queue)
 {
-       sgen_workers_take_from_queue (queue);
+       sgen_workers_take_from_queue (current_collection_generation, queue);
 }
 
 void
@@ -1338,7 +1360,7 @@ typedef struct {
 
 typedef struct {
        ScanJob scan_job;
-       int job_index;
+       int job_index, job_split_count;
 } ParallelScanJob;
 
 static ScanCopyContext
@@ -1353,7 +1375,7 @@ scan_copy_context_for_scan_job (void *worker_data_untyped, ScanJob *job)
                 * object ops changes, like forced concurrent finish.
                 */
                SGEN_ASSERT (0, sgen_workers_is_worker_thread (mono_native_thread_id_get ()), "We need a context for the scan job");
-               job->ops = sgen_workers_get_idle_func_object_ops ();
+               job->ops = sgen_workers_get_idle_func_object_ops (worker_data);
        }
 
        return CONTEXT_FROM_OBJECT_OPERATIONS (job->ops, sgen_workers_get_job_gray_queue (worker_data, job->gc_thread_gray_queue));
@@ -1422,7 +1444,7 @@ job_scan_major_card_table (void *worker_data_untyped, SgenThreadPoolJob *job)
        ScanCopyContext ctx = scan_copy_context_for_scan_job (worker_data_untyped, (ScanJob*)job_data);
 
        SGEN_TV_GETTIME (atv);
-       major_collector.scan_card_table (CARDTABLE_SCAN_GLOBAL, ctx, job_data->job_index, sgen_workers_get_job_split_count ());
+       major_collector.scan_card_table (CARDTABLE_SCAN_GLOBAL, ctx, job_data->job_index, job_data->job_split_count);
        SGEN_TV_GETTIME (btv);
        time_minor_scan_major_blocks += SGEN_TV_ELAPSED (atv, btv);
 }
@@ -1436,7 +1458,7 @@ job_scan_los_card_table (void *worker_data_untyped, SgenThreadPoolJob *job)
        ScanCopyContext ctx = scan_copy_context_for_scan_job (worker_data_untyped, (ScanJob*)job_data);
 
        SGEN_TV_GETTIME (atv);
-       sgen_los_scan_card_table (CARDTABLE_SCAN_GLOBAL, ctx, job_data->job_index, sgen_workers_get_job_split_count ());
+       sgen_los_scan_card_table (CARDTABLE_SCAN_GLOBAL, ctx, job_data->job_index, job_data->job_split_count);
        SGEN_TV_GETTIME (btv);
        time_minor_scan_los += SGEN_TV_ELAPSED (atv, btv);
 }
@@ -1448,7 +1470,7 @@ job_scan_major_mod_union_card_table (void *worker_data_untyped, SgenThreadPoolJo
        ScanCopyContext ctx = scan_copy_context_for_scan_job (worker_data_untyped, (ScanJob*)job_data);
 
        g_assert (concurrent_collection_in_progress);
-       major_collector.scan_card_table (CARDTABLE_SCAN_MOD_UNION, ctx, job_data->job_index, sgen_workers_get_job_split_count ());
+       major_collector.scan_card_table (CARDTABLE_SCAN_MOD_UNION, ctx, job_data->job_index, job_data->job_split_count);
 }
 
 static void
@@ -1458,7 +1480,7 @@ job_scan_los_mod_union_card_table (void *worker_data_untyped, SgenThreadPoolJob
        ScanCopyContext ctx = scan_copy_context_for_scan_job (worker_data_untyped, (ScanJob*)job_data);
 
        g_assert (concurrent_collection_in_progress);
-       sgen_los_scan_card_table (CARDTABLE_SCAN_MOD_UNION, ctx, job_data->job_index, sgen_workers_get_job_split_count ());
+       sgen_los_scan_card_table (CARDTABLE_SCAN_MOD_UNION, ctx, job_data->job_index, job_data->job_split_count);
 }
 
 static void
@@ -1469,7 +1491,7 @@ job_major_mod_union_preclean (void *worker_data_untyped, SgenThreadPoolJob *job)
 
        g_assert (concurrent_collection_in_progress);
 
-       major_collector.scan_card_table (CARDTABLE_SCAN_MOD_UNION_PRECLEAN, ctx, job_data->job_index, sgen_workers_get_job_split_count ());
+       major_collector.scan_card_table (CARDTABLE_SCAN_MOD_UNION_PRECLEAN, ctx, job_data->job_index, job_data->job_split_count);
 }
 
 static void
@@ -1480,7 +1502,7 @@ job_los_mod_union_preclean (void *worker_data_untyped, SgenThreadPoolJob *job)
 
        g_assert (concurrent_collection_in_progress);
 
-       sgen_los_scan_card_table (CARDTABLE_SCAN_MOD_UNION_PRECLEAN, ctx, job_data->job_index, sgen_workers_get_job_split_count ());
+       sgen_los_scan_card_table (CARDTABLE_SCAN_MOD_UNION_PRECLEAN, ctx, job_data->job_index, job_data->job_split_count);
 }
 
 static void
@@ -1499,46 +1521,46 @@ workers_finish_callback (void)
 {
        ParallelScanJob *psj;
        ScanJob *sj;
-       int split_count = sgen_workers_get_job_split_count ();
+       int split_count = sgen_workers_get_job_split_count (GENERATION_OLD);
        int i;
        /* Mod union preclean jobs */
        for (i = 0; i < split_count; i++) {
                psj = (ParallelScanJob*)sgen_thread_pool_job_alloc ("preclean major mod union cardtable", job_major_mod_union_preclean, sizeof (ParallelScanJob));
                psj->scan_job.gc_thread_gray_queue = NULL;
                psj->job_index = i;
-               sgen_workers_enqueue_job (&psj->scan_job.job, TRUE);
+               psj->job_split_count = split_count;
+               sgen_workers_enqueue_job (GENERATION_OLD, &psj->scan_job.job, TRUE);
        }
 
        for (i = 0; i < split_count; i++) {
                psj = (ParallelScanJob*)sgen_thread_pool_job_alloc ("preclean los mod union cardtable", job_los_mod_union_preclean, sizeof (ParallelScanJob));
                psj->scan_job.gc_thread_gray_queue = NULL;
                psj->job_index = i;
-               sgen_workers_enqueue_job (&psj->scan_job.job, TRUE);
+               psj->job_split_count = split_count;
+               sgen_workers_enqueue_job (GENERATION_OLD, &psj->scan_job.job, TRUE);
        }
 
        sj = (ScanJob*)sgen_thread_pool_job_alloc ("scan last pinned", job_scan_last_pinned, sizeof (ScanJob));
        sj->gc_thread_gray_queue = NULL;
-       sgen_workers_enqueue_job (&sj->job, TRUE);
+       sgen_workers_enqueue_job (GENERATION_OLD, &sj->job, TRUE);
 }
 
 static void
-init_gray_queue (SgenGrayQueue *gc_thread_gray_queue, gboolean use_workers)
+init_gray_queue (SgenGrayQueue *gc_thread_gray_queue)
 {
-       if (use_workers)
-               sgen_workers_init_distribute_gray_queue ();
        sgen_gray_object_queue_init (gc_thread_gray_queue, NULL, TRUE);
 }
 
 static void
 enqueue_scan_remembered_set_jobs (SgenGrayQueue *gc_thread_gray_queue, SgenObjectOperations *ops, gboolean enqueue)
 {
-       int i, split_count = sgen_workers_get_job_split_count ();
+       int i, split_count = sgen_workers_get_job_split_count (GENERATION_NURSERY);
        ScanJob *sj;
 
        sj = (ScanJob*)sgen_thread_pool_job_alloc ("scan wbroots", job_scan_wbroots, sizeof (ScanJob));
        sj->ops = ops;
        sj->gc_thread_gray_queue = gc_thread_gray_queue;
-       sgen_workers_enqueue_job (&sj->job, enqueue);
+       sgen_workers_enqueue_job (GENERATION_NURSERY, &sj->job, enqueue);
 
        for (i = 0; i < split_count; i++) {
                ParallelScanJob *psj;
@@ -1547,13 +1569,15 @@ enqueue_scan_remembered_set_jobs (SgenGrayQueue *gc_thread_gray_queue, SgenObjec
                psj->scan_job.ops = ops;
                psj->scan_job.gc_thread_gray_queue = gc_thread_gray_queue;
                psj->job_index = i;
-               sgen_workers_enqueue_job (&psj->scan_job.job, enqueue);
+               psj->job_split_count = split_count;
+               sgen_workers_enqueue_job (GENERATION_NURSERY, &psj->scan_job.job, enqueue);
 
                psj = (ParallelScanJob*)sgen_thread_pool_job_alloc ("scan LOS remsets", job_scan_los_card_table, sizeof (ParallelScanJob));
                psj->scan_job.ops = ops;
                psj->scan_job.gc_thread_gray_queue = gc_thread_gray_queue;
                psj->job_index = i;
-               sgen_workers_enqueue_job (&psj->scan_job.job, enqueue);
+               psj->job_split_count = split_count;
+               sgen_workers_enqueue_job (GENERATION_NURSERY, &psj->scan_job.job, enqueue);
        }
 }
 
@@ -1572,7 +1596,7 @@ enqueue_scan_from_roots_jobs (SgenGrayQueue *gc_thread_gray_queue, char *heap_st
        scrrj->heap_start = heap_start;
        scrrj->heap_end = heap_end;
        scrrj->root_type = ROOT_TYPE_NORMAL;
-       sgen_workers_enqueue_job (&scrrj->scan_job.job, enqueue);
+       sgen_workers_enqueue_job (current_collection_generation, &scrrj->scan_job.job, enqueue);
 
        if (current_collection_generation == GENERATION_OLD) {
                /* During minors we scan the cardtable for these roots instead */
@@ -1582,7 +1606,7 @@ enqueue_scan_from_roots_jobs (SgenGrayQueue *gc_thread_gray_queue, char *heap_st
                scrrj->heap_start = heap_start;
                scrrj->heap_end = heap_end;
                scrrj->root_type = ROOT_TYPE_WBARRIER;
-               sgen_workers_enqueue_job (&scrrj->scan_job.job, enqueue);
+               sgen_workers_enqueue_job (current_collection_generation, &scrrj->scan_job.job, enqueue);
        }
 
        /* Threads */
@@ -1592,7 +1616,7 @@ enqueue_scan_from_roots_jobs (SgenGrayQueue *gc_thread_gray_queue, char *heap_st
        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->scan_job.job, enqueue);
+       sgen_workers_enqueue_job (current_collection_generation, &stdj->scan_job.job, enqueue);
 
        /* Scan the list of objects ready for finalization. */
 
@@ -1600,13 +1624,13 @@ enqueue_scan_from_roots_jobs (SgenGrayQueue *gc_thread_gray_queue, char *heap_st
        sfej->scan_job.ops = ops;
        sfej->scan_job.gc_thread_gray_queue = gc_thread_gray_queue;
        sfej->queue = &fin_ready_queue;
-       sgen_workers_enqueue_job (&sfej->scan_job.job, enqueue);
+       sgen_workers_enqueue_job (current_collection_generation, &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;
-       sgen_workers_enqueue_job (&sfej->scan_job.job, enqueue);
+       sgen_workers_enqueue_job (current_collection_generation, &sfej->scan_job.job, enqueue);
 }
 
 /*
@@ -1622,7 +1646,6 @@ collect_nursery (const char *reason, gboolean is_overflow, SgenGrayQueue *unpin_
        SgenGrayQueue gc_thread_gray_queue;
        SgenObjectOperations *object_ops_nopar, *object_ops_par = NULL;
        ScanCopyContext ctx;
-       int duration;
        TV_DECLARE (atv);
        TV_DECLARE (btv);
        SGEN_TV_DECLARE (last_minor_collection_start_tv);
@@ -1675,7 +1698,7 @@ collect_nursery (const char *reason, gboolean is_overflow, SgenGrayQueue *unpin_
 
        sgen_memgov_minor_collection_start ();
 
-       init_gray_queue (&gc_thread_gray_queue, is_parallel);
+       init_gray_queue (&gc_thread_gray_queue);
        ctx = CONTEXT_FROM_OBJECT_OPERATIONS (object_ops_nopar, &gc_thread_gray_queue);
 
        gc_stats.minor_gc_count ++;
@@ -1684,6 +1707,8 @@ collect_nursery (const char *reason, gboolean is_overflow, SgenGrayQueue *unpin_
 
        /* pin from pinned handles */
        sgen_init_pinning ();
+       if (concurrent_collection_in_progress)
+               sgen_init_pinning_for_conc ();
        sgen_client_binary_protocol_mark_start (GENERATION_NURSERY);
        pin_from_roots (nursery_section->data, nursery_section->end_data, ctx);
        /* pin cemented objects */
@@ -1694,6 +1719,8 @@ 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 (concurrent_collection_in_progress)
+               sgen_finish_pinning_for_conc ();
 
        if (remset_consistency_checks)
                sgen_check_remset_consistency ();
@@ -1729,8 +1756,8 @@ collect_nursery (const char *reason, gboolean is_overflow, SgenGrayQueue *unpin_
 
        if (is_parallel) {
                gray_queue_redirect (&gc_thread_gray_queue);
-               sgen_workers_start_all_workers (object_ops_nopar, object_ops_par, NULL);
-               sgen_workers_join ();
+               sgen_workers_start_all_workers (GENERATION_NURSERY, object_ops_nopar, object_ops_par, NULL);
+               sgen_workers_join (GENERATION_NURSERY);
        }
 
        TV_GETTIME (btv);
@@ -1755,12 +1782,18 @@ collect_nursery (const char *reason, gboolean is_overflow, SgenGrayQueue *unpin_
                sgen_check_remset_consistency ();
 
 
-       TV_GETTIME (btv);
-       duration = (int)(TV_ELAPSED (last_minor_collection_start_tv, btv) / 10000);
-       if (duration > (sgen_max_pause_time * sgen_max_pause_margin))
-               sgen_resize_nursery (TRUE);
-       else
-               sgen_resize_nursery (FALSE);
+       if (sgen_max_pause_time) {
+               int duration;
+
+               TV_GETTIME (btv);
+               duration = (int)(TV_ELAPSED (last_minor_collection_start_tv, btv) / 10000);
+               if (duration > (sgen_max_pause_time * sgen_max_pause_margin))
+                       sgen_resize_nursery (TRUE);
+               else
+                       sgen_resize_nursery (FALSE);
+       } else {
+                       sgen_resize_nursery (FALSE);
+       }
 
        /* 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
@@ -1877,6 +1910,8 @@ major_copy_or_mark_from_roots (SgenGrayQueue *gc_thread_gray_queue, size_t *old_
 
        TV_GETTIME (atv);
        sgen_init_pinning ();
+       if (mode == COPY_OR_MARK_FROM_ROOTS_START_CONCURRENT)
+               sgen_init_pinning_for_conc ();
        SGEN_LOG (6, "Collecting pinned addresses");
        pin_from_roots ((void*)lowest_heap_address, (void*)highest_heap_address, ctx);
        if (mode == COPY_OR_MARK_FROM_ROOTS_FINISH_CONCURRENT) {
@@ -1947,20 +1982,23 @@ major_copy_or_mark_from_roots (SgenGrayQueue *gc_thread_gray_queue, size_t *old_
        SGEN_LOG (2, "Finding pinned pointers: %zd in %lld usecs", sgen_get_pinned_count (), (long long)TV_ELAPSED (atv, btv));
        SGEN_LOG (4, "Start scan with %zd pinned objects", sgen_get_pinned_count ());
 
+       if (mode == COPY_OR_MARK_FROM_ROOTS_START_CONCURRENT)
+               sgen_finish_pinning_for_conc ();
+
        major_collector.init_to_space ();
 
        SGEN_ASSERT (0, sgen_workers_all_done (), "Why are the workers not done when we start or finish a major collection?");
        if (mode == COPY_OR_MARK_FROM_ROOTS_FINISH_CONCURRENT) {
                if (object_ops_par != NULL)
-                       sgen_workers_set_num_active_workers (0);
-               if (sgen_workers_have_idle_work ()) {
+                       sgen_workers_set_num_active_workers (GENERATION_OLD, 0);
+               if (sgen_workers_have_idle_work (GENERATION_OLD)) {
                        /*
                         * We force the finish of the worker with the new object ops context
                         * which can also do copying. We need to have finished pinning.
                         */
-                       sgen_workers_start_all_workers (object_ops_nopar, object_ops_par, NULL);
+                       sgen_workers_start_all_workers (GENERATION_OLD, object_ops_nopar, object_ops_par, NULL);
 
-                       sgen_workers_join ();
+                       sgen_workers_join (GENERATION_OLD);
                }
        }
 
@@ -1986,17 +2024,17 @@ major_copy_or_mark_from_roots (SgenGrayQueue *gc_thread_gray_queue, size_t *old_
         * the roots.
         */
        if (mode == COPY_OR_MARK_FROM_ROOTS_START_CONCURRENT) {
-               sgen_workers_set_num_active_workers (1);
+               sgen_workers_set_num_active_workers (GENERATION_OLD, 1);
                gray_queue_redirect (gc_thread_gray_queue);
                if (precleaning_enabled) {
-                       sgen_workers_start_all_workers (object_ops_nopar, object_ops_par, workers_finish_callback);
+                       sgen_workers_start_all_workers (GENERATION_OLD, object_ops_nopar, object_ops_par, workers_finish_callback);
                } else {
-                       sgen_workers_start_all_workers (object_ops_nopar, object_ops_par, NULL);
+                       sgen_workers_start_all_workers (GENERATION_OLD, object_ops_nopar, object_ops_par, NULL);
                }
        }
 
        if (mode == COPY_OR_MARK_FROM_ROOTS_FINISH_CONCURRENT) {
-               int i, split_count = sgen_workers_get_job_split_count ();
+               int i, split_count = sgen_workers_get_job_split_count (GENERATION_OLD);
                gboolean parallel = object_ops_par != NULL;
 
                /* If we're not parallel we finish the collection on the gc thread */
@@ -2011,13 +2049,15 @@ major_copy_or_mark_from_roots (SgenGrayQueue *gc_thread_gray_queue, size_t *old_
                        psj->scan_job.ops = parallel ? NULL : object_ops_nopar;
                        psj->scan_job.gc_thread_gray_queue = gc_thread_gray_queue;
                        psj->job_index = i;
-                       sgen_workers_enqueue_job (&psj->scan_job.job, parallel);
+                       psj->job_split_count = split_count;
+                       sgen_workers_enqueue_job (GENERATION_OLD, &psj->scan_job.job, parallel);
 
                        psj = (ParallelScanJob*)sgen_thread_pool_job_alloc ("scan LOS mod union cardtable", job_scan_los_mod_union_card_table, sizeof (ParallelScanJob));
                        psj->scan_job.ops = parallel ? NULL : object_ops_nopar;
                        psj->scan_job.gc_thread_gray_queue = gc_thread_gray_queue;
                        psj->job_index = i;
-                       sgen_workers_enqueue_job (&psj->scan_job.job, parallel);
+                       psj->job_split_count = split_count;
+                       sgen_workers_enqueue_job (GENERATION_OLD, &psj->scan_job.job, parallel);
                }
 
                if (parallel) {
@@ -2029,8 +2069,8 @@ major_copy_or_mark_from_roots (SgenGrayQueue *gc_thread_gray_queue, size_t *old_
                         * stack that contained roots and pinned objects and also scan the mod union card
                         * table.
                         */
-                       sgen_workers_start_all_workers (object_ops_nopar, object_ops_par, NULL);
-                       sgen_workers_join ();
+                       sgen_workers_start_all_workers (GENERATION_OLD, object_ops_nopar, object_ops_par, NULL);
+                       sgen_workers_join (GENERATION_OLD);
                }
        }
 
@@ -2055,7 +2095,7 @@ major_start_collection (SgenGrayQueue *gc_thread_gray_queue, const char *reason,
 
        current_collection_generation = GENERATION_OLD;
 
-       sgen_workers_assert_gray_queue_is_empty ();
+       sgen_workers_assert_gray_queue_is_empty (GENERATION_OLD);
 
        if (!concurrent)
                sgen_cement_reset ();
@@ -2118,7 +2158,7 @@ major_finish_collection (SgenGrayQueue *gc_thread_gray_queue, const char *reason
                object_ops_nopar = &major_collector.major_ops_serial;
        }
 
-       sgen_workers_assert_gray_queue_is_empty ();
+       sgen_workers_assert_gray_queue_is_empty (GENERATION_OLD);
 
        finish_gray_stack (GENERATION_OLD, CONTEXT_FROM_OBJECT_OPERATIONS (object_ops_nopar, gc_thread_gray_queue));
        TV_GETTIME (atv);
@@ -2210,7 +2250,7 @@ major_finish_collection (SgenGrayQueue *gc_thread_gray_queue, const char *reason
        memset (&counts, 0, sizeof (ScannedObjectCounts));
        major_collector.finish_major_collection (&counts);
 
-       sgen_workers_assert_gray_queue_is_empty ();
+       sgen_workers_assert_gray_queue_is_empty (GENERATION_OLD);
 
        SGEN_ASSERT (0, sgen_workers_all_done (), "Can't have workers working after major collection has finished");
        if (concurrent_collection_in_progress)
@@ -2244,7 +2284,7 @@ major_do_collection (const char *reason, gboolean is_overflow, gboolean forced)
        /* world must be stopped already */
        TV_GETTIME (time_start);
 
-       init_gray_queue (&gc_thread_gray_queue, FALSE);
+       init_gray_queue (&gc_thread_gray_queue);
        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);
@@ -2278,7 +2318,7 @@ major_start_concurrent_collection (const char *reason)
 
        binary_protocol_concurrent_start ();
 
-       init_gray_queue (&gc_thread_gray_queue, TRUE);
+       init_gray_queue (&gc_thread_gray_queue);
        // FIXME: store reason and pass it when finishing
        major_start_collection (&gc_thread_gray_queue, reason, TRUE, NULL);
        sgen_gray_object_queue_dispose (&gc_thread_gray_queue);
@@ -2333,7 +2373,7 @@ major_finish_concurrent_collection (gboolean forced)
         * The workers will be resumed with a finishing pause context to avoid
         * additional cardtable and object scanning.
         */
-       sgen_workers_stop_all_workers ();
+       sgen_workers_stop_all_workers (GENERATION_OLD);
 
        SGEN_TV_GETTIME (time_major_conc_collection_end);
        gc_stats.major_gc_time_concurrent += SGEN_TV_ELAPSED (time_major_conc_collection_start, time_major_conc_collection_end);
@@ -2346,7 +2386,7 @@ major_finish_concurrent_collection (gboolean forced)
 
        current_collection_generation = GENERATION_OLD;
        sgen_cement_reset ();
-       init_gray_queue (&gc_thread_gray_queue, FALSE);
+       init_gray_queue (&gc_thread_gray_queue);
        major_finish_collection (&gc_thread_gray_queue, "finishing", FALSE, -1, forced);
        sgen_gray_object_queue_dispose (&gc_thread_gray_queue);
 
@@ -2804,19 +2844,19 @@ sgen_get_current_collection_generation (void)
 }
 
 void*
-sgen_thread_register (SgenThreadInfo* info, void *stack_bottom_fallback)
+sgen_thread_attach (SgenThreadInfo* info)
 {
        info->tlab_start = info->tlab_next = info->tlab_temp_end = info->tlab_real_end = NULL;
 
-       sgen_client_thread_register (info, stack_bottom_fallback);
+       sgen_client_thread_attach (info);
 
        return info;
 }
 
 void
-sgen_thread_unregister (SgenThreadInfo *p)
+sgen_thread_detach_with_lock (SgenThreadInfo *p)
 {
-       sgen_client_thread_unregister (p);
+       sgen_client_thread_detach_with_lock (p);
 }
 
 /*
@@ -2999,13 +3039,162 @@ parse_double_in_interval (const char *env_var, const char *opt_name, const char
        return TRUE;
 }
 
+static SgenMinor
+parse_sgen_minor (const char *opt)
+{
+       if (!opt)
+               return SGEN_MINOR_DEFAULT;
+
+       if (!strcmp (opt, "simple")) {
+               return SGEN_MINOR_SIMPLE;
+       } else if (!strcmp (opt, "simple-par")) {
+               return SGEN_MINOR_SIMPLE_PARALLEL;
+       } else if (!strcmp (opt, "split")) {
+               return SGEN_MINOR_SPLIT;
+       } else {
+               sgen_env_var_error (MONO_GC_PARAMS_NAME, "Using default instead.", "Unknown minor collector `%s'.", opt);
+               return SGEN_MINOR_DEFAULT;
+       }
+}
+
+static SgenMajor
+parse_sgen_major (const char *opt)
+{
+       if (!opt)
+               return SGEN_MAJOR_DEFAULT;
+
+       if (!strcmp (opt, "marksweep")) {
+               return SGEN_MAJOR_SERIAL;
+       } else if (!strcmp (opt, "marksweep-conc")) {
+               return SGEN_MAJOR_CONCURRENT;
+       } else if (!strcmp (opt, "marksweep-conc-par")) {
+               return SGEN_MAJOR_CONCURRENT_PARALLEL;
+       } else {
+               sgen_env_var_error (MONO_GC_PARAMS_NAME, "Using default instead.", "Unknown major collector `%s'.", opt);
+               return SGEN_MAJOR_DEFAULT;
+       }
+
+}
+
+static SgenMode
+parse_sgen_mode (const char *opt)
+{
+       if (!opt)
+               return SGEN_MODE_NONE;
+
+       if (!strcmp (opt, "balanced")) {
+               return SGEN_MODE_BALANCED;
+       } else if (!strcmp (opt, "throughput")) {
+               return SGEN_MODE_THROUGHPUT;
+       } else if (!strcmp (opt, "pause") || g_str_has_prefix (opt, "pause:")) {
+               return SGEN_MODE_PAUSE;
+       } else {
+               sgen_env_var_error (MONO_GC_PARAMS_NAME, "Using default configurations.", "Unknown mode `%s'.", opt);
+               return SGEN_MODE_NONE;
+       }
+}
+
+static void
+init_sgen_minor (SgenMinor minor)
+{
+       switch (minor) {
+       case SGEN_MINOR_DEFAULT:
+       case SGEN_MINOR_SIMPLE:
+               sgen_simple_nursery_init (&sgen_minor_collector, FALSE);
+               break;
+       case SGEN_MINOR_SIMPLE_PARALLEL:
+               sgen_simple_nursery_init (&sgen_minor_collector, TRUE);
+               break;
+       case SGEN_MINOR_SPLIT:
+               sgen_split_nursery_init (&sgen_minor_collector);
+               break;
+       default:
+               g_assert_not_reached ();
+       }
+}
+
+static void
+init_sgen_major (SgenMajor major)
+{
+       if (major == SGEN_MAJOR_DEFAULT)
+               major = DEFAULT_MAJOR;
+
+       switch (major) {
+       case SGEN_MAJOR_SERIAL:
+               sgen_marksweep_init (&major_collector);
+               break;
+       case SGEN_MAJOR_CONCURRENT:
+               sgen_marksweep_conc_init (&major_collector);
+               break;
+       case SGEN_MAJOR_CONCURRENT_PARALLEL:
+               sgen_marksweep_conc_par_init (&major_collector);
+               break;
+       default:
+               g_assert_not_reached ();
+       }
+}
+
+/*
+ * If sgen mode is set, major/minor configuration is fixed. The other gc_params
+ * are parsed and processed after major/minor initialization, so it can potentially
+ * override some knobs set by the sgen mode. We can consider locking out additional
+ * configurations when gc_modes are used.
+ */
+static void
+init_sgen_mode (SgenMode mode)
+{
+       SgenMinor minor = SGEN_MINOR_DEFAULT;
+       SgenMajor major = SGEN_MAJOR_DEFAULT;
+
+       switch (mode) {
+       case SGEN_MODE_BALANCED:
+               /*
+                * Use a dynamic parallel nursery with a major concurrent collector.
+                * This uses the default values for max pause time and nursery size.
+                */
+               minor = SGEN_MINOR_SIMPLE;
+               major = SGEN_MAJOR_CONCURRENT;
+               dynamic_nursery = TRUE;
+               break;
+       case SGEN_MODE_THROUGHPUT:
+               /*
+                * Use concurrent major to let the mutator do more work. Use a larger
+                * nursery, without pause time constraints, in order to collect more
+                * objects in parallel and avoid repetitive collection tasks (pinning,
+                * root scanning etc)
+                */
+               minor = SGEN_MINOR_SIMPLE_PARALLEL;
+               major = SGEN_MAJOR_CONCURRENT;
+               dynamic_nursery = TRUE;
+               sgen_max_pause_time = 0;
+               break;
+       case SGEN_MODE_PAUSE:
+               /*
+                * Use concurrent major and dynamic nursery with a more
+                * aggressive shrinking relative to pause times.
+                * FIXME use parallel minors
+                */
+               minor = SGEN_MINOR_SIMPLE;
+               major = SGEN_MAJOR_CONCURRENT;
+               dynamic_nursery = TRUE;
+               sgen_max_pause_margin = SGEN_PAUSE_MODE_MAX_PAUSE_MARGIN;
+               break;
+       default:
+               g_assert_not_reached ();
+       }
+
+       init_sgen_minor (minor);
+       init_sgen_major (major);
+}
+
 void
 sgen_gc_init (void)
 {
        char *env;
        char **opts, **ptr;
-       char *major_collector_opt = NULL;
-       char *minor_collector_opt = NULL;
+       SgenMajor sgen_major = SGEN_MAJOR_DEFAULT;
+       SgenMinor sgen_minor = SGEN_MINOR_DEFAULT;
+       SgenMode sgen_mode = SGEN_MODE_NONE;
        char *params_opts = NULL;
        char *debug_opts = NULL;
        size_t max_heap = 0;
@@ -3014,8 +3203,6 @@ sgen_gc_init (void)
        gboolean debug_print_allowance = FALSE;
        double allowance_ratio = 0, save_target = 0;
        gboolean cement_enabled = TRUE;
-       gboolean dynamic_nursery = FALSE;
-       size_t min_nursery_size = 0, max_nursery_size = 0;
 
        do {
                result = InterlockedCompareExchange (&gc_initialized, -1, 0);
@@ -3058,10 +3245,13 @@ sgen_gc_init (void)
                        char *opt = *ptr;
                        if (g_str_has_prefix (opt, "major=")) {
                                opt = strchr (opt, '=') + 1;
-                               major_collector_opt = g_strdup (opt);
+                               sgen_major = parse_sgen_major (opt);
                        } else if (g_str_has_prefix (opt, "minor=")) {
                                opt = strchr (opt, '=') + 1;
-                               minor_collector_opt = g_strdup (opt);
+                               sgen_minor = parse_sgen_minor (opt);
+                       } else if (g_str_has_prefix (opt, "mode=")) {
+                               opt = strchr (opt, '=') + 1;
+                               sgen_mode = parse_sgen_mode (opt);
                        }
                }
        } else {
@@ -3083,34 +3273,13 @@ sgen_gc_init (void)
 
        sgen_client_init ();
 
-       if (!minor_collector_opt) {
-               sgen_simple_nursery_init (&sgen_minor_collector, FALSE);
-       } else {
-               if (!strcmp (minor_collector_opt, "simple")) {
-               use_simple_nursery:
-                       sgen_simple_nursery_init (&sgen_minor_collector, FALSE);
-               } else if (!strcmp (minor_collector_opt, "simple-par")) {
-                       sgen_simple_nursery_init (&sgen_minor_collector, TRUE);
-               } else if (!strcmp (minor_collector_opt, "split")) {
-                       sgen_split_nursery_init (&sgen_minor_collector);
-               } else {
-                       sgen_env_var_error (MONO_GC_PARAMS_NAME, "Using `simple` instead.", "Unknown minor collector `%s'.", minor_collector_opt);
-                       goto use_simple_nursery;
-               }
-       }
-
-       if (!major_collector_opt) {
-       use_default_major:
-               DEFAULT_MAJOR_INIT (&major_collector);
-       } else if (!strcmp (major_collector_opt, "marksweep")) {
-               sgen_marksweep_init (&major_collector);
-       } else if (!strcmp (major_collector_opt, "marksweep-conc")) {
-               sgen_marksweep_conc_init (&major_collector);
-       } else if (!strcmp (major_collector_opt, "marksweep-conc-par")) {
-               sgen_marksweep_conc_par_init (&major_collector);
+       if (sgen_mode != SGEN_MODE_NONE) {
+               if (sgen_minor != SGEN_MINOR_DEFAULT || sgen_major != SGEN_MAJOR_DEFAULT)
+                       sgen_env_var_error (MONO_GC_PARAMS_NAME, "Ignoring major/minor configuration", "Major/minor configurations cannot be used with sgen modes");
+               init_sgen_mode (sgen_mode);
        } else {
-               sgen_env_var_error (MONO_GC_PARAMS_NAME, "Using `" DEFAULT_MAJOR_NAME "` instead.", "Unknown major collector `%s'.", major_collector_opt);
-               goto use_default_major;
+               init_sgen_minor (sgen_minor);
+               init_sgen_major (sgen_major);
        }
 
        if (opts) {
@@ -3124,6 +3293,17 @@ sgen_gc_init (void)
                                continue;
                        if (g_str_has_prefix (opt, "minor="))
                                continue;
+                       if (g_str_has_prefix (opt, "mode=")) {
+                               if (g_str_has_prefix (opt, "mode=pause:")) {
+                                       char *str_pause = strchr (opt, ':') + 1;
+                                       int pause = atoi (str_pause);
+                                       if (pause)
+                                               sgen_max_pause_time = pause;
+                                       else
+                                               sgen_env_var_error (MONO_GC_PARAMS_NAME, "Using default", "Invalid maximum pause time for `pause` sgen mode");
+                               }
+                               continue;
+                       }
                        if (g_str_has_prefix (opt, "max-heap-size=")) {
                                size_t page_size = mono_pagesize ();
                                size_t max_heap_candidate = 0;
@@ -3239,6 +3419,7 @@ sgen_gc_init (void)
                        fprintf (stderr, "\n%s must be a comma-delimited list of one or more of the following:\n", MONO_GC_PARAMS_NAME);
                        fprintf (stderr, "  max-heap-size=N (where N is an integer, possibly with a k, m or a g suffix)\n");
                        fprintf (stderr, "  soft-heap-limit=n (where N is an integer, possibly with a k, m or a g suffix)\n");
+                       fprintf (stderr, "  mode=MODE (where MODE is 'balanced', 'throughput' or 'pause[:N]' and N is maximum pause in milliseconds)\n");
                        fprintf (stderr, "  nursery-size=N (where N is an integer, possibly with a k, m or a g suffix)\n");
                        fprintf (stderr, "  major=COLLECTOR (where COLLECTOR is `marksweep', `marksweep-conc', `marksweep-par')\n");
                        fprintf (stderr, "  minor=COLLECTOR (where COLLECTOR is `simple' or `split')\n");
@@ -3260,12 +3441,6 @@ sgen_gc_init (void)
                g_strfreev (opts);
        }
 
-       if (major_collector_opt)
-               g_free (major_collector_opt);
-
-       if (minor_collector_opt)
-               g_free (minor_collector_opt);
-
        if (params_opts)
                g_free (params_opts);
 
@@ -3432,19 +3607,7 @@ sgen_gc_init (void)
        if (major_collector.post_param_init)
                major_collector.post_param_init (&major_collector);
 
-       if (major_collector.is_concurrent || sgen_minor_collector.is_parallel) {
-               int num_workers = 1;
-               if (major_collector.is_parallel || sgen_minor_collector.is_parallel) {
-                       num_workers = mono_cpu_count ();
-                       if (num_workers <= 1) {
-                               num_workers = 1;
-                               major_collector.is_parallel = FALSE;
-                               sgen_minor_collector.is_parallel = FALSE;
-                       }
-               }
-               if (major_collector.is_concurrent || sgen_minor_collector.is_parallel)
-                       sgen_workers_init (num_workers, (SgenWorkerCallback) major_collector.worker_init_cb);
-       }
+       sgen_thread_pool_start ();
 
        sgen_memgov_init (max_heap, soft_limit, debug_print_allowance, allowance_ratio, save_target);