Merge pull request #4618 from BrzVlad/feature-par-nrs
authorVlad Brezae <brezaevlad@gmail.com>
Tue, 4 Apr 2017 22:13:00 +0000 (01:13 +0300)
committerGitHub <noreply@github.com>
Tue, 4 Apr 2017 22:13:00 +0000 (01:13 +0300)
[sgen] Parallel nursery collections

18 files changed:
mono/metadata/sgen-mono.c
mono/metadata/sgen-stw.c
mono/sgen/sgen-cardtable.c
mono/sgen/sgen-copy-object.h
mono/sgen/sgen-gc.c
mono/sgen/sgen-gc.h
mono/sgen/sgen-gray.h
mono/sgen/sgen-marksweep.c
mono/sgen/sgen-memory-governor.c
mono/sgen/sgen-minor-copy-object.h
mono/sgen/sgen-minor-scan-object.h
mono/sgen/sgen-protocol.c
mono/sgen/sgen-simple-nursery.c
mono/sgen/sgen-split-nursery.c
mono/sgen/sgen-thread-pool.c
mono/sgen/sgen-thread-pool.h
mono/sgen/sgen-workers.c
mono/sgen/sgen-workers.h

index f2958e964be0ddb76c22b4ff4ebd7574d827c121..a67fc067263ca69b8714b0bb056b9c2fe4028e3d 100644 (file)
@@ -17,7 +17,7 @@
 #include "sgen/sgen-client.h"
 #include "sgen/sgen-cardtable.h"
 #include "sgen/sgen-pinning.h"
-#include "sgen/sgen-thread-pool.h"
+#include "sgen/sgen-workers.h"
 #include "metadata/marshal.h"
 #include "metadata/method-builder.h"
 #include "metadata/abi-details.h"
@@ -2069,7 +2069,7 @@ mono_sgen_register_moved_object (void *obj, void *destination)
         * lock-free data structure for the queue as multiple threads will be
         * adding to it at the same time.
         */
-       if (sgen_thread_pool_is_thread_pool_thread (mono_native_thread_id_get ())) {
+       if (sgen_workers_is_worker_thread (mono_native_thread_id_get ())) {
                sgen_pointer_queue_add (&moved_objects_queue, obj);
                sgen_pointer_queue_add (&moved_objects_queue, destination);
        } else {
@@ -3023,7 +3023,9 @@ mono_gc_base_init (void)
 void
 mono_gc_base_cleanup (void)
 {
-       sgen_thread_pool_shutdown ();
+       sgen_thread_pool_shutdown (major_collector.get_sweep_pool ());
+
+       sgen_workers_shutdown ();
 
        // We should have consumed any outstanding moves.
        g_assert (sgen_pointer_queue_is_empty (&moved_objects_queue));
index 8c1a234616f499e752b71233db8d038245a2debe..fc1de7c25f87aa9759f19d52f0741c0b911fd71f 100644 (file)
@@ -20,7 +20,7 @@
 #include "sgen/sgen-gc.h"
 #include "sgen/sgen-protocol.h"
 #include "sgen/sgen-memory-governor.h"
-#include "sgen/sgen-thread-pool.h"
+#include "sgen/sgen-workers.h"
 #include "metadata/profiler-private.h"
 #include "sgen/sgen-client.h"
 #include "metadata/sgen-bridge-internals.h"
@@ -227,7 +227,8 @@ sgen_is_thread_in_current_stw (SgenThreadInfo *info, int *reason)
        We can't suspend the workers that will do all the heavy lifting.
        FIXME Use some state bit in SgenThreadInfo for this.
        */
-       if (sgen_thread_pool_is_thread_pool_thread (mono_thread_info_get_tid (info))) {
+       if (sgen_thread_pool_is_thread_pool_thread (major_collector.get_sweep_pool (), mono_thread_info_get_tid (info)) ||
+                       sgen_workers_is_worker_thread (mono_thread_info_get_tid (info))) {
                if (reason)
                        *reason = 4;
                return FALSE;
index 42a9101b1d66eb591e57be26164b5454e1dbe46f..a1fac4ada5da4a212f1f63b2fb19e7cbbf072216 100644 (file)
@@ -49,13 +49,6 @@ guint64 remarked_cards;
 static guint64 large_objects;
 static guint64 bloby_objects;
 #endif
-static guint64 major_card_scan_time;
-static guint64 los_card_scan_time;
-
-static guint64 last_major_scan_time;
-static guint64 last_los_scan_time;
-
-static void sgen_card_tables_collect_stats (gboolean begin);
 
 mword
 sgen_card_table_number_of_cards_in_range (mword address, mword size)
@@ -417,19 +410,8 @@ sgen_card_table_clear_cards (void)
 }
 
 static void
-sgen_card_table_finish_minor_collection (void)
-{
-       sgen_card_tables_collect_stats (FALSE);
-}
-
-static void
-sgen_card_table_scan_remsets (ScanCopyContext ctx)
+sgen_card_table_start_scan_remsets (void)
 {
-       SGEN_TV_DECLARE (atv);
-       SGEN_TV_DECLARE (btv);
-
-       sgen_card_tables_collect_stats (TRUE);
-
 #ifdef SGEN_HAVE_OVERLAPPING_CARDS
        /*FIXME we should have a bit on each block/los object telling if the object have marked cards.*/
        /*First we copy*/
@@ -440,17 +422,6 @@ sgen_card_table_scan_remsets (ScanCopyContext ctx)
        /*Then we clear*/
        sgen_card_table_clear_cards ();
 #endif
-       SGEN_TV_GETTIME (atv);
-       sgen_get_major_collector ()->scan_card_table (CARDTABLE_SCAN_GLOBAL, ctx, 0, 1);
-       SGEN_TV_GETTIME (btv);
-       last_major_scan_time = SGEN_TV_ELAPSED (atv, btv); 
-       major_card_scan_time += last_major_scan_time;
-       sgen_los_scan_card_table (CARDTABLE_SCAN_GLOBAL, ctx, 0, 1);
-       SGEN_TV_GETTIME (atv);
-       last_los_scan_time = SGEN_TV_ELAPSED (btv, atv);
-       los_card_scan_time += last_los_scan_time;
-
-       sgen_wbroots_scan_card_table (ctx);
 }
 
 guint8*
@@ -573,69 +544,6 @@ sgen_cardtable_scan_object (GCObject *obj, mword block_obj_size, guint8 *cards,
        binary_protocol_card_scan (obj, sgen_safe_object_get_size (obj));
 }
 
-#ifdef CARDTABLE_STATS
-
-typedef struct {
-       int total, marked, remarked, gc_marked; 
-} card_stats;
-
-static card_stats major_stats, los_stats;
-static card_stats *cur_stats;
-
-static void
-count_marked_cards (mword start, mword size)
-{
-       mword end = start + size;
-       while (start <= end) {
-               guint8 card = *sgen_card_table_get_card_address (start);
-               ++cur_stats->total;
-               if (card)
-                       ++cur_stats->marked;
-               if (card == 2)
-                       ++cur_stats->gc_marked;
-               start += CARD_SIZE_IN_BYTES;
-       }
-}
-
-static void
-count_remarked_cards (mword start, mword size)
-{
-       mword end = start + size;
-       while (start <= end) {
-               if (sgen_card_table_address_is_marked (start)) {
-                       ++cur_stats->remarked;
-                       *sgen_card_table_get_card_address (start) = 2;
-               }
-               start += CARD_SIZE_IN_BYTES;
-       }
-}
-
-#endif
-
-static void
-sgen_card_tables_collect_stats (gboolean begin)
-{
-#ifdef CARDTABLE_STATS
-       if (begin) {
-               memset (&major_stats, 0, sizeof (card_stats));
-               memset (&los_stats, 0, sizeof (card_stats));
-               cur_stats = &major_stats;
-               sgen_major_collector_iterate_live_block_ranges (count_marked_cards);
-               cur_stats = &los_stats;
-               sgen_los_iterate_live_block_ranges (count_marked_cards);
-       } else {
-               cur_stats = &major_stats;
-               sgen_major_collector_iterate_live_block_ranges (count_remarked_cards);
-               cur_stats = &los_stats;
-               sgen_los_iterate_live_block_ranges (count_remarked_cards);
-               printf ("cards major (t %d m %d g %d r %d)  los (t %d m %d g %d r %d) major_scan %.2fms los_scan %.2fms\n", 
-                       major_stats.total, major_stats.marked, major_stats.gc_marked, major_stats.remarked,
-                       los_stats.total, los_stats.marked, los_stats.gc_marked, los_stats.remarked,
-                       last_major_scan_time / 10000.0f, last_los_scan_time / 10000.0f);
-       }
-#endif
-}
-
 void
 sgen_card_table_init (SgenRememberedSet *remset)
 {
@@ -654,9 +562,6 @@ sgen_card_table_init (SgenRememberedSet *remset)
        mono_counters_register ("cardtable large objects", MONO_COUNTER_GC | MONO_COUNTER_ULONG, &large_objects);
        mono_counters_register ("cardtable bloby objects", MONO_COUNTER_GC | MONO_COUNTER_ULONG, &bloby_objects);
 #endif
-       mono_counters_register ("cardtable major scan time", MONO_COUNTER_GC | MONO_COUNTER_ULONG | MONO_COUNTER_TIME, &major_card_scan_time);
-       mono_counters_register ("cardtable los scan time", MONO_COUNTER_GC | MONO_COUNTER_ULONG | MONO_COUNTER_TIME, &los_card_scan_time);
-
 
        remset->wbarrier_set_field = sgen_card_table_wbarrier_set_field;
        remset->wbarrier_arrayref_copy = sgen_card_table_wbarrier_arrayref_copy;
@@ -665,9 +570,8 @@ sgen_card_table_init (SgenRememberedSet *remset)
        remset->wbarrier_generic_nostore = sgen_card_table_wbarrier_generic_nostore;
        remset->record_pointer = sgen_card_table_record_pointer;
 
-       remset->scan_remsets = sgen_card_table_scan_remsets;
+       remset->start_scan_remsets = sgen_card_table_start_scan_remsets;
 
-       remset->finish_minor_collection = sgen_card_table_finish_minor_collection;
        remset->clear_cards = sgen_card_table_clear_cards;
 
        remset->find_address = sgen_card_table_find_address;
index 1e3d3918b65ce7187ff5a09ff6a9f7e4223cdfe3..925af17275dd67a1d2606d0c302b1ba3d7a4c38b 100644 (file)
@@ -124,6 +124,12 @@ copy_object_no_checks_par (GCObject *obj, SgenGrayQueue *queue)
                                GRAY_OBJECT_ENQUEUE_PARALLEL (queue, (GCObject *)destination, sgen_vtable_get_descriptor (vt));
                        }
                } else {
+                       /*
+                        * Unlikely case. Clear the allocated object so it doesn't confuse nursery
+                        * card table scanning, since it can contain old invalid refs.
+                        * FIXME make sure it is not a problem if another threads scans it while we clear
+                        */
+                       mono_gc_bzero_aligned (destination, objsize);
                        destination = final_destination;
                }
        }
index ab46a371126ca76de0901d9cb657988d91409d4f..da44b24c334fd08a06ef1098599e8cfd0c55dd74 100644 (file)
@@ -267,6 +267,8 @@ static guint64 stat_pinned_objects = 0;
 static guint64 time_minor_pre_collection_fragment_clear = 0;
 static guint64 time_minor_pinning = 0;
 static guint64 time_minor_scan_remsets = 0;
+static guint64 time_minor_scan_major_blocks = 0;
+static guint64 time_minor_scan_los = 0;
 static guint64 time_minor_scan_pinned = 0;
 static guint64 time_minor_scan_roots = 0;
 static guint64 time_minor_finish_gray_stack = 0;
@@ -426,8 +428,6 @@ sgen_workers_get_job_gray_queue (WorkerData *worker_data, SgenGrayQueue *default
 static void
 gray_queue_redirect (SgenGrayQueue *queue)
 {
-       SGEN_ASSERT (0, concurrent_collection_in_progress, "Where are we redirecting the gray queue to, without a concurrent collection?");
-
        sgen_workers_take_from_queue (queue);
 }
 
@@ -1233,6 +1233,8 @@ init_stats (void)
        mono_counters_register ("Minor fragment clear", MONO_COUNTER_GC | MONO_COUNTER_ULONG | MONO_COUNTER_TIME, &time_minor_pre_collection_fragment_clear);
        mono_counters_register ("Minor pinning", MONO_COUNTER_GC | MONO_COUNTER_ULONG | MONO_COUNTER_TIME, &time_minor_pinning);
        mono_counters_register ("Minor scan remembered set", MONO_COUNTER_GC | MONO_COUNTER_ULONG | MONO_COUNTER_TIME, &time_minor_scan_remsets);
+       mono_counters_register ("Minor scan major blocks", MONO_COUNTER_GC | MONO_COUNTER_ULONG | MONO_COUNTER_TIME, &time_minor_scan_major_blocks);
+       mono_counters_register ("Minor scan los", MONO_COUNTER_GC | MONO_COUNTER_ULONG | MONO_COUNTER_TIME, &time_minor_scan_los);
        mono_counters_register ("Minor scan pinned", MONO_COUNTER_GC | MONO_COUNTER_ULONG | MONO_COUNTER_TIME, &time_minor_scan_pinned);
        mono_counters_register ("Minor scan roots", MONO_COUNTER_GC | MONO_COUNTER_ULONG | MONO_COUNTER_TIME, &time_minor_scan_roots);
        mono_counters_register ("Minor fragment creation", MONO_COUNTER_GC | MONO_COUNTER_ULONG | MONO_COUNTER_TIME, &time_minor_fragment_creation);
@@ -1333,12 +1335,6 @@ scan_copy_context_for_scan_job (void *worker_data_untyped, ScanJob *job)
        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)
-{
-       remset.scan_remsets (scan_copy_context_for_scan_job (worker_data_untyped, (ScanJob*)job));
-}
-
 typedef struct {
        ScanJob scan_job;
        char *heap_start;
@@ -1384,6 +1380,43 @@ job_scan_finalizer_entries (void *worker_data_untyped, SgenThreadPoolJob *job)
        scan_finalizer_entries (job_data->queue, ctx);
 }
 
+static void
+job_scan_wbroots (void *worker_data_untyped, SgenThreadPoolJob *job)
+{
+       ScanJob *job_data = (ScanJob*)job;
+       ScanCopyContext ctx = scan_copy_context_for_scan_job (worker_data_untyped, job_data);
+
+       sgen_wbroots_scan_card_table (ctx);
+}
+
+static void
+job_scan_major_card_table (void *worker_data_untyped, SgenThreadPoolJob *job)
+{
+       SGEN_TV_DECLARE (atv);
+       SGEN_TV_DECLARE (btv);
+       ParallelScanJob *job_data = (ParallelScanJob*)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 ());
+       SGEN_TV_GETTIME (btv);
+       time_minor_scan_major_blocks += SGEN_TV_ELAPSED (atv, btv);
+}
+
+static void
+job_scan_los_card_table (void *worker_data_untyped, SgenThreadPoolJob *job)
+{
+       SGEN_TV_DECLARE (atv);
+       SGEN_TV_DECLARE (btv);
+       ParallelScanJob *job_data = (ParallelScanJob*)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_TV_GETTIME (btv);
+       time_minor_scan_los += SGEN_TV_ELAPSED (atv, btv);
+}
+
 static void
 job_scan_major_mod_union_card_table (void *worker_data_untyped, SgenThreadPoolJob *job)
 {
@@ -1475,6 +1508,34 @@ init_gray_queue (SgenGrayQueue *gc_thread_gray_queue, gboolean use_workers)
        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 ();
+       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);
+
+       for (i = 0; i < split_count; i++) {
+               ParallelScanJob *psj;
+
+               psj = (ParallelScanJob*)sgen_thread_pool_job_alloc ("scan major remsets", job_scan_major_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 = (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);
+       }
+}
+
 static void
 enqueue_scan_from_roots_jobs (SgenGrayQueue *gc_thread_gray_queue, char *heap_start, char *heap_end, SgenObjectOperations *ops, gboolean enqueue)
 {
@@ -1535,13 +1596,12 @@ enqueue_scan_from_roots_jobs (SgenGrayQueue *gc_thread_gray_queue, char *heap_st
 static gboolean
 collect_nursery (const char *reason, gboolean is_overflow, SgenGrayQueue *unpin_queue)
 {
-       gboolean needs_major;
+       gboolean needs_major, is_parallel = FALSE;
        size_t max_garbage_amount;
        char *nursery_next;
        mword fragment_total;
-       ScanJob *sj;
        SgenGrayQueue gc_thread_gray_queue;
-       SgenObjectOperations *object_ops;
+       SgenObjectOperations *object_ops_nopar, *object_ops_par = NULL;
        ScanCopyContext ctx;
        TV_DECLARE (atv);
        TV_DECLARE (btv);
@@ -1556,10 +1616,16 @@ 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 (sgen_concurrent_collection_in_progress ()) {
+               /* FIXME Support parallel nursery collections with concurrent major */
+               object_ops_nopar = &sgen_minor_collector.serial_ops_with_concurrent_major;
+       } else {
+               object_ops_nopar = &sgen_minor_collector.serial_ops;
+               if (sgen_minor_collector.is_parallel) {
+                       object_ops_par = &sgen_minor_collector.parallel_ops;
+                       is_parallel = TRUE;
+               }
+       }
 
        if (do_verify_nursery || do_dump_nursery_content)
                sgen_debug_verify_nursery (do_dump_nursery_content);
@@ -1596,8 +1662,8 @@ collect_nursery (const char *reason, gboolean is_overflow, SgenGrayQueue *unpin_
 
        sgen_memgov_minor_collection_start ();
 
-       init_gray_queue (&gc_thread_gray_queue, FALSE);
-       ctx = CONTEXT_FROM_OBJECT_OPERATIONS (object_ops, &gc_thread_gray_queue);
+       init_gray_queue (&gc_thread_gray_queue, is_parallel);
+       ctx = CONTEXT_FROM_OBJECT_OPERATIONS (object_ops_nopar, &gc_thread_gray_queue);
 
        gc_stats.minor_gc_count ++;
 
@@ -1629,10 +1695,9 @@ collect_nursery (const char *reason, gboolean is_overflow, SgenGrayQueue *unpin_
        SGEN_LOG (2, "Finding pinned pointers: %zd in %lld usecs", sgen_get_pinned_count (), (long long)TV_ELAPSED (btv, atv));
        SGEN_LOG (4, "Start scan with %zd pinned objects", sgen_get_pinned_count ());
 
-       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);
+       remset.start_scan_remsets ();
+
+       enqueue_scan_remembered_set_jobs (&gc_thread_gray_queue, is_parallel ? object_ops_par : object_ops_nopar, is_parallel);
 
        /* we don't have complete write barrier yet, so we scan all the old generation sections */
        TV_GETTIME (btv);
@@ -1647,7 +1712,13 @@ 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 (&gc_thread_gray_queue, sgen_get_nursery_start (), nursery_next, object_ops, FALSE);
+       enqueue_scan_from_roots_jobs (&gc_thread_gray_queue, sgen_get_nursery_start (), nursery_next, is_parallel ? object_ops_par : object_ops_nopar, is_parallel);
+
+       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 ();
+       }
 
        TV_GETTIME (btv);
        time_minor_scan_roots += TV_ELAPSED (atv, btv);
@@ -1709,8 +1780,6 @@ collect_nursery (const char *reason, gboolean is_overflow, SgenGrayQueue *unpin_
 
        sgen_gray_object_queue_dispose (&gc_thread_gray_queue);
 
-       remset.finish_minor_collection ();
-
        check_scan_starts ();
 
        binary_protocol_flush_buffers (FALSE);
@@ -1867,7 +1936,8 @@ major_copy_or_mark_from_roots (SgenGrayQueue *gc_thread_gray_queue, size_t *old_
 
        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) {
-               sgen_workers_set_num_active_workers (0);
+               if (object_ops_par != NULL)
+                       sgen_workers_set_num_active_workers (0);
                if (sgen_workers_have_idle_work ()) {
                        /*
                         * We force the finish of the worker with the new object ops context
@@ -1912,8 +1982,11 @@ major_copy_or_mark_from_roots (SgenGrayQueue *gc_thread_gray_queue, size_t *old_
 
        if (mode == COPY_OR_MARK_FROM_ROOTS_FINISH_CONCURRENT) {
                int i, split_count = sgen_workers_get_job_split_count ();
+               gboolean parallel = object_ops_par != NULL;
 
-               gray_queue_redirect (gc_thread_gray_queue);
+               /* If we're not parallel we finish the collection on the gc thread */
+               if (parallel)
+                       gray_queue_redirect (gc_thread_gray_queue);
 
                /* Mod union card table */
                for (i = 0; i < split_count; i++) {
@@ -1921,27 +1994,29 @@ major_copy_or_mark_from_roots (SgenGrayQueue *gc_thread_gray_queue, size_t *old_
 
                        psj = (ParallelScanJob*)sgen_thread_pool_job_alloc ("scan mod union cardtable", job_scan_major_mod_union_card_table, sizeof (ParallelScanJob));
                        psj->scan_job.ops = object_ops_par ? object_ops_par : object_ops_nopar;
-                       psj->scan_job.gc_thread_gray_queue = NULL;
+                       psj->scan_job.gc_thread_gray_queue = gc_thread_gray_queue;
                        psj->job_index = i;
-                       sgen_workers_enqueue_job (&psj->scan_job.job, TRUE);
+                       sgen_workers_enqueue_job (&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 = object_ops_par ? object_ops_par : object_ops_nopar;
-                       psj->scan_job.gc_thread_gray_queue = NULL;
+                       psj->scan_job.gc_thread_gray_queue = gc_thread_gray_queue;
                        psj->job_index = i;
-                       sgen_workers_enqueue_job (&psj->scan_job.job, TRUE);
+                       sgen_workers_enqueue_job (&psj->scan_job.job, parallel);
                }
 
-               /*
-                * If we enqueue a job while workers are running we need to sgen_workers_ensure_awake
-                * in order to make sure that we are running the idle func and draining all worker
-                * gray queues. The operation of starting workers implies this, so we start them after
-                * in order to avoid doing this operation twice. The workers will drain the main gray
-                * 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 ();
+               if (parallel) {
+                       /*
+                        * If we enqueue a job while workers are running we need to sgen_workers_ensure_awake
+                        * in order to make sure that we are running the idle func and draining all worker
+                        * gray queues. The operation of starting workers implies this, so we start them after
+                        * in order to avoid doing this operation twice. The workers will drain the main gray
+                        * 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_pin_stats_report ();
@@ -3004,11 +3079,13 @@ sgen_gc_init (void)
        sgen_client_init ();
 
        if (!minor_collector_opt) {
-               sgen_simple_nursery_init (&sgen_minor_collector);
+               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);
+                       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 {
@@ -3333,9 +3410,9 @@ sgen_gc_init (void)
        if (major_collector.post_param_init)
                major_collector.post_param_init (&major_collector);
 
-       if (major_collector.needs_thread_pool) {
+       if (major_collector.is_concurrent || sgen_minor_collector.is_parallel) {
                int num_workers = 1;
-               if (major_collector.is_parallel) {
+               if (major_collector.is_parallel || sgen_minor_collector.is_parallel) {
                        /* FIXME Detect the number of physical cores, instead of logical */
                        num_workers = mono_cpu_count () / 2;
                        if (num_workers < 1)
@@ -3399,6 +3476,12 @@ sgen_get_major_collector (void)
        return &major_collector;
 }
 
+SgenMinorCollector*
+sgen_get_minor_collector (void)
+{
+       return &sgen_minor_collector;
+}
+
 SgenRememberedSet*
 sgen_get_remset (void)
 {
index 74d985252816aee3e6093e11c1df03580852e6d3..8cf5a46fa500f40683e1d916f7a8ac08807c2349 100644 (file)
@@ -35,6 +35,7 @@ typedef struct _SgenThreadInfo SgenThreadInfo;
 #include "mono/sgen/sgen-hash-table.h"
 #include "mono/sgen/sgen-protocol.h"
 #include "mono/sgen/gc-internal-agnostic.h"
+#include "mono/sgen/sgen-thread-pool.h"
 
 /* The method used to clear the nursery */
 /* Clearing at nursery collections is the safest, but has bad interactions with caches.
@@ -559,11 +560,13 @@ sgen_nursery_is_object_alive (GCObject *obj)
 
 typedef struct {
        gboolean is_split;
+       gboolean is_parallel;
 
        GCObject* (*alloc_for_promotion) (GCVTable vtable, GCObject *obj, size_t objsize, gboolean has_references);
 
        SgenObjectOperations serial_ops;
        SgenObjectOperations serial_ops_with_concurrent_major;
+       SgenObjectOperations parallel_ops;
 
        void (*prepare_to_space) (char *to_space_bitmap, size_t space_bitmap_size);
        void (*clear_fragments) (void);
@@ -578,7 +581,7 @@ typedef struct {
 
 extern SgenMinorCollector sgen_minor_collector;
 
-void sgen_simple_nursery_init (SgenMinorCollector *collector);
+void sgen_simple_nursery_init (SgenMinorCollector *collector, gboolean parallel);
 void sgen_split_nursery_init (SgenMinorCollector *collector);
 
 /* Updating references */
@@ -591,7 +594,7 @@ sgen_update_reference (GCObject **p, GCObject *o, gboolean allow_null)
 {
        if (!allow_null)
                SGEN_ASSERT (0, o, "Cannot update a reference with a NULL pointer");
-       SGEN_ASSERT (0, !sgen_thread_pool_is_thread_pool_thread (mono_native_thread_id_get ()), "Can't update a reference in the worker thread");
+       SGEN_ASSERT (0, !sgen_workers_is_worker_thread (mono_native_thread_id_get ()), "Can't update a reference in the worker thread");
        *p = o;
 }
 
@@ -634,7 +637,6 @@ struct _SgenMajorCollector {
        size_t section_size;
        gboolean is_concurrent;
        gboolean is_parallel;
-       gboolean needs_thread_pool;
        gboolean supports_cardtable;
        gboolean sweeps_lazily;
 
@@ -691,6 +693,7 @@ struct _SgenMajorCollector {
        guint8* (*get_cardtable_mod_union_for_reference) (char *object);
        long long (*get_and_reset_num_major_objects_marked) (void);
        void (*count_cards) (long long *num_total_cards, long long *num_marked_cards);
+       SgenThreadPool* (*get_sweep_pool) (void);
 
        void (*worker_init_cb) (gpointer worker);
 };
@@ -701,6 +704,7 @@ void sgen_marksweep_init (SgenMajorCollector *collector);
 void sgen_marksweep_conc_init (SgenMajorCollector *collector);
 void sgen_marksweep_conc_par_init (SgenMajorCollector *collector);
 SgenMajorCollector* sgen_get_major_collector (void);
+SgenMinorCollector* sgen_get_minor_collector (void);
 
 
 typedef struct _SgenRememberedSet {
@@ -711,11 +715,10 @@ typedef struct _SgenRememberedSet {
        void (*wbarrier_generic_nostore) (gpointer ptr);
        void (*record_pointer) (gpointer ptr);
 
-       void (*scan_remsets) (ScanCopyContext ctx);
+       void (*start_scan_remsets) (void);
 
        void (*clear_cards) (void);
 
-       void (*finish_minor_collection) (void);
        gboolean (*find_address) (char *addr);
        gboolean (*find_address_with_cards) (char *cards_start, guint8 *cards, char *addr);
 } SgenRememberedSet;
index bbaefe07575539c21e9ba7f02708f8d5a8b1148a..3ea4037af245c7489790da11c2836eae471d4bd0 100644 (file)
@@ -47,7 +47,7 @@
 #define SGEN_GRAY_QUEUE_HEADER_SIZE    3
 #endif
 
-#define SGEN_GRAY_QUEUE_SECTION_SIZE   (128 - SGEN_GRAY_QUEUE_HEADER_SIZE)
+#define SGEN_GRAY_QUEUE_SECTION_SIZE   (512 - SGEN_GRAY_QUEUE_HEADER_SIZE)
 
 #ifdef SGEN_CHECK_GRAY_OBJECT_SECTIONS
 typedef enum {
index 67b422c1990c10c52989478c2331e14b0f1dfbb3..04ab32dab84a0dbc96e64bf00e7293ed6eee2e8d 100644 (file)
@@ -187,6 +187,9 @@ static volatile int sweep_state = SWEEP_STATE_SWEPT;
 static gboolean concurrent_mark;
 static gboolean concurrent_sweep = TRUE;
 
+SgenThreadPool sweep_pool_inst;
+SgenThreadPool *sweep_pool;
+
 #define BLOCK_IS_TAGGED_HAS_REFERENCES(bl)     SGEN_POINTER_IS_TAGGED_1 ((bl))
 #define BLOCK_TAG_HAS_REFERENCES(bl)           SGEN_POINTER_TAG_1 ((bl))
 
@@ -727,7 +730,6 @@ get_block:
                         */
                        if (SGEN_CAS_PTR ((volatile gpointer *)&free_blocks [size_index], next_free, block) != block)
                                goto get_block;
-                       g_assert (block->free_list);
                        block->next_free = free_blocks_local [size_index];
                        free_blocks_local [size_index] = block;
 
@@ -919,7 +921,7 @@ major_finish_sweep_checking (void)
  wait:
        job = sweep_job;
        if (job)
-               sgen_thread_pool_job_wait (job);
+               sgen_thread_pool_job_wait (sweep_pool, job);
        SGEN_ASSERT (0, !sweep_job, "Why did the sweep job not null itself?");
        SGEN_ASSERT (0, sweep_state == SWEEP_STATE_SWEPT, "How is the sweep job done but we're not swept?");
 }
@@ -1556,6 +1558,25 @@ sgen_worker_clear_free_block_lists (WorkerData *worker)
        }
 }
 
+static void
+sgen_worker_clear_free_block_lists_evac (WorkerData *worker)
+{
+       int i, j;
+
+       if (!worker->free_block_lists)
+               return;
+
+       for (i = 0; i < MS_BLOCK_TYPE_MAX; i++) {
+               for (j = 0; j < num_block_obj_sizes; j++) {
+                       if (((MSBlockInfo***) worker->free_block_lists) [i][j])
+                               SGEN_ASSERT (0, !((MSBlockInfo***) worker->free_block_lists) [i][j]->next_free, "Why do we have linked free blocks on the workers");
+
+                       if (evacuate_block_obj_sizes [j])
+                               ((MSBlockInfo***) worker->free_block_lists) [i][j] = NULL;
+               }
+       }
+}
+
 static void
 sweep_start (void)
 {
@@ -1788,7 +1809,7 @@ sweep_job_func (void *thread_data_untyped, SgenThreadPoolJob *job)
         */
        if (concurrent_sweep && lazy_sweep) {
                sweep_blocks_job = sgen_thread_pool_job_alloc ("sweep_blocks", sweep_blocks_job_func, sizeof (SgenThreadPoolJob));
-               sgen_thread_pool_job_enqueue (sweep_blocks_job);
+               sgen_thread_pool_job_enqueue (sweep_pool, sweep_blocks_job);
        }
 
        sweep_finish ();
@@ -1837,7 +1858,7 @@ major_sweep (void)
        SGEN_ASSERT (0, !sweep_job, "We haven't finished the last sweep?");
        if (concurrent_sweep) {
                sweep_job = sgen_thread_pool_job_alloc ("sweep", sweep_job_func, sizeof (SgenThreadPoolJob));
-               sgen_thread_pool_job_enqueue (sweep_job);
+               sgen_thread_pool_job_enqueue (sweep_pool, sweep_job);
        } else {
                sweep_job_func (NULL, NULL);
        }
@@ -2039,6 +2060,9 @@ major_start_major_collection (void)
                sgen_evacuation_freelist_blocks (&free_block_lists [MS_BLOCK_FLAG_REFS][i], i);
        }
 
+       /* We expect workers to have very few blocks on the freelist, just evacuate them */
+       sgen_workers_foreach (sgen_worker_clear_free_block_lists_evac);
+
        if (lazy_sweep && concurrent_sweep) {
                /*
                 * sweep_blocks_job is created before sweep_finish, which we wait for above
@@ -2047,7 +2071,7 @@ major_start_major_collection (void)
                 */
                SgenThreadPoolJob *job = sweep_blocks_job;
                if (job)
-                       sgen_thread_pool_job_wait (job);
+                       sgen_thread_pool_job_wait (sweep_pool, job);
        }
 
        if (lazy_sweep && !concurrent_sweep)
@@ -2087,6 +2111,12 @@ major_finish_major_collection (ScannedObjectCounts *counts)
 #endif
 }
 
+static SgenThreadPool*
+major_get_sweep_pool (void)
+{
+       return sweep_pool;
+}
+
 static int
 compare_pointers (const void *va, const void *vb) {
        char *a = *(char**)va, *b = *(char**)vb;
@@ -2693,7 +2723,6 @@ static void
 post_param_init (SgenMajorCollector *collector)
 {
        collector->sweeps_lazily = lazy_sweep;
-       collector->needs_thread_pool = concurrent_mark || concurrent_sweep;
 }
 
 /* We are guaranteed to be called by the worker in question */
@@ -2712,6 +2741,12 @@ sgen_worker_init_callback (gpointer worker_untyped)
        mono_native_tls_set_value (worker_block_free_list_key, worker_free_blocks);
 }
 
+static void
+thread_pool_init_func (void *data_untyped)
+{
+       sgen_client_thread_register_worker ();
+}
+
 static void
 sgen_marksweep_init_internal (SgenMajorCollector *collector, gboolean is_concurrent, gboolean is_parallel)
 {
@@ -2748,6 +2783,12 @@ sgen_marksweep_init_internal (SgenMajorCollector *collector, gboolean is_concurr
        for (i = 0; i < MS_NUM_FAST_BLOCK_OBJ_SIZE_INDEXES * 8; ++i)
                g_assert (MS_BLOCK_OBJ_SIZE_INDEX (i) == ms_find_block_obj_size_index (i));
 
+       /* We can do this because we always init the minor before the major */
+       if (is_parallel || sgen_get_minor_collector ()->is_parallel) {
+               mono_native_tls_alloc (&worker_block_free_list_key, NULL);
+               collector->worker_init_cb = sgen_worker_init_callback;
+       }
+
        mono_counters_register ("# major blocks allocated", MONO_COUNTER_GC | MONO_COUNTER_ULONG, &stat_major_blocks_alloced);
        mono_counters_register ("# major blocks freed", MONO_COUNTER_GC | MONO_COUNTER_ULONG, &stat_major_blocks_freed);
        mono_counters_register ("# major blocks lazy swept", MONO_COUNTER_GC | MONO_COUNTER_ULONG, &stat_major_blocks_lazy_swept);
@@ -2761,7 +2802,6 @@ sgen_marksweep_init_internal (SgenMajorCollector *collector, gboolean is_concurr
        concurrent_mark = is_concurrent;
        collector->is_concurrent = is_concurrent;
        collector->is_parallel = is_parallel;
-       collector->needs_thread_pool = is_concurrent || concurrent_sweep;
        collector->get_and_reset_num_major_objects_marked = major_get_and_reset_num_major_objects_marked;
        collector->supports_cardtable = TRUE;
 
@@ -2807,6 +2847,7 @@ sgen_marksweep_init_internal (SgenMajorCollector *collector, gboolean is_concurr
        collector->is_valid_object = major_is_valid_object;
        collector->describe_pointer = major_describe_pointer;
        collector->count_cards = major_count_cards;
+       collector->get_sweep_pool = major_get_sweep_pool;
 
        collector->major_ops_serial.copy_or_mark_object = major_copy_or_mark_object_canonical;
        collector->major_ops_serial.scan_object = major_scan_object_with_evacuation;
@@ -2837,10 +2878,6 @@ sgen_marksweep_init_internal (SgenMajorCollector *collector, gboolean is_concurr
                        collector->major_ops_conc_par_finish.scan_vtype = major_scan_vtype_par_with_evacuation;
                        collector->major_ops_conc_par_finish.scan_ptr_field = major_scan_ptr_field_par_with_evacuation;
                        collector->major_ops_conc_par_finish.drain_gray_stack = drain_gray_stack_par;
-
-                       collector->worker_init_cb = sgen_worker_init_callback;
-
-                       mono_native_tls_alloc (&worker_block_free_list_key, NULL);
                }
        }
 
@@ -2870,6 +2907,12 @@ sgen_marksweep_init_internal (SgenMajorCollector *collector, gboolean is_concurr
 
        /*cardtable requires major pages to be 8 cards aligned*/
        g_assert ((MS_BLOCK_SIZE % (8 * CARD_SIZE_IN_BYTES)) == 0);
+
+       if (concurrent_sweep) {
+               SgenThreadPool **thread_datas = &sweep_pool;
+               sweep_pool = &sweep_pool_inst;
+               sgen_thread_pool_init (sweep_pool, 1, thread_pool_init_func, NULL, NULL, NULL, (SgenThreadPoolData**)&thread_datas);
+       }
 }
 
 void
index 8dae5a062fd6ddcc54ee582cd161624449ee6a11..3bf90115b854910c743596a6e32c1a061e35d734 100644 (file)
@@ -20,7 +20,7 @@
 
 #include "mono/sgen/sgen-gc.h"
 #include "mono/sgen/sgen-memory-governor.h"
-#include "mono/sgen/sgen-thread-pool.h"
+#include "mono/sgen/sgen-workers.h"
 #include "mono/sgen/sgen-client.h"
 
 #define MIN_MINOR_COLLECTION_ALLOWANCE ((mword)(DEFAULT_NURSERY_SIZE * default_allowance_nursery_size_ratio))
@@ -459,7 +459,7 @@ gboolean
 sgen_memgov_try_alloc_space (mword size, int space)
 {
        if (sgen_memgov_available_free_space () < size) {
-               SGEN_ASSERT (4, !sgen_thread_pool_is_thread_pool_thread (mono_native_thread_id_get ()), "Memory shouldn't run out in worker thread");
+               SGEN_ASSERT (4, !sgen_workers_is_worker_thread (mono_native_thread_id_get ()), "Memory shouldn't run out in worker thread");
                return FALSE;
        }
 
index 9ba0d129522e677b98bf87768d72b09c1e2fc2c4..3ad10aac4447fb2608803abd67bd71b77cf8441d 100644 (file)
 
 #if defined(SGEN_SIMPLE_NURSERY)
 
+#ifdef SGEN_SIMPLE_PAR_NURSERY
+/* Not supported with concurrent major yet */
+#define SERIAL_COPY_OBJECT simple_par_nursery_copy_object
+#define SERIAL_COPY_OBJECT_FROM_OBJ simple_par_nursery_copy_object_from_obj
+#else
 #ifdef SGEN_CONCURRENT_MAJOR
 #define SERIAL_COPY_OBJECT simple_nursery_serial_with_concurrent_major_copy_object
 #define SERIAL_COPY_OBJECT_FROM_OBJ simple_nursery_serial_with_concurrent_major_copy_object_from_obj
@@ -21,6 +26,7 @@
 #define SERIAL_COPY_OBJECT simple_nursery_serial_copy_object
 #define SERIAL_COPY_OBJECT_FROM_OBJ simple_nursery_serial_copy_object_from_obj
 #endif
+#endif
 
 #elif defined (SGEN_SPLIT_NURSERY)
 
@@ -108,7 +114,11 @@ SERIAL_COPY_OBJECT (GCObject **obj_slot, SgenGrayQueue *queue)
 
        HEAVY_STAT (++stat_objects_copied_nursery);
 
+#ifdef SGEN_SIMPLE_PAR_NURSERY
+       copy = copy_object_no_checks_par (obj, queue);
+#else
        copy = copy_object_no_checks (obj, queue);
+#endif
        SGEN_UPDATE_REFERENCE (obj_slot, copy);
 }
 
@@ -214,7 +224,11 @@ SERIAL_COPY_OBJECT_FROM_OBJ (GCObject **obj_slot, SgenGrayQueue *queue)
 
        HEAVY_STAT (++stat_objects_copied_nursery);
 
+#ifdef SGEN_SIMPLE_PAR_NURSERY
+       copy = copy_object_no_checks_par (obj, queue);
+#else
        copy = copy_object_no_checks (obj, queue);
+#endif
 #ifdef SGEN_CONCURRENT_MAJOR
        /*
         * If an object is evacuated to the major heap and a reference to it, from the major
index aadfe068e24f401bf99510dcebda4595031c6b91..b92d309f4ec1228d02086deb5d856902459c94cc 100644 (file)
@@ -18,6 +18,12 @@ extern guint64 stat_scan_object_called_nursery;
 
 #if defined(SGEN_SIMPLE_NURSERY)
 
+#ifdef SGEN_SIMPLE_PAR_NURSERY
+#define SERIAL_SCAN_OBJECT simple_par_nursery_serial_scan_object
+#define SERIAL_SCAN_VTYPE simple_par_nursery_serial_scan_vtype
+#define SERIAL_SCAN_PTR_FIELD simple_par_nursery_serial_scan_ptr_field
+#define SERIAL_DRAIN_GRAY_STACK simple_par_nursery_serial_drain_gray_stack
+#else
 #ifdef SGEN_CONCURRENT_MAJOR
 #define SERIAL_SCAN_OBJECT simple_nursery_serial_with_concurrent_major_scan_object
 #define SERIAL_SCAN_VTYPE simple_nursery_serial_with_concurrent_major_scan_vtype
@@ -29,6 +35,7 @@ extern guint64 stat_scan_object_called_nursery;
 #define SERIAL_SCAN_PTR_FIELD simple_nursery_serial_scan_ptr_field
 #define SERIAL_DRAIN_GRAY_STACK simple_nursery_serial_drain_gray_stack
 #endif
+#endif
 
 #elif defined (SGEN_SPLIT_NURSERY)
 
@@ -104,16 +111,31 @@ SERIAL_SCAN_PTR_FIELD (GCObject *full_object, GCObject **ptr, SgenGrayQueue *que
 static gboolean
 SERIAL_DRAIN_GRAY_STACK (SgenGrayQueue *queue)
 {
-        for (;;) {
-                GCObject *obj;
-                SgenDescriptor desc;
+#ifdef SGEN_SIMPLE_PAR_NURSERY
+       int i;
+       /*
+        * We do bounded iteration so we can switch to optimized context
+        * when we are the last worker remaining.
+        */
+       for (i = 0; i < 32; i++) {
+#else
+       for (;;) {
+#endif
+               GCObject *obj;
+               SgenDescriptor desc;
+
+#ifdef SGEN_SIMPLE_PAR_NURSERY
+               GRAY_OBJECT_DEQUEUE_PARALLEL (queue, &obj, &desc);
+#else
+               GRAY_OBJECT_DEQUEUE_SERIAL (queue, &obj, &desc);
+#endif
+               if (!obj)
+                       return TRUE;
 
-                GRAY_OBJECT_DEQUEUE_SERIAL (queue, &obj, &desc);
-                if (!obj)
-                        return TRUE;
+               SERIAL_SCAN_OBJECT (obj, desc, queue);
+       }
 
-                SERIAL_SCAN_OBJECT (obj, desc, queue);
-        }
+       return FALSE;
 }
 
 #define FILL_MINOR_COLLECTOR_SCAN_OBJECT(ops)  do {                    \
index 419e90a482a50faa961382308edd80844b743a17..f86b05e51c963dd4de1f6c5dba3728732db8b97a 100644 (file)
@@ -16,7 +16,7 @@
 #include "sgen-gc.h"
 #include "sgen-protocol.h"
 #include "sgen-memory-governor.h"
-#include "sgen-thread-pool.h"
+#include "sgen-workers.h"
 #include "sgen-client.h"
 #include "mono/utils/mono-membar.h"
 #include "mono/utils/mono-proclib.h"
@@ -365,11 +365,17 @@ protocol_entry (unsigned char type, gpointer data, int size)
        buffer->buffer [index++] = type;
        /* We should never change the header format */
        if (include_worker_index) {
+               int worker_index;
+               MonoNativeThreadId tid = mono_native_thread_id_get ();
                /*
                 * If the thread is not a worker thread we insert 0, which is interpreted
                 * as gc thread. Worker indexes are 1 based.
                 */
-               buffer->buffer [index++] = (unsigned char) sgen_thread_pool_is_thread_pool_thread (mono_native_thread_id_get ());
+               worker_index = sgen_workers_is_worker_thread (tid);
+               if (!worker_index)
+                       worker_index = sgen_thread_pool_is_thread_pool_thread (major_collector.get_sweep_pool (), tid);
+               /* FIXME Consider using different index bases for different thread pools */
+               buffer->buffer [index++] = (unsigned char) worker_index;
        }
        memcpy (buffer->buffer + index, data, size);
        index += size;
index 24a59f759a97602d2d1c0d99558201d775940bf9..4234559e9e8cec8ced5617f60fe64e9e9e862c38 100644 (file)
@@ -66,6 +66,7 @@ init_nursery (SgenFragmentAllocator *allocator, char *start, char *end)
 #define collector_pin_object(obj, queue) sgen_pin_object (obj, queue);
 #define COLLECTOR_SERIAL_ALLOC_FOR_PROMOTION alloc_for_promotion
 
+#define COPY_OR_MARK_PARALLEL
 #include "sgen-copy-object.h"
 
 #define SGEN_SIMPLE_NURSERY
@@ -80,6 +81,19 @@ fill_serial_ops (SgenObjectOperations *ops)
        FILL_MINOR_COLLECTOR_SCAN_OBJECT (ops);
 }
 
+#define SGEN_SIMPLE_PAR_NURSERY
+
+#include "sgen-minor-copy-object.h"
+#include "sgen-minor-scan-object.h"
+
+static void
+fill_parallel_ops (SgenObjectOperations *ops)
+{
+       ops->copy_or_mark_object = SERIAL_COPY_OBJECT;
+       FILL_MINOR_COLLECTOR_SCAN_OBJECT (ops);
+}
+
+#undef SGEN_SIMPLE_PAR_NURSERY
 #define SGEN_CONCURRENT_MAJOR
 
 #include "sgen-minor-copy-object.h"
@@ -93,9 +107,10 @@ fill_serial_with_concurrent_major_ops (SgenObjectOperations *ops)
 }
 
 void
-sgen_simple_nursery_init (SgenMinorCollector *collector)
+sgen_simple_nursery_init (SgenMinorCollector *collector, gboolean parallel)
 {
        collector->is_split = FALSE;
+       collector->is_parallel = parallel;
 
        collector->alloc_for_promotion = alloc_for_promotion;
 
@@ -108,6 +123,7 @@ sgen_simple_nursery_init (SgenMinorCollector *collector)
 
        fill_serial_ops (&collector->serial_ops);
        fill_serial_with_concurrent_major_ops (&collector->serial_ops_with_concurrent_major);
+       fill_parallel_ops (&collector->parallel_ops);
 }
 
 
index 078e1908d4e36855e3dfe2fd0202371793210911..38dc0c8c3a7a68b5b836e361ee0931471fcad54b 100644 (file)
@@ -452,6 +452,7 @@ void
 sgen_split_nursery_init (SgenMinorCollector *collector)
 {
        collector->is_split = TRUE;
+       collector->is_parallel = FALSE;
 
        collector->alloc_for_promotion = minor_alloc_for_promotion;
 
index 6f164d278c1b7d9b9a075c3de72506c0d2ba021a..a7abfad24472822f85e7f51632aa998cb913f05c 100644 (file)
 
 #include "mono/sgen/sgen-gc.h"
 #include "mono/sgen/sgen-thread-pool.h"
-#include "mono/sgen/sgen-pointer-queue.h"
 #include "mono/utils/mono-os-mutex.h"
-#ifndef SGEN_WITHOUT_MONO
-#include "mono/utils/mono-threads.h"
-#endif
-
-#define MAX_NUM_THREADS 8
-
-static mono_mutex_t lock;
-static mono_cond_t work_cond;
-static mono_cond_t done_cond;
-
-static int threads_num = 0;
-static MonoNativeThreadId threads [MAX_NUM_THREADS];
-
-/* Only accessed with the lock held. */
-static SgenPointerQueue job_queue;
-
-static SgenThreadPoolThreadInitFunc thread_init_func;
-static SgenThreadPoolIdleJobFunc idle_job_func;
-static SgenThreadPoolContinueIdleJobFunc continue_idle_job_func;
-static SgenThreadPoolShouldWorkFunc should_work_func;
-
-static volatile gboolean threadpool_shutdown;
-static volatile int threads_finished = 0;
 
 enum {
        STATE_WAITING,
@@ -46,10 +22,10 @@ enum {
 
 /* Assumes that the lock is held. */
 static SgenThreadPoolJob*
-get_job_and_set_in_progress (void)
+get_job_and_set_in_progress (SgenThreadPool *pool)
 {
-       for (size_t i = 0; i < job_queue.next_slot; ++i) {
-               SgenThreadPoolJob *job = (SgenThreadPoolJob *)job_queue.data [i];
+       for (size_t i = 0; i < pool->job_queue.next_slot; ++i) {
+               SgenThreadPoolJob *job = (SgenThreadPoolJob *)pool->job_queue.data [i];
                if (job->state == STATE_WAITING) {
                        job->state = STATE_IN_PROGRESS;
                        return job;
@@ -60,10 +36,10 @@ get_job_and_set_in_progress (void)
 
 /* Assumes that the lock is held. */
 static ssize_t
-find_job_in_queue (SgenThreadPoolJob *job)
+find_job_in_queue (SgenThreadPool *pool, SgenThreadPoolJob *job)
 {
-       for (ssize_t i = 0; i < job_queue.next_slot; ++i) {
-               if (job_queue.data [i] == job)
+       for (ssize_t i = 0; i < pool->job_queue.next_slot; ++i) {
+               if (pool->job_queue.data [i] == job)
                        return i;
        }
        return -1;
@@ -71,45 +47,47 @@ find_job_in_queue (SgenThreadPoolJob *job)
 
 /* Assumes that the lock is held. */
 static void
-remove_job (SgenThreadPoolJob *job)
+remove_job (SgenThreadPool *pool, SgenThreadPoolJob *job)
 {
        ssize_t index;
        SGEN_ASSERT (0, job->state == STATE_DONE, "Why are we removing a job that's not done?");
-       index = find_job_in_queue (job);
+       index = find_job_in_queue (pool, job);
        SGEN_ASSERT (0, index >= 0, "Why is the job we're trying to remove not in the queue?");
-       job_queue.data [index] = NULL;
-       sgen_pointer_queue_remove_nulls (&job_queue);
+       pool->job_queue.data [index] = NULL;
+       sgen_pointer_queue_remove_nulls (&pool->job_queue);
        sgen_thread_pool_job_free (job);
 }
 
 static gboolean
-continue_idle_job (void *thread_data)
+continue_idle_job (SgenThreadPool *pool, void *thread_data)
 {
-       if (!continue_idle_job_func)
+       if (!pool->continue_idle_job_func)
                return FALSE;
-       return continue_idle_job_func (thread_data);
+       return pool->continue_idle_job_func (thread_data);
 }
 
 static gboolean
-should_work (void *thread_data)
+should_work (SgenThreadPool *pool, void *thread_data)
 {
-       if (!should_work_func)
+       if (!pool->should_work_func)
                return TRUE;
-       return should_work_func (thread_data);
+       return pool->should_work_func (thread_data);
 }
 
 static mono_native_thread_return_t
-thread_func (void *thread_data)
+thread_func (SgenThreadPoolData *thread_data)
 {
-       thread_init_func (thread_data);
+       SgenThreadPool *pool = thread_data->pool;
+
+       pool->thread_init_func (thread_data);
 
-       mono_os_mutex_lock (&lock);
+       mono_os_mutex_lock (&pool->lock);
        for (;;) {
                gboolean do_idle;
                SgenThreadPoolJob *job;
 
-               if (!should_work (thread_data)) {
-                       mono_os_cond_wait (&work_cond, &lock);
+               if (!should_work (pool, thread_data) && !pool->threadpool_shutdown) {
+                       mono_os_cond_wait (&pool->work_cond, &pool->lock);
                        continue;
                }
                /*
@@ -118,51 +96,51 @@ thread_func (void *thread_data)
                 * main thread might then set continue idle and signal us before we can take
                 * the lock, and we'd lose the signal.
                 */
-               do_idle = continue_idle_job (thread_data);
-               job = get_job_and_set_in_progress ();
+               do_idle = continue_idle_job (pool, thread_data);
+               job = get_job_and_set_in_progress (pool);
 
-               if (!job && !do_idle && !threadpool_shutdown) {
+               if (!job && !do_idle && !pool->threadpool_shutdown) {
                        /*
                         * pthread_cond_wait() can return successfully despite the condition
                         * not being signalled, so we have to run this in a loop until we
                         * really have work to do.
                         */
-                       mono_os_cond_wait (&work_cond, &lock);
+                       mono_os_cond_wait (&pool->work_cond, &pool->lock);
                        continue;
                }
 
-               mono_os_mutex_unlock (&lock);
+               mono_os_mutex_unlock (&pool->lock);
 
                if (job) {
                        job->func (thread_data, job);
 
-                       mono_os_mutex_lock (&lock);
+                       mono_os_mutex_lock (&pool->lock);
 
                        SGEN_ASSERT (0, job->state == STATE_IN_PROGRESS, "The job should still be in progress.");
                        job->state = STATE_DONE;
-                       remove_job (job);
+                       remove_job (pool, job);
                        /*
                         * Only the main GC thread will ever wait on the done condition, so we don't
                         * have to broadcast.
                         */
-                       mono_os_cond_signal (&done_cond);
+                       mono_os_cond_signal (&pool->done_cond);
                } else if (do_idle) {
-                       SGEN_ASSERT (0, idle_job_func, "Why do we have idle work when there's no idle job function?");
+                       SGEN_ASSERT (0, pool->idle_job_func, "Why do we have idle work when there's no idle job function?");
                        do {
-                               idle_job_func (thread_data);
-                               do_idle = continue_idle_job (thread_data);
-                       } while (do_idle && !job_queue.next_slot);
+                               pool->idle_job_func (thread_data);
+                               do_idle = continue_idle_job (pool, thread_data);
+                       } while (do_idle && !pool->job_queue.next_slot);
 
-                       mono_os_mutex_lock (&lock);
+                       mono_os_mutex_lock (&pool->lock);
 
                        if (!do_idle)
-                               mono_os_cond_signal (&done_cond);
+                               mono_os_cond_signal (&pool->done_cond);
                } else {
-                       SGEN_ASSERT (0, threadpool_shutdown, "Why did we unlock if no jobs and not shutting down?");
-                       mono_os_mutex_lock (&lock);
-                       threads_finished++;
-                       mono_os_cond_signal (&done_cond);
-                       mono_os_mutex_unlock (&lock);
+                       SGEN_ASSERT (0, pool->threadpool_shutdown, "Why did we unlock if no jobs and not shutting down?");
+                       mono_os_mutex_lock (&pool->lock);
+                       pool->threads_finished++;
+                       mono_os_cond_signal (&pool->done_cond);
+                       mono_os_mutex_unlock (&pool->lock);
                        return 0;
                }
        }
@@ -171,41 +149,49 @@ thread_func (void *thread_data)
 }
 
 void
-sgen_thread_pool_init (int num_threads, SgenThreadPoolThreadInitFunc init_func, SgenThreadPoolIdleJobFunc idle_func, SgenThreadPoolContinueIdleJobFunc continue_idle_func, SgenThreadPoolShouldWorkFunc should_work_func_p, void **thread_datas)
+sgen_thread_pool_init (SgenThreadPool *pool, int num_threads, SgenThreadPoolThreadInitFunc init_func, SgenThreadPoolIdleJobFunc idle_func, SgenThreadPoolContinueIdleJobFunc continue_idle_func, SgenThreadPoolShouldWorkFunc should_work_func_p, SgenThreadPoolData **thread_datas)
 {
        int i;
 
-       threads_num = (num_threads < MAX_NUM_THREADS) ? num_threads : MAX_NUM_THREADS;
+       SGEN_ASSERT (0, num_threads > 0, "Why are we creating a threadpool with no threads?");
+
+       pool->threads_num = (num_threads < MAX_NUM_THREADS) ? num_threads : MAX_NUM_THREADS;
 
-       mono_os_mutex_init (&lock);
-       mono_os_cond_init (&work_cond);
-       mono_os_cond_init (&done_cond);
+       mono_os_mutex_init (&pool->lock);
+       mono_os_cond_init (&pool->work_cond);
+       mono_os_cond_init (&pool->done_cond);
 
-       thread_init_func = init_func;
-       idle_job_func = idle_func;
-       continue_idle_job_func = continue_idle_func;
-       should_work_func = should_work_func_p;
+       pool->thread_init_func = init_func;
+       pool->idle_job_func = idle_func;
+       pool->continue_idle_job_func = continue_idle_func;
+       pool->should_work_func = should_work_func_p;
 
-       for (i = 0; i < threads_num; i++)
-               mono_native_thread_create (&threads [i], thread_func, thread_datas ? thread_datas [i] : NULL);
+       sgen_pointer_queue_init (&pool->job_queue, 0);
+       pool->threads_finished = 0;
+       pool->threadpool_shutdown = FALSE;
+
+       for (i = 0; i < pool->threads_num; i++) {
+               thread_datas [i]->pool = pool;
+               mono_native_thread_create (&pool->threads [i], thread_func, thread_datas [i]);
+       }
 }
 
 void
-sgen_thread_pool_shutdown (void)
+sgen_thread_pool_shutdown (SgenThreadPool *pool)
 {
-       if (!threads_num)
+       if (!pool)
                return;
 
-       mono_os_mutex_lock (&lock);
-       threadpool_shutdown = TRUE;
-       mono_os_cond_broadcast (&work_cond);
-       while (threads_finished < threads_num)
-               mono_os_cond_wait (&done_cond, &lock);
-       mono_os_mutex_unlock (&lock);
+       mono_os_mutex_lock (&pool->lock);
+       pool->threadpool_shutdown = TRUE;
+       mono_os_cond_broadcast (&pool->work_cond);
+       while (pool->threads_finished < pool->threads_num)
+               mono_os_cond_wait (&pool->done_cond, &pool->lock);
+       mono_os_mutex_unlock (&pool->lock);
 
-       mono_os_mutex_destroy (&lock);
-       mono_os_cond_destroy (&work_cond);
-       mono_os_cond_destroy (&done_cond);
+       mono_os_mutex_destroy (&pool->lock);
+       mono_os_cond_destroy (&pool->work_cond);
+       mono_os_cond_destroy (&pool->done_cond);
 }
 
 SgenThreadPoolJob*
@@ -226,74 +212,77 @@ sgen_thread_pool_job_free (SgenThreadPoolJob *job)
 }
 
 void
-sgen_thread_pool_job_enqueue (SgenThreadPoolJob *job)
+sgen_thread_pool_job_enqueue (SgenThreadPool *pool, SgenThreadPoolJob *job)
 {
-       mono_os_mutex_lock (&lock);
+       mono_os_mutex_lock (&pool->lock);
 
-       sgen_pointer_queue_add (&job_queue, job);
-       mono_os_cond_signal (&work_cond);
+       sgen_pointer_queue_add (&pool->job_queue, job);
+       mono_os_cond_signal (&pool->work_cond);
 
-       mono_os_mutex_unlock (&lock);
+       mono_os_mutex_unlock (&pool->lock);
 }
 
 void
-sgen_thread_pool_job_wait (SgenThreadPoolJob *job)
+sgen_thread_pool_job_wait (SgenThreadPool *pool, SgenThreadPoolJob *job)
 {
        SGEN_ASSERT (0, job, "Where's the job?");
 
-       mono_os_mutex_lock (&lock);
+       mono_os_mutex_lock (&pool->lock);
 
-       while (find_job_in_queue (job) >= 0)
-               mono_os_cond_wait (&done_cond, &lock);
+       while (find_job_in_queue (pool, job) >= 0)
+               mono_os_cond_wait (&pool->done_cond, &pool->lock);
 
-       mono_os_mutex_unlock (&lock);
+       mono_os_mutex_unlock (&pool->lock);
 }
 
 void
-sgen_thread_pool_idle_signal (void)
+sgen_thread_pool_idle_signal (SgenThreadPool *pool)
 {
-       SGEN_ASSERT (0, idle_job_func, "Why are we signaling idle without an idle function?");
+       SGEN_ASSERT (0, pool->idle_job_func, "Why are we signaling idle without an idle function?");
 
-       mono_os_mutex_lock (&lock);
+       mono_os_mutex_lock (&pool->lock);
 
-       if (continue_idle_job_func (NULL))
-               mono_os_cond_broadcast (&work_cond);
+       if (pool->continue_idle_job_func (NULL))
+               mono_os_cond_broadcast (&pool->work_cond);
 
-       mono_os_mutex_unlock (&lock);
+       mono_os_mutex_unlock (&pool->lock);
 }
 
 void
-sgen_thread_pool_idle_wait (void)
+sgen_thread_pool_idle_wait (SgenThreadPool *pool)
 {
-       SGEN_ASSERT (0, idle_job_func, "Why are we waiting for idle without an idle function?");
+       SGEN_ASSERT (0, pool->idle_job_func, "Why are we waiting for idle without an idle function?");
 
-       mono_os_mutex_lock (&lock);
+       mono_os_mutex_lock (&pool->lock);
 
-       while (continue_idle_job_func (NULL))
-               mono_os_cond_wait (&done_cond, &lock);
+       while (pool->continue_idle_job_func (NULL))
+               mono_os_cond_wait (&pool->done_cond, &pool->lock);
 
-       mono_os_mutex_unlock (&lock);
+       mono_os_mutex_unlock (&pool->lock);
 }
 
 void
-sgen_thread_pool_wait_for_all_jobs (void)
+sgen_thread_pool_wait_for_all_jobs (SgenThreadPool *pool)
 {
-       mono_os_mutex_lock (&lock);
+       mono_os_mutex_lock (&pool->lock);
 
-       while (!sgen_pointer_queue_is_empty (&job_queue))
-               mono_os_cond_wait (&done_cond, &lock);
+       while (!sgen_pointer_queue_is_empty (&pool->job_queue))
+               mono_os_cond_wait (&pool->done_cond, &pool->lock);
 
-       mono_os_mutex_unlock (&lock);
+       mono_os_mutex_unlock (&pool->lock);
 }
 
 /* Return 0 if is not a thread pool thread or the thread number otherwise */
 int
-sgen_thread_pool_is_thread_pool_thread (MonoNativeThreadId some_thread)
+sgen_thread_pool_is_thread_pool_thread (SgenThreadPool *pool, MonoNativeThreadId some_thread)
 {
        int i;
 
-       for (i = 0; i < threads_num; i++) {
-               if (some_thread == threads [i])
+       if (!pool)
+               return 0;
+
+       for (i = 0; i < pool->threads_num; i++) {
+               if (some_thread == pool->threads [i])
                        return i + 1;
        }
 
index ce4bb11e321ee58668336c2a6989fa7ee71d4e6e..1b48e4433f040137bf37e0368f6b126b17a63c4e 100644 (file)
 #ifndef __MONO_SGEN_THREAD_POOL_H__
 #define __MONO_SGEN_THREAD_POOL_H__
 
+#include "mono/sgen/sgen-pointer-queue.h"
+#include "mono/utils/mono-threads.h"
+
 typedef struct _SgenThreadPoolJob SgenThreadPoolJob;
+typedef struct _SgenThreadPool SgenThreadPool;
+typedef struct _SgenThreadPoolData SgenThreadPoolData;
 
 typedef void (*SgenThreadPoolJobFunc) (void *thread_data, SgenThreadPoolJob *job);
+typedef void (*SgenThreadPoolThreadInitFunc) (void*);
+typedef void (*SgenThreadPoolIdleJobFunc) (void*);
+typedef gboolean (*SgenThreadPoolContinueIdleJobFunc) (void*);
+typedef gboolean (*SgenThreadPoolShouldWorkFunc) (void*);
 
 struct _SgenThreadPoolJob {
        const char *name;
@@ -21,28 +30,49 @@ struct _SgenThreadPoolJob {
        volatile gint32 state;
 };
 
-typedef void (*SgenThreadPoolThreadInitFunc) (void*);
-typedef void (*SgenThreadPoolIdleJobFunc) (void*);
-typedef gboolean (*SgenThreadPoolContinueIdleJobFunc) (void*);
-typedef gboolean (*SgenThreadPoolShouldWorkFunc) (void*);
+#define MAX_NUM_THREADS 8
+
+struct _SgenThreadPool {
+       mono_mutex_t lock;
+       mono_cond_t work_cond;
+       mono_cond_t done_cond;
+
+       int threads_num;
+       MonoNativeThreadId threads [MAX_NUM_THREADS];
+
+       /* Only accessed with the lock held. */
+       SgenPointerQueue job_queue;
+
+       SgenThreadPoolThreadInitFunc thread_init_func;
+       SgenThreadPoolIdleJobFunc idle_job_func;
+       SgenThreadPoolContinueIdleJobFunc continue_idle_job_func;
+       SgenThreadPoolShouldWorkFunc should_work_func;
+
+       volatile gboolean threadpool_shutdown;
+       volatile int threads_finished;
+};
+
+struct _SgenThreadPoolData {
+       SgenThreadPool *pool;
+};
 
-void sgen_thread_pool_init (int num_threads, SgenThreadPoolThreadInitFunc init_func, SgenThreadPoolIdleJobFunc idle_func, SgenThreadPoolContinueIdleJobFunc continue_idle_func, SgenThreadPoolShouldWorkFunc should_work_func, void **thread_datas);
+void sgen_thread_pool_init (SgenThreadPool *pool, int num_threads, SgenThreadPoolThreadInitFunc init_func, SgenThreadPoolIdleJobFunc idle_func, SgenThreadPoolContinueIdleJobFunc continue_idle_func, SgenThreadPoolShouldWorkFunc should_work_func, SgenThreadPoolData **thread_datas);
 
-void sgen_thread_pool_shutdown (void);
+void sgen_thread_pool_shutdown (SgenThreadPool *pool);
 
 SgenThreadPoolJob* sgen_thread_pool_job_alloc (const char *name, SgenThreadPoolJobFunc func, size_t size);
 /* This only needs to be called on jobs that are not enqueued. */
 void sgen_thread_pool_job_free (SgenThreadPoolJob *job);
 
-void sgen_thread_pool_job_enqueue (SgenThreadPoolJob *job);
+void sgen_thread_pool_job_enqueue (SgenThreadPool *pool, SgenThreadPoolJob *job);
 /* This must only be called after the job has been enqueued. */
-void sgen_thread_pool_job_wait (SgenThreadPoolJob *job);
+void sgen_thread_pool_job_wait (SgenThreadPool *pool, SgenThreadPoolJob *job);
 
-void sgen_thread_pool_idle_signal (void);
-void sgen_thread_pool_idle_wait (void);
+void sgen_thread_pool_idle_signal (SgenThreadPool *pool);
+void sgen_thread_pool_idle_wait (SgenThreadPool *pool);
 
-void sgen_thread_pool_wait_for_all_jobs (void);
+void sgen_thread_pool_wait_for_all_jobs (SgenThreadPool *pool);
 
-int sgen_thread_pool_is_thread_pool_thread (MonoNativeThreadId thread);
+int sgen_thread_pool_is_thread_pool_thread (SgenThreadPool *pool, MonoNativeThreadId thread);
 
 #endif
index f5169f45ab2e711c87ac1a59d41007ef8715b236..47fb00d9e1f8db39461cc62ff7a29c126ffedf61 100644 (file)
@@ -26,6 +26,9 @@ static volatile gboolean forced_stop;
 static WorkerData *workers_data;
 static SgenWorkerCallback worker_init_cb;
 
+static SgenThreadPool pool_inst;
+static SgenThreadPool *pool; /* null if we're not using workers */
+
 /*
  * When using multiple workers, we need to have the last worker
  * enqueue the preclean jobs (if there are any). This lock ensures
@@ -61,6 +64,8 @@ enum {
        STATE_WORK_ENQUEUED
 };
 
+#define SGEN_WORKER_MIN_SECTIONS_SIGNAL 4
+
 typedef gint32 State;
 
 static SgenObjectOperations * volatile idle_func_object_ops;
@@ -82,7 +87,7 @@ set_state (WorkerData *data, State old_state, State new_state)
        else if (new_state == STATE_WORKING)
                SGEN_ASSERT (0, old_state == STATE_WORK_ENQUEUED, "We can only transition to WORKING from WORK ENQUEUED");
        if (new_state == STATE_NOT_WORKING || new_state == STATE_WORKING)
-               SGEN_ASSERT (6, sgen_thread_pool_is_thread_pool_thread (mono_native_thread_id_get ()), "Only the worker thread is allowed to transition to NOT_WORKING or WORKING");
+               SGEN_ASSERT (6, sgen_thread_pool_is_thread_pool_thread (pool, mono_native_thread_id_get ()), "Only the worker thread is allowed to transition to NOT_WORKING or WORKING");
 
        return InterlockedCompareExchange (&data->state, new_state, old_state) == old_state;
 }
@@ -126,7 +131,7 @@ sgen_workers_ensure_awake (void)
        }
 
        if (need_signal)
-               sgen_thread_pool_idle_signal ();
+               sgen_thread_pool_idle_signal (pool);
 }
 
 static void
@@ -198,24 +203,23 @@ sgen_workers_enqueue_job (SgenThreadPoolJob *job, gboolean enqueue)
                return;
        }
 
-       sgen_thread_pool_job_enqueue (job);
+       sgen_thread_pool_job_enqueue (pool, job);
 }
 
 static gboolean
 workers_get_work (WorkerData *data)
 {
-       SgenMajorCollector *major;
+       SgenMajorCollector *major = sgen_get_major_collector ();
+       SgenMinorCollector *minor = sgen_get_minor_collector ();
+       GrayQueueSection *section;
 
        g_assert (sgen_gray_object_queue_is_empty (&data->private_gray_queue));
+       g_assert (major->is_concurrent || minor->is_parallel);
 
-       /* If we're concurrent, steal from the workers distribute gray queue. */
-       major = sgen_get_major_collector ();
-       if (major->is_concurrent) {
-               GrayQueueSection *section = sgen_section_gray_queue_dequeue (&workers_distribute_gray_queue);
-               if (section) {
-                       sgen_gray_object_enqueue_section (&data->private_gray_queue, section, major->is_parallel);
-                       return TRUE;
-               }
+       section = sgen_section_gray_queue_dequeue (&workers_distribute_gray_queue);
+       if (section) {
+               sgen_gray_object_enqueue_section (&data->private_gray_queue, section, major->is_parallel);
+               return TRUE;
        }
 
        /* Nobody to steal from */
@@ -227,10 +231,13 @@ static gboolean
 workers_steal_work (WorkerData *data)
 {
        SgenMajorCollector *major = sgen_get_major_collector ();
+       SgenMinorCollector *minor = sgen_get_minor_collector ();
+       int generation = sgen_get_current_collection_generation ();
        GrayQueueSection *section = NULL;
        int i, current_worker;
 
-       if (!major->is_parallel)
+       if ((generation == GENERATION_OLD && !major->is_parallel) ||
+                       (generation == GENERATION_NURSERY && !minor->is_parallel))
                return FALSE;
 
        /* If we're parallel, steal from other workers' private gray queues  */
@@ -275,10 +282,11 @@ thread_pool_init_func (void *data_untyped)
 {
        WorkerData *data = (WorkerData *)data_untyped;
        SgenMajorCollector *major = sgen_get_major_collector ();
+       SgenMinorCollector *minor = sgen_get_minor_collector ();
 
        sgen_client_thread_register_worker ();
 
-       if (!major->is_concurrent)
+       if (!major->is_concurrent && !minor->is_parallel)
                return;
 
        init_private_gray_queue (data);
@@ -314,7 +322,6 @@ marker_idle_func (void *data_untyped)
        WorkerData *data = (WorkerData *)data_untyped;
 
        SGEN_ASSERT (0, continue_idle_func (data_untyped), "Why are we called when we're not supposed to work?");
-       SGEN_ASSERT (0, sgen_concurrent_collection_in_progress (), "The worker should only mark in concurrent collections.");
 
        if (data->state == STATE_WORK_ENQUEUED) {
                set_state (data, STATE_WORK_ENQUEUED, STATE_WORKING);
@@ -328,7 +335,8 @@ marker_idle_func (void *data_untyped)
 
                sgen_drain_gray_stack (ctx);
 
-               if (data->private_gray_queue.num_sections > 16 && workers_finished && worker_awakenings < active_workers_num) {
+               if (data->private_gray_queue.num_sections >= SGEN_WORKER_MIN_SECTIONS_SIGNAL
+                               && workers_finished && worker_awakenings < active_workers_num) {
                        /* We bound the number of worker awakenings just to be sure */
                        worker_awakenings++;
                        mono_os_mutex_lock (&finished_lock);
@@ -357,7 +365,7 @@ init_distribute_gray_queue (void)
 void
 sgen_workers_init_distribute_gray_queue (void)
 {
-       SGEN_ASSERT (0, sgen_get_major_collector ()->is_concurrent,
+       SGEN_ASSERT (0, sgen_get_major_collector ()->is_concurrent || sgen_get_minor_collector ()->is_parallel,
                        "Why should we init the distribute gray queue if we don't need it?");
        init_distribute_gray_queue ();
 }
@@ -366,12 +374,7 @@ void
 sgen_workers_init (int num_workers, SgenWorkerCallback callback)
 {
        int i;
-       void **workers_data_ptrs = (void **)alloca(num_workers * sizeof(void *));
-
-       if (!sgen_get_major_collector ()->is_concurrent) {
-               sgen_thread_pool_init (num_workers, thread_pool_init_func, NULL, NULL, NULL, NULL);
-               return;
-       }
+       WorkerData **workers_data_ptrs = (WorkerData**)alloca(num_workers * sizeof(WorkerData*));
 
        mono_os_mutex_init (&finished_lock);
        //g_print ("initing %d workers\n", num_workers);
@@ -385,15 +388,23 @@ sgen_workers_init (int num_workers, SgenWorkerCallback callback)
        init_distribute_gray_queue ();
 
        for (i = 0; i < num_workers; ++i)
-               workers_data_ptrs [i] = (void *) &workers_data [i];
+               workers_data_ptrs [i] = &workers_data [i];
 
        worker_init_cb = callback;
 
-       sgen_thread_pool_init (num_workers, thread_pool_init_func, marker_idle_func, continue_idle_func, should_work_func, workers_data_ptrs);
+       pool = &pool_inst;
+       sgen_thread_pool_init (pool, num_workers, thread_pool_init_func, marker_idle_func, continue_idle_func, should_work_func, (SgenThreadPoolData**)workers_data_ptrs);
 
        mono_counters_register ("# workers finished", MONO_COUNTER_GC | MONO_COUNTER_ULONG, &stat_workers_num_finished);
 }
 
+void
+sgen_workers_shutdown (void)
+{
+       if (pool)
+               sgen_thread_pool_shutdown (pool);
+}
+
 void
 sgen_workers_stop_all_workers (void)
 {
@@ -401,8 +412,8 @@ sgen_workers_stop_all_workers (void)
        mono_memory_write_barrier ();
        forced_stop = TRUE;
 
-       sgen_thread_pool_wait_for_all_jobs ();
-       sgen_thread_pool_idle_wait ();
+       sgen_thread_pool_wait_for_all_jobs (pool);
+       sgen_thread_pool_idle_wait (pool);
        SGEN_ASSERT (0, sgen_workers_all_done (), "Can only signal enqueue work when in no work state");
 }
 
@@ -441,8 +452,8 @@ sgen_workers_join (void)
 {
        int i;
 
-       sgen_thread_pool_wait_for_all_jobs ();
-       sgen_thread_pool_idle_wait ();
+       sgen_thread_pool_wait_for_all_jobs (pool);
+       sgen_thread_pool_idle_wait (pool);
        SGEN_ASSERT (0, sgen_workers_all_done (), "Can only signal enqueue work when in no work state");
 
        /* At this point all the workers have stopped. */
@@ -541,4 +552,12 @@ sgen_workers_foreach (SgenWorkerCallback callback)
                callback (&workers_data [i]);
 }
 
+gboolean
+sgen_workers_is_worker_thread (MonoNativeThreadId id)
+{
+       if (!pool)
+               return FALSE;
+       return sgen_thread_pool_is_thread_pool_thread (pool, id);
+}
+
 #endif
index e2f030dc159135210d50d80fd1e60a5799e7291a..78dea19867d645d76b7fe73dfcf81c04981da7a2 100644 (file)
 
 typedef struct _WorkerData WorkerData;
 struct _WorkerData {
+       /*
+        * Threadpool threads receive as their starting argument a WorkerData.
+        * tp_data is meant for use inside the sgen thread pool and must be first.
+        */
+       SgenThreadPoolData tp_data;
        gint32 state;
        SgenGrayQueue private_gray_queue; /* only read/written by worker thread */
        /*
@@ -30,6 +35,7 @@ typedef void (*SgenWorkersFinishCallback) (void);
 typedef void (*SgenWorkerCallback) (WorkerData *data);
 
 void sgen_workers_init (int num_workers, SgenWorkerCallback callback);
+void sgen_workers_shutdown (void);
 void sgen_workers_stop_all_workers (void);
 void sgen_workers_set_num_active_workers (int num_workers);
 void sgen_workers_start_all_workers (SgenObjectOperations *object_ops_nopar, SgenObjectOperations *object_ops_par, SgenWorkersFinishCallback finish_job);
@@ -46,5 +52,6 @@ void sgen_workers_take_from_queue (SgenGrayQueue *queue);
 SgenObjectOperations* sgen_workers_get_idle_func_object_ops (void);
 int sgen_workers_get_job_split_count (void);
 void sgen_workers_foreach (SgenWorkerCallback callback);
+gboolean sgen_workers_is_worker_thread (MonoNativeThreadId id);
 
 #endif