[utils] Move mutex and semaphore to mono_os_*
[mono.git] / mono / sgen / sgen-gc.c
index 4b1a1ac16169849b72bd9bc8b630e45592caa29f..a5d14f783192b4fb9a9bd65f2dd0d1507d58e0c0 100644 (file)
@@ -539,30 +539,25 @@ sgen_add_to_global_remset (gpointer ptr, gpointer obj)
  * frequently after each object is copied, to achieve better locality and cache
  * usage.
  *
- * max_objs is the maximum number of objects to scan, or -1 to scan until the stack is
- * empty.
  */
 gboolean
-sgen_drain_gray_stack (int max_objs, ScanCopyContext ctx)
+sgen_drain_gray_stack (ScanCopyContext ctx)
 {
        ScanObjectFunc scan_func = ctx.ops->scan_object;
        GrayQueue *queue = ctx.queue;
 
-       if (current_collection_generation == GENERATION_OLD && major_collector.drain_gray_stack)
-               return major_collector.drain_gray_stack (ctx);
+       if (ctx.ops->drain_gray_stack)
+               return ctx.ops->drain_gray_stack (queue);
 
-       do {
-               int i;
-               for (i = 0; i != max_objs; ++i) {
-                       GCObject *obj;
-                       SgenDescriptor desc;
-                       GRAY_OBJECT_DEQUEUE (queue, &obj, &desc);
-                       if (!obj)
-                               return TRUE;
-                       SGEN_LOG (9, "Precise gray object scan %p (%s)", obj, sgen_client_vtable_get_name (SGEN_LOAD_VTABLE (obj)));
-                       scan_func (obj, desc, queue);
-               }
-       } while (max_objs < 0);
+       for (;;) {
+               GCObject *obj;
+               SgenDescriptor desc;
+               GRAY_OBJECT_DEQUEUE (queue, &obj, &desc);
+               if (!obj)
+                       return TRUE;
+               SGEN_LOG (9, "Precise gray object scan %p (%s)", obj, sgen_client_vtable_get_name (SGEN_LOAD_VTABLE (obj)));
+               scan_func (obj, desc, queue);
+       }
        return FALSE;
 }
 
@@ -822,7 +817,9 @@ sgen_conservatively_pin_objects_from (void **start, void **end, void *start_nurs
 {
        int count = 0;
 
-#ifdef VALGRIND_MAKE_MEM_DEFINED_IF_ADDRESSABLE
+       SGEN_ASSERT (0, ((mword)start & (SIZEOF_VOID_P - 1)) == 0, "Why are we scanning for references in unaligned memory ?");
+
+#if defined(VALGRIND_MAKE_MEM_DEFINED_IF_ADDRESSABLE) && !defined(_WIN64)
        VALGRIND_MAKE_MEM_DEFINED_IF_ADDRESSABLE (start, (char*)end - (char*)start);
 #endif
 
@@ -885,20 +882,6 @@ pin_from_roots (void *start_nursery, void *end_nursery, ScanCopyContext ctx)
        sgen_client_scan_thread_data (start_nursery, end_nursery, FALSE, ctx);
 }
 
-static void
-unpin_objects_from_queue (SgenGrayQueue *queue)
-{
-       for (;;) {
-               GCObject *addr;
-               SgenDescriptor desc;
-               GRAY_OBJECT_DEQUEUE (queue, &addr, &desc);
-               if (!addr)
-                       break;
-               g_assert (SGEN_OBJECT_IS_PINNED (addr));
-               SGEN_UNPIN_OBJECT (addr);
-       }
-}
-
 static void
 single_arg_user_copy_or_mark (GCObject **obj, void *gc_data)
 {
@@ -1092,7 +1075,7 @@ finish_gray_stack (int generation, ScanCopyContext ctx)
         *   To achieve better cache locality and cache usage, we drain the gray stack 
         * frequently, after each object is copied, and just finish the work here.
         */
-       sgen_drain_gray_stack (-1, ctx);
+       sgen_drain_gray_stack (ctx);
        TV_GETTIME (atv);
        SGEN_LOG (2, "%s generation done", generation_name (generation));
 
@@ -1114,7 +1097,7 @@ finish_gray_stack (int generation, ScanCopyContext ctx)
        done_with_ephemerons = 0;
        do {
                done_with_ephemerons = sgen_client_mark_ephemerons (ctx);
-               sgen_drain_gray_stack (-1, ctx);
+               sgen_drain_gray_stack (ctx);
                ++ephemeron_rounds;
        } while (!done_with_ephemerons);
 
@@ -1122,7 +1105,7 @@ finish_gray_stack (int generation, ScanCopyContext ctx)
 
        if (sgen_client_bridge_need_processing ()) {
                /*Make sure the gray stack is empty before we process bridge objects so we get liveness right*/
-               sgen_drain_gray_stack (-1, ctx);
+               sgen_drain_gray_stack (ctx);
                sgen_collect_bridge_objects (generation, ctx);
                if (generation == GENERATION_OLD)
                        sgen_collect_bridge_objects (GENERATION_NURSERY, ctx);
@@ -1146,15 +1129,15 @@ finish_gray_stack (int generation, ScanCopyContext ctx)
        Make sure we drain the gray stack before processing disappearing links and finalizers.
        If we don't make sure it is empty we might wrongly see a live object as dead.
        */
-       sgen_drain_gray_stack (-1, ctx);
+       sgen_drain_gray_stack (ctx);
 
        /*
        We must clear weak links that don't track resurrection before processing object ready for
        finalization so they can be cleared before that.
        */
-       sgen_null_link_in_range (generation, TRUE, ctx);
+       sgen_null_link_in_range (generation, ctx, FALSE);
        if (generation == GENERATION_OLD)
-               sgen_null_link_in_range (GENERATION_NURSERY, TRUE, ctx);
+               sgen_null_link_in_range (GENERATION_NURSERY, ctx, FALSE);
 
 
        /* walk the finalization queue and move also the objects that need to be
@@ -1167,7 +1150,7 @@ finish_gray_stack (int generation, ScanCopyContext ctx)
                sgen_finalize_in_range (GENERATION_NURSERY, ctx);
        /* drain the new stack that might have been created */
        SGEN_LOG (6, "Precise scan of gray area post fin");
-       sgen_drain_gray_stack (-1, ctx);
+       sgen_drain_gray_stack (ctx);
 
        /*
         * This must be done again after processing finalizable objects since CWL slots are cleared only after the key is finalized.
@@ -1175,7 +1158,7 @@ finish_gray_stack (int generation, ScanCopyContext ctx)
        done_with_ephemerons = 0;
        do {
                done_with_ephemerons = sgen_client_mark_ephemerons (ctx);
-               sgen_drain_gray_stack (-1, ctx);
+               sgen_drain_gray_stack (ctx);
                ++ephemeron_rounds;
        } while (!done_with_ephemerons);
 
@@ -1201,12 +1184,12 @@ finish_gray_stack (int generation, ScanCopyContext ctx)
         */
        g_assert (sgen_gray_object_queue_is_empty (queue));
        for (;;) {
-               sgen_null_link_in_range (generation, FALSE, ctx);
+               sgen_null_link_in_range (generation, ctx, TRUE);
                if (generation == GENERATION_OLD)
-                       sgen_null_link_in_range (GENERATION_NURSERY, FALSE, ctx);
+                       sgen_null_link_in_range (GENERATION_NURSERY, ctx, TRUE);
                if (sgen_gray_object_queue_is_empty (queue))
                        break;
-               sgen_drain_gray_stack (-1, ctx);
+               sgen_drain_gray_stack (ctx);
        }
 
        g_assert (sgen_gray_object_queue_is_empty (queue));
@@ -1428,15 +1411,15 @@ job_scan_los_mod_union_card_table (void *worker_data_untyped, SgenThreadPoolJob
 }
 
 static void
-init_gray_queue (void)
+init_gray_queue (gboolean use_workers)
 {
-       if (sgen_collection_is_concurrent ())
+       if (use_workers)
                sgen_workers_init_distribute_gray_queue ();
        sgen_gray_object_queue_init (&gray_queue, NULL);
 }
 
 static void
-enqueue_scan_from_roots_jobs (char *heap_start, char *heap_end, SgenObjectOperations *ops)
+enqueue_scan_from_roots_jobs (char *heap_start, char *heap_end, SgenObjectOperations *ops, gboolean enqueue)
 {
        ScanFromRegisteredRootsJob *scrrj;
        ScanThreadDataJob *stdj;
@@ -1449,33 +1432,33 @@ enqueue_scan_from_roots_jobs (char *heap_start, char *heap_end, SgenObjectOperat
        scrrj->heap_start = heap_start;
        scrrj->heap_end = heap_end;
        scrrj->root_type = ROOT_TYPE_NORMAL;
-       sgen_workers_enqueue_job (&scrrj->job);
+       sgen_workers_enqueue_job (&scrrj->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->heap_start = heap_start;
        scrrj->heap_end = heap_end;
        scrrj->root_type = ROOT_TYPE_WBARRIER;
-       sgen_workers_enqueue_job (&scrrj->job);
+       sgen_workers_enqueue_job (&scrrj->job, enqueue);
 
        /* Threads */
 
        stdj = (ScanThreadDataJob*)sgen_thread_pool_job_alloc ("scan thread data", job_scan_thread_data, sizeof (ScanThreadDataJob));
        stdj->heap_start = heap_start;
        stdj->heap_end = heap_end;
-       sgen_workers_enqueue_job (&stdj->job);
+       sgen_workers_enqueue_job (&stdj->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->queue = &fin_ready_queue;
        sfej->ops = ops;
-       sgen_workers_enqueue_job (&sfej->job);
+       sgen_workers_enqueue_job (&sfej->job, enqueue);
 
        sfej = (ScanFinalizerEntriesJob*)sgen_thread_pool_job_alloc ("scan critical finalizer entries", job_scan_finalizer_entries, sizeof (ScanFinalizerEntriesJob));
        sfej->queue = &critical_fin_queue;
        sfej->ops = ops;
-       sgen_workers_enqueue_job (&sfej->job);
+       sgen_workers_enqueue_job (&sfej->job, enqueue);
 }
 
 /*
@@ -1539,7 +1522,7 @@ collect_nursery (SgenGrayQueue *unpin_queue, gboolean finish_up_concurrent_mark)
 
        sgen_memgov_minor_collection_start ();
 
-       init_gray_queue ();
+       init_gray_queue (FALSE);
 
        gc_stats.minor_gc_count ++;
 
@@ -1551,7 +1534,6 @@ collect_nursery (SgenGrayQueue *unpin_queue, gboolean finish_up_concurrent_mark)
                sgen_check_consistency ();
 
        sgen_process_fin_stage_entries ();
-       sgen_process_dislink_stage_entries ();
 
        /* pin from pinned handles */
        sgen_init_pinning ();
@@ -1578,7 +1560,7 @@ collect_nursery (SgenGrayQueue *unpin_queue, gboolean finish_up_concurrent_mark)
         */
        sj = (ScanJob*)sgen_thread_pool_job_alloc ("scan remset", job_remembered_set_scan, sizeof (ScanJob));
        sj->ops = object_ops;
-       sgen_workers_enqueue_job (&sj->job);
+       sgen_workers_enqueue_job (&sj->job, FALSE);
 
        /* we don't have complete write barrier yet, so we scan all the old generation sections */
        TV_GETTIME (btv);
@@ -1587,7 +1569,7 @@ collect_nursery (SgenGrayQueue *unpin_queue, gboolean finish_up_concurrent_mark)
 
        sgen_pin_stats_print_class_stats ();
 
-       sgen_drain_gray_stack (-1, ctx);
+       sgen_drain_gray_stack (ctx);
 
        /* FIXME: Why do we do this at this specific, seemingly random, point? */
        sgen_client_collecting_minor (&fin_ready_queue, &critical_fin_queue);
@@ -1595,7 +1577,7 @@ collect_nursery (SgenGrayQueue *unpin_queue, gboolean finish_up_concurrent_mark)
        TV_GETTIME (atv);
        time_minor_scan_pinned += TV_ELAPSED (btv, atv);
 
-       enqueue_scan_from_roots_jobs (sgen_get_nursery_start (), nursery_next, object_ops);
+       enqueue_scan_from_roots_jobs (sgen_get_nursery_start (), nursery_next, object_ops, FALSE);
 
        TV_GETTIME (btv);
        time_minor_scan_roots += TV_ELAPSED (atv, btv);
@@ -1671,23 +1653,6 @@ collect_nursery (SgenGrayQueue *unpin_queue, gboolean finish_up_concurrent_mark)
        return needs_major;
 }
 
-static void
-scan_nursery_objects_callback (GCObject *obj, size_t size, ScanCopyContext *ctx)
-{
-       /*
-        * This is called on all objects in the nursery, including pinned ones, so we need
-        * to use sgen_obj_get_descriptor_safe(), which masks out the vtable tag bits.
-        */
-       ctx->ops->scan_object (obj, sgen_obj_get_descriptor_safe (obj), ctx->queue);
-}
-
-static void
-scan_nursery_objects (ScanCopyContext ctx)
-{
-       sgen_scan_area_with_callback (nursery_section->data, nursery_section->end_data,
-                       (IterateObjectCallbackFunc)scan_nursery_objects_callback, (void*)&ctx, FALSE, TRUE);
-}
-
 typedef enum {
        COPY_OR_MARK_FROM_ROOTS_SERIAL,
        COPY_OR_MARK_FROM_ROOTS_START_CONCURRENT,
@@ -1695,7 +1660,7 @@ typedef enum {
 } CopyOrMarkFromRootsMode;
 
 static void
-major_copy_or_mark_from_roots (size_t *old_next_pin_slot, CopyOrMarkFromRootsMode mode, gboolean scan_whole_nursery, SgenObjectOperations *object_ops)
+major_copy_or_mark_from_roots (size_t *old_next_pin_slot, CopyOrMarkFromRootsMode mode, SgenObjectOperations *object_ops)
 {
        LOSObject *bigobj;
        TV_DECLARE (atv);
@@ -1710,10 +1675,7 @@ major_copy_or_mark_from_roots (size_t *old_next_pin_slot, CopyOrMarkFromRootsMod
 
        SGEN_ASSERT (0, !!concurrent == !!concurrent_collection_in_progress, "We've been called with the wrong mode.");
 
-       if (scan_whole_nursery)
-               SGEN_ASSERT (0, mode == COPY_OR_MARK_FROM_ROOTS_FINISH_CONCURRENT, "Scanning whole nursery only makes sense when we're finishing a concurrent collection.");
-
-       if (concurrent) {
+       if (mode == COPY_OR_MARK_FROM_ROOTS_START_CONCURRENT) {
                /*This cleans up unused fragments */
                sgen_nursery_allocator_prepare_for_pinning ();
 
@@ -1724,7 +1686,7 @@ major_copy_or_mark_from_roots (size_t *old_next_pin_slot, CopyOrMarkFromRootsMod
                sgen_nursery_alloc_prepare_for_major ();
        }
 
-       init_gray_queue ();
+       init_gray_queue (mode == COPY_OR_MARK_FROM_ROOTS_START_CONCURRENT);
 
        TV_GETTIME (atv);
 
@@ -1753,26 +1715,12 @@ major_copy_or_mark_from_roots (size_t *old_next_pin_slot, CopyOrMarkFromRootsMod
        }
 
        sgen_process_fin_stage_entries ();
-       sgen_process_dislink_stage_entries ();
 
        TV_GETTIME (atv);
        sgen_init_pinning ();
        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_START_CONCURRENT) {
-               if (major_collector.is_concurrent) {
-                       /*
-                        * The concurrent major collector cannot evict
-                        * yet, so we need to pin cemented objects to
-                        * not break some asserts.
-                        *
-                        * FIXME: We could evict now!
-                        */
-                       sgen_pin_cemented_objects ();
-               }
-       }
-
        sgen_optimize_pin_queue ();
 
        sgen_client_collecting_major_1 ();
@@ -1814,41 +1762,10 @@ major_copy_or_mark_from_roots (size_t *old_next_pin_slot, CopyOrMarkFromRootsMod
                        sgen_client_pinned_los_object (bigobj->data);
                }
        }
-       /* second pass for the sections */
 
-       /*
-        * Concurrent mark never follows references into the nursery.  In the start and
-        * finish pauses we must scan live nursery objects, though.
-        *
-        * In the finish pause we do this conservatively by scanning all nursery objects.
-        * Previously we would only scan pinned objects here.  We assumed that all objects
-        * that were pinned during the nursery collection immediately preceding this finish
-        * mark would be pinned again here.  Due to the way we get the stack end for the GC
-        * thread, however, that's not necessarily the case: we scan part of the stack used
-        * by the GC itself, which changes constantly, so pinning isn't entirely
-        * deterministic.
-        *
-        * The split nursery also complicates things because non-pinned objects can survive
-        * in the nursery.  That's why we need to do a full scan of the nursery for it, too.
-        *
-        * In the future we shouldn't do a preceding nursery collection at all and instead
-        * do the finish pause with promotion from the nursery.
-        *
-        * A further complication arises when we have late-pinned objects from the preceding
-        * nursery collection.  Those are the result of being out of memory when trying to
-        * evacuate objects.  They won't be found from the roots, so we just scan the whole
-        * nursery.
-        *
-        * Non-concurrent mark evacuates from the nursery, so it's
-        * sufficient to just scan pinned nursery objects.
-        */
-       if (scan_whole_nursery || mode == COPY_OR_MARK_FROM_ROOTS_FINISH_CONCURRENT || (concurrent && sgen_minor_collector.is_split)) {
-               scan_nursery_objects (ctx);
-       } else {
-               pin_objects_in_nursery (concurrent, ctx);
-               if (check_nursery_objects_pinned && !sgen_minor_collector.is_split)
-                       sgen_check_nursery_objects_pinned (mode != COPY_OR_MARK_FROM_ROOTS_START_CONCURRENT);
-       }
+       pin_objects_in_nursery (mode == COPY_OR_MARK_FROM_ROOTS_START_CONCURRENT, ctx);
+       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);
        if (old_next_pin_slot)
@@ -1867,10 +1784,15 @@ major_copy_or_mark_from_roots (size_t *old_next_pin_slot, CopyOrMarkFromRootsMod
         * before pinning has finished.  For the non-concurrent
         * collector we start the workers after pinning.
         */
-       if (mode != COPY_OR_MARK_FROM_ROOTS_SERIAL) {
+       if (mode == COPY_OR_MARK_FROM_ROOTS_START_CONCURRENT) {
                SGEN_ASSERT (0, sgen_workers_all_done (), "Why are the workers not done when we start or finish a major collection?");
                sgen_workers_start_all_workers (object_ops);
                gray_queue_enable_redirect (WORKERS_DISTRIBUTE_GRAY_QUEUE);
+       } else if (mode == COPY_OR_MARK_FROM_ROOTS_FINISH_CONCURRENT) {
+               if (sgen_workers_have_idle_work ()) {
+                       sgen_workers_start_all_workers (object_ops);
+                       sgen_workers_join ();
+               }
        }
 
 #ifdef SGEN_DEBUG_INTERNAL_ALLOC
@@ -1888,7 +1810,7 @@ major_copy_or_mark_from_roots (size_t *old_next_pin_slot, CopyOrMarkFromRootsMod
         * FIXME: is this the right context?  It doesn't seem to contain a copy function
         * unless we're concurrent.
         */
-       enqueue_scan_from_roots_jobs (heap_start, heap_end, object_ops);
+       enqueue_scan_from_roots_jobs (heap_start, heap_end, object_ops, mode == COPY_OR_MARK_FROM_ROOTS_START_CONCURRENT);
 
        TV_GETTIME (btv);
        time_major_scan_roots += TV_ELAPSED (atv, btv);
@@ -1899,11 +1821,11 @@ 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;
-               sgen_workers_enqueue_job (&sj->job);
+               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;
-               sgen_workers_enqueue_job (&sj->job);
+               sgen_workers_enqueue_job (&sj->job, FALSE);
 
                TV_GETTIME (atv);
                time_major_scan_mod_union += TV_ELAPSED (btv, atv);
@@ -1913,22 +1835,21 @@ major_copy_or_mark_from_roots (size_t *old_next_pin_slot, CopyOrMarkFromRootsMod
 }
 
 static void
-major_finish_copy_or_mark (void)
+major_finish_copy_or_mark (CopyOrMarkFromRootsMode mode)
 {
-       if (!concurrent_collection_in_progress)
-               return;
-
-       /*
-        * Prepare the pin queue for the next collection.  Since pinning runs on the worker
-        * threads we must wait for the jobs to finish before we can reset it.
-        */
-       sgen_workers_wait_for_jobs_finished ();
-       sgen_finish_pinning ();
+       if (mode == COPY_OR_MARK_FROM_ROOTS_START_CONCURRENT) {
+               /*
+                * Prepare the pin queue for the next collection.  Since pinning runs on the worker
+                * threads we must wait for the jobs to finish before we can reset it.
+                */
+               sgen_workers_wait_for_jobs_finished ();
+               sgen_finish_pinning ();
 
-       sgen_pin_stats_reset ();
+               sgen_pin_stats_reset ();
 
-       if (do_concurrent_checks)
-               sgen_debug_check_nursery_is_clean ();
+               if (do_concurrent_checks)
+                       sgen_debug_check_nursery_is_clean ();
+       }
 }
 
 static void
@@ -1942,7 +1863,8 @@ major_start_collection (gboolean concurrent, size_t *old_next_pin_slot)
 
        g_assert (sgen_section_gray_queue_is_empty (sgen_workers_get_distribute_section_gray_queue ()));
 
-       sgen_cement_reset ();
+       if (!concurrent)
+               sgen_cement_reset ();
 
        if (concurrent) {
                g_assert (major_collector.is_concurrent);
@@ -1969,15 +1891,16 @@ major_start_collection (gboolean concurrent, size_t *old_next_pin_slot)
        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, FALSE, object_ops);
-       major_finish_copy_or_mark ();
+       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);
 }
 
 static void
-major_finish_collection (const char *reason, size_t old_next_pin_slot, gboolean forced, gboolean scan_whole_nursery)
+major_finish_collection (const char *reason, size_t old_next_pin_slot, gboolean forced)
 {
        ScannedObjectCounts counts;
        SgenObjectOperations *object_ops;
+       mword fragment_total;
        TV_DECLARE (atv);
        TV_DECLARE (btv);
 
@@ -1986,31 +1909,17 @@ major_finish_collection (const char *reason, size_t old_next_pin_slot, gboolean
        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, scan_whole_nursery, object_ops);
-
-               major_finish_copy_or_mark ();
+               major_copy_or_mark_from_roots (NULL, COPY_OR_MARK_FROM_ROOTS_FINISH_CONCURRENT, object_ops);
 
-               sgen_workers_join ();
-
-               SGEN_ASSERT (0, sgen_gray_object_queue_is_empty (&gray_queue), "Why is the gray queue not empty after workers have finished working?");
+               major_finish_copy_or_mark (COPY_OR_MARK_FROM_ROOTS_FINISH_CONCURRENT);
 
 #ifdef SGEN_DEBUG_INTERNAL_ALLOC
                main_gc_thread = NULL;
 #endif
-
-               if (do_concurrent_checks)
-                       sgen_debug_check_nursery_is_clean ();
        } else {
-               SGEN_ASSERT (0, !scan_whole_nursery, "scan_whole_nursery only applies to concurrent collections");
                object_ops = &major_collector.major_ops_serial;
        }
 
-       /*
-        * The workers have stopped so we need to finish gray queue
-        * work that might result from finalization in the main GC
-        * thread.  Redirection must therefore be turned off.
-        */
-       sgen_gray_object_queue_disable_alloc_prepare (&gray_queue);
        g_assert (sgen_section_gray_queue_is_empty (sgen_workers_get_distribute_section_gray_queue ()));
 
        /* all the objects in the heap */
@@ -2045,22 +1954,25 @@ major_finish_collection (const char *reason, size_t old_next_pin_slot, gboolean
        reset_heap_boundaries ();
        sgen_update_heap_boundaries ((mword)sgen_get_nursery_start (), (mword)sgen_get_nursery_end ());
 
-       if (!concurrent_collection_in_progress) {
-               /* 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.
-                */
-               if (!sgen_build_nursery_fragments (nursery_section, NULL))
-                       degraded_mode = 1;
+       /* 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.
+        */
+       fragment_total = sgen_build_nursery_fragments (nursery_section, NULL);
+       if (!fragment_total)
+               degraded_mode = 1;
+       SGEN_LOG (4, "Free space in nursery after major %ld", (long)fragment_total);
 
-               /* prepare the pin queue for the next collection */
-               sgen_finish_pinning ();
+       if (do_concurrent_checks && concurrent_collection_in_progress)
+               sgen_debug_check_nursery_is_clean ();
 
-               /* Clear TLABs for all threads */
-               sgen_clear_tlabs ();
+       /* prepare the pin queue for the next collection */
+       sgen_finish_pinning ();
 
-               sgen_pin_stats_reset ();
-       }
+       /* Clear TLABs for all threads */
+       sgen_clear_tlabs ();
+
+       sgen_pin_stats_reset ();
 
        sgen_cement_clear_below_threshold ();
 
@@ -2136,7 +2048,7 @@ major_do_collection (const char *reason, gboolean forced)
        TV_GETTIME (time_start);
 
        major_start_collection (FALSE, &old_next_pin_slot);
-       major_finish_collection (reason, old_next_pin_slot, forced, FALSE);
+       major_finish_collection (reason, old_next_pin_slot, forced);
 
        TV_GETTIME (time_end);
        gc_stats.major_gc_time += TV_ELAPSED (time_start, time_end);
@@ -2211,21 +2123,17 @@ major_finish_concurrent_collection (gboolean forced)
 {
        TV_DECLARE (total_start);
        TV_DECLARE (total_end);
-       gboolean late_pinned;
-       SgenGrayQueue unpin_queue;
-       memset (&unpin_queue, 0, sizeof (unpin_queue));
 
        TV_GETTIME (total_start);
 
        binary_protocol_concurrent_finish ();
 
        /*
-        * The major collector can add global remsets which are processed in the finishing
-        * nursery collection, below.  That implies that the workers must have finished
-        * marking before the nursery collection is allowed to run, otherwise we might miss
-        * some remsets.
+        * We need to stop all workers since we're updating the cardtable below.
+        * The workers will be resumed with a finishing pause context to avoid
+        * additional cardtable and object scanning.
         */
-       sgen_workers_wait ();
+       sgen_workers_stop_all_workers ();
 
        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);
@@ -2233,20 +2141,16 @@ major_finish_concurrent_collection (gboolean forced)
        major_collector.update_cardtable_mod_union ();
        sgen_los_update_cardtable_mod_union ();
 
-       late_pinned = collect_nursery (&unpin_queue, TRUE);
-
        if (mod_union_consistency_check)
                sgen_check_mod_union_consistency ();
 
        current_collection_generation = GENERATION_OLD;
-       major_finish_collection ("finishing", -1, forced, late_pinned);
+       sgen_cement_reset ();
+       major_finish_collection ("finishing", -1, forced);
 
        if (whole_heap_check_before_collection)
                sgen_check_whole_heap (FALSE);
 
-       unpin_objects_from_queue (&unpin_queue);
-       sgen_gray_object_queue_deinit (&unpin_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);
 
@@ -2276,7 +2180,7 @@ sgen_ensure_free_space (size_t size)
                                generation_to_collect = GENERATION_OLD;
                        }
                } else if (sgen_need_major_collection (size)) {
-                       reason = "Minor allowance";
+                       reason = concurrent_collection_in_progress ? "Forced finish concurrent collection" : "Minor allowance";
                        generation_to_collect = GENERATION_OLD;
                } else {
                        generation_to_collect = GENERATION_NURSERY;
@@ -2323,22 +2227,18 @@ sgen_perform_collection (size_t requested_size, int generation_to_collect, const
 
        if (concurrent_collection_in_progress) {
                /*
-                * We update the concurrent collection.  If it finished, we're done.  If
-                * not, and we've been asked to do a nursery collection, we do that.
+                * If the concurrent worker is finished or we are asked to do a major collection
+                * then we finish the concurrent collection.
                 */
-               gboolean finish = major_should_finish_concurrent_collection () || (wait_to_finish && generation_to_collect == GENERATION_OLD);
+               gboolean finish = major_should_finish_concurrent_collection () || generation_to_collect == GENERATION_OLD;
 
                if (finish) {
                        major_finish_concurrent_collection (wait_to_finish);
                        oldest_generation_collected = GENERATION_OLD;
                } else {
-                       sgen_workers_signal_start_nursery_collection_and_wait ();
-
+                       SGEN_ASSERT (0, generation_to_collect == GENERATION_NURSERY, "Why aren't we finishing the concurrent collection?");
                        major_update_concurrent_collection ();
-                       if (generation_to_collect == GENERATION_NURSERY)
-                               collect_nursery (NULL, FALSE);
-
-                       sgen_workers_signal_finish_nursery_collection ();
+                       collect_nursery (NULL, FALSE);
                }
 
                goto done;
@@ -2596,7 +2496,7 @@ sgen_have_pending_finalizers (void)
  * We do not coalesce roots.
  */
 int
-sgen_register_root (char *start, size_t size, SgenDescriptor descr, int root_type)
+sgen_register_root (char *start, size_t size, SgenDescriptor descr, int root_type, int source, const char *msg)
 {
        RootRecord new_root;
        int i;
@@ -2608,6 +2508,8 @@ sgen_register_root (char *start, size_t size, SgenDescriptor descr, int root_typ
                        size_t old_size = root->end_root - start;
                        root->end_root = start + size;
                        SGEN_ASSERT (0, !!root->root_desc == !!descr, "Can't change whether a root is precise or conservative.");
+                       SGEN_ASSERT (0, root->source == source, "Can't change a root's source identifier.");
+                       SGEN_ASSERT (0, !!root->msg == !!msg, "Can't change a root's message.");
                        root->root_desc = descr;
                        roots_size += size;
                        roots_size -= old_size;
@@ -2618,11 +2520,13 @@ sgen_register_root (char *start, size_t size, SgenDescriptor descr, int root_typ
 
        new_root.end_root = start + size;
        new_root.root_desc = descr;
+       new_root.source = source;
+       new_root.msg = msg;
 
        sgen_hash_table_replace (&roots_hash [root_type], start, &new_root, NULL);
        roots_size += size;
 
-       SGEN_LOG (3, "Added root for range: %p-%p, descr: %llx  (%d/%d bytes)", start, new_root.end_root, descr, (int)size, (int)roots_size);
+       SGEN_LOG (3, "Added root for range: %p-%p, descr: %llx  (%d/%d bytes)", start, new_root.end_root, (long long)descr, (int)size, (int)roots_size);
 
        UNLOCK_GC;
        return TRUE;
@@ -2744,7 +2648,7 @@ mono_gc_wbarrier_generic_store (gpointer ptr, GCObject* value)
 {
        SGEN_LOG (8, "Wbarrier store at %p to %p (%s)", ptr, value, value ? sgen_client_vtable_get_name (SGEN_LOAD_VTABLE (value)) : "null");
        SGEN_UPDATE_REFERENCE_ALLOW_NULL (ptr, value);
-       if (ptr_in_nursery (value))
+       if (ptr_in_nursery (value) || concurrent_collection_in_progress)
                mono_gc_wbarrier_generic_nostore (ptr);
        sgen_dummy_use (value);
 }
@@ -2761,7 +2665,7 @@ mono_gc_wbarrier_generic_store_atomic (gpointer ptr, GCObject *value)
 
        InterlockedWritePointer (ptr, value);
 
-       if (ptr_in_nursery (value))
+       if (ptr_in_nursery (value) || concurrent_collection_in_progress)
                mono_gc_wbarrier_generic_nostore (ptr);
 
        sgen_dummy_use (value);
@@ -2822,48 +2726,6 @@ sgen_gc_get_used_size (void)
        return tot;
 }
 
-GCObject*
-sgen_weak_link_get (void **link_addr)
-{
-       void * volatile *link_addr_volatile;
-       void *ptr;
-       GCObject *obj;
- retry:
-       link_addr_volatile = link_addr;
-       ptr = (void*)*link_addr_volatile;
-       /*
-        * At this point we have a hidden pointer.  If the GC runs
-        * here, it will not recognize the hidden pointer as a
-        * reference, and if the object behind it is not referenced
-        * elsewhere, it will be freed.  Once the world is restarted
-        * we reveal the pointer, giving us a pointer to a freed
-        * object.  To make sure we don't return it, we load the
-        * hidden pointer again.  If it's still the same, we can be
-        * sure the object reference is valid.
-        */
-       if (ptr)
-               obj = (GCObject*) REVEAL_POINTER (ptr);
-       else
-               return NULL;
-
-       mono_memory_barrier ();
-
-       /*
-        * During the second bridge processing step the world is
-        * running again.  That step processes all weak links once
-        * more to null those that refer to dead objects.  Before that
-        * is completed, those links must not be followed, so we
-        * conservatively wait for bridge processing when any weak
-        * link is dereferenced.
-        */
-       sgen_client_bridge_wait_for_processing ();
-
-       if ((void*)*link_addr_volatile != ptr)
-               goto retry;
-
-       return obj;
-}
-
 gboolean
 sgen_set_allow_synchronous_major (gboolean flag)
 {
@@ -2975,6 +2837,7 @@ sgen_gc_init (void)
        sgen_init_descriptors ();
        sgen_init_gray_queues ();
        sgen_init_allocator ();
+       sgen_init_gchandles ();
 
        sgen_register_fixed_internal_mem_type (INTERNAL_MEM_SECTION, SGEN_SIZEOF_GC_MEM_SECTION);
        sgen_register_fixed_internal_mem_type (INTERNAL_MEM_GRAY_QUEUE, sizeof (GrayQueueSection));
@@ -3007,9 +2870,6 @@ sgen_gc_init (void)
 
        sgen_nursery_size = DEFAULT_NURSERY_SIZE;
 
-       if (major_collector.is_concurrent)
-               cement_enabled = FALSE;
-
        if (opts) {
                gboolean usage_printed = FALSE;
 
@@ -3166,11 +3026,6 @@ sgen_gc_init (void)
 
        alloc_nursery ();
 
-       if (major_collector.is_concurrent && cement_enabled) {
-               sgen_env_var_error (MONO_GC_PARAMS_NAME, "Ignoring.", "`cementing` is not supported on concurrent major collectors.");
-               cement_enabled = FALSE;
-       }
-
        sgen_cement_init (cement_enabled);
 
        if ((env = g_getenv (MONO_GC_DEBUG_NAME))) {
@@ -3243,6 +3098,7 @@ sgen_gc_init (void)
                                        sgen_env_var_error (MONO_GC_DEBUG_NAME, "Ignoring.", "`check-concurrent` only works with concurrent major collectors.");
                                        continue;
                                }
+                               nursery_clear_policy = CLEAR_AT_GC;
                                do_concurrent_checks = TRUE;
                        } else if (!strcmp (opt, "dump-nursery-at-minor-gc")) {
                                do_dump_nursery_content = TRUE;
@@ -3257,7 +3113,7 @@ sgen_gc_init (void)
                        } else if (g_str_has_prefix (opt, "binary-protocol=")) {
                                char *filename = strchr (opt, '=') + 1;
                                char *colon = strrchr (filename, ':');
-                               size_t limit = -1;
+                               size_t limit = 0;
                                if (colon) {
                                        if (!mono_gc_parse_environment_string_extract_number (colon + 1, &limit)) {
                                                sgen_env_var_error (MONO_GC_DEBUG_NAME, "Ignoring limit.", "Binary protocol file size limit must be an integer.");
@@ -3321,6 +3177,8 @@ sgen_gc_init (void)
 
        sgen_card_table_init (&remset);
 
+       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");
+
        gc_initialized = 1;
 }
 
@@ -3333,7 +3191,9 @@ sgen_get_nursery_clear_policy (void)
 void
 sgen_gc_lock (void)
 {
-       LOCK_GC;
+       MONO_TRY_BLOCKING;
+       mono_os_mutex_lock (&gc_mutex);
+       MONO_FINISH_TRY_BLOCKING;
 }
 
 void
@@ -3341,7 +3201,7 @@ sgen_gc_unlock (void)
 {
        gboolean try_free = sgen_try_free_some_memory;
        sgen_try_free_some_memory = FALSE;
-       mono_mutex_unlock (&gc_mutex);
+       mono_os_mutex_unlock (&gc_mutex);
        if (try_free)
                mono_thread_hazardous_try_free_some ();
 }