[runtime] Further removed code that doubted IMT
[mono.git] / mono / metadata / sgen-marksweep.c
index 892dcfbb3af110cf104d72066b5fe3bd2326cc31..bd2d3bbbbcf946e218802a4996f4f923e7d26b9a 100644 (file)
@@ -43,6 +43,7 @@
 #include "metadata/sgen-pointer-queue.h"
 #include "metadata/sgen-pinning.h"
 #include "metadata/sgen-workers.h"
+#include "metadata/sgen-thread-pool.h"
 
 #if defined(ARCH_MIN_MS_BLOCK_SIZE) && defined(ARCH_MIN_MS_BLOCK_SIZE_SHIFT)
 #define MS_BLOCK_SIZE  ARCH_MIN_MS_BLOCK_SIZE
@@ -188,6 +189,7 @@ enum {
 static volatile int sweep_state = SWEEP_STATE_SWEPT;
 
 static gboolean concurrent_mark;
+static gboolean concurrent_sweep = TRUE;
 
 #define BLOCK_IS_TAGGED_HAS_REFERENCES(bl)     SGEN_POINTER_IS_TAGGED_1 ((bl))
 #define BLOCK_TAG_HAS_REFERENCES(bl)           SGEN_POINTER_TAG_1 ((bl))
@@ -206,8 +208,20 @@ static SgenPointerQueue allocated_blocks;
 static void *empty_blocks = NULL;
 static size_t num_empty_blocks = 0;
 
-#define FOREACH_BLOCK_NO_LOCK(bl)      { size_t __index; SGEN_ASSERT (0, sgen_is_world_stopped () && !sweep_in_progress (), "Can't iterate blocks while the world is running or sweep is in progress."); for (__index = 0; __index < allocated_blocks.next_slot; ++__index) { (bl) = BLOCK_UNTAG (allocated_blocks.data [__index]);
-#define FOREACH_BLOCK_HAS_REFERENCES_NO_LOCK(bl,hr)    { size_t __index; SGEN_ASSERT (0, sgen_is_world_stopped () && !sweep_in_progress (), "Can't iterate blocks while the world is running or sweep is in progress."); for (__index = 0; __index < allocated_blocks.next_slot; ++__index) { (bl) = allocated_blocks.data [__index]; (hr) = BLOCK_IS_TAGGED_HAS_REFERENCES ((bl)); (bl) = BLOCK_UNTAG ((bl));
+#define FOREACH_BLOCK_NO_LOCK_CONDITION(cond,bl) {                     \
+       size_t __index;                                                 \
+       SGEN_ASSERT (0, (cond) && !sweep_in_progress (), "Can't iterate blocks while the world is running or sweep is in progress."); \
+       for (__index = 0; __index < allocated_blocks.next_slot; ++__index) { \
+               (bl) = BLOCK_UNTAG (allocated_blocks.data [__index]);
+#define FOREACH_BLOCK_NO_LOCK(bl)                                      \
+       FOREACH_BLOCK_NO_LOCK_CONDITION(sgen_is_world_stopped (), bl)
+#define FOREACH_BLOCK_HAS_REFERENCES_NO_LOCK(bl,hr) {                  \
+       size_t __index;                                                 \
+       SGEN_ASSERT (0, sgen_is_world_stopped () && !sweep_in_progress (), "Can't iterate blocks while the world is running or sweep is in progress."); \
+       for (__index = 0; __index < allocated_blocks.next_slot; ++__index) { \
+               (bl) = allocated_blocks.data [__index];                 \
+               (hr) = BLOCK_IS_TAGGED_HAS_REFERENCES ((bl));           \
+               (bl) = BLOCK_UNTAG ((bl));
 #define END_FOREACH_BLOCK_NO_LOCK      } }
 
 static volatile size_t num_major_sections = 0;
@@ -525,8 +539,7 @@ ms_alloc_block (int size_index, gboolean pinned, gboolean has_references)
         */
        info->is_to_space = (sgen_get_current_collection_generation () == GENERATION_OLD);
        info->state = (info->is_to_space || sgen_concurrent_collection_in_progress ()) ? BLOCK_STATE_MARKING : BLOCK_STATE_SWEPT;
-       if (sweep_in_progress ())
-               SGEN_ASSERT (0, info->state == BLOCK_STATE_SWEPT, "How do we add a new block to be swept while sweeping?");
+       SGEN_ASSERT (6, !sweep_in_progress () || info->state == BLOCK_STATE_SWEPT, "How do we add a new block to be swept while sweeping?");
        info->cardtable_mod_union = NULL;
 
        update_heap_boundaries_for_block (info);
@@ -613,7 +626,7 @@ unlink_slot_from_free_list_uncontested (MSBlockInfo * volatile *free_blocks, int
        ensure_can_access_block_free_list (block);
 
        obj = block->free_list;
-       SGEN_ASSERT (0, obj, "block %p in free list had no available object to alloc from", block);
+       SGEN_ASSERT (6, obj, "block %p in free list had no available object to alloc from", block);
 
        next_free_slot = *(void**)obj;
        if (next_free_slot) {
@@ -670,7 +683,7 @@ free_object (char *obj, size_t size, gboolean pinned)
        int word, bit;
        gboolean in_free_list;
 
-       SGEN_ASSERT (0, sweep_state == SWEEP_STATE_SWEPT, "Should have waited for sweep to free objects.");
+       SGEN_ASSERT (9, sweep_state == SWEEP_STATE_SWEPT, "Should have waited for sweep to free objects.");
 
        ensure_can_access_block_free_list (block);
        SGEN_ASSERT (9, (pinned && block->pinned) || (!pinned && !block->pinned), "free-object pinning mixup object %p pinned %d block %p pinned %d", obj, pinned, block, block->pinned);
@@ -802,10 +815,13 @@ set_sweep_state (int new, int expected)
 
 static gboolean ensure_block_is_checked_for_sweeping (int block_index, gboolean wait, gboolean *have_checked);
 
+static SgenThreadPoolJob * volatile sweep_job;
+
 static void
 major_finish_sweep_checking (void)
 {
        int block_index;
+       SgenThreadPoolJob *job;
 
  retry:
        switch (sweep_state) {
@@ -820,8 +836,7 @@ major_finish_sweep_checking (void)
                SGEN_ASSERT (0, FALSE, "Is there another minor collection running?");
                goto retry;
        case SWEEP_STATE_COMPACTING:
-               g_usleep (100);
-               goto retry;
+               goto wait;
        default:
                SGEN_ASSERT (0, FALSE, "Invalid sweep state.");
                break;
@@ -836,8 +851,13 @@ major_finish_sweep_checking (void)
                ensure_block_is_checked_for_sweeping (block_index, FALSE, NULL);
 
        set_sweep_state (SWEEP_STATE_SWEEPING, SWEEP_STATE_SWEEPING_AND_ITERATING);
-       while (sweep_state != SWEEP_STATE_SWEPT)
-               g_usleep (100);
+
+ wait:
+       job = sweep_job;
+       if (job)
+               sgen_thread_pool_job_wait (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?");
 }
 
 static void
@@ -859,7 +879,7 @@ major_iterate_objects (IterateObjectsFlags flags, IterateObjectCallbackFunc call
                        continue;
                if (sweep && lazy_sweep) {
                        sweep_block (block);
-                       SGEN_ASSERT (0, block->state == BLOCK_STATE_SWEPT, "Block must be swept after sweeping");
+                       SGEN_ASSERT (6, block->state == BLOCK_STATE_SWEPT, "Block must be swept after sweeping");
                }
 
                for (i = 0; i < count; ++i) {
@@ -872,7 +892,7 @@ major_iterate_objects (IterateObjectsFlags flags, IterateObjectCallbackFunc call
                         */
                        if (!block_is_swept_or_marking (block)) {
                                int word, bit;
-                               SGEN_ASSERT (0, !sweep && block->state == BLOCK_STATE_NEED_SWEEPING, "Has sweeping not finished?");
+                               SGEN_ASSERT (6, !sweep && block->state == BLOCK_STATE_NEED_SWEEPING, "Has sweeping not finished?");
                                MS_CALC_MARK_BIT (word, bit, obj);
                                if (!MS_MARK_BIT (block, word, bit))
                                        continue;
@@ -1056,7 +1076,7 @@ static void
 major_copy_or_mark_object_with_evacuation_concurrent (void **ptr, void *obj, SgenGrayQueue *queue)
 {
        SGEN_ASSERT (9, sgen_concurrent_collection_in_progress (), "Why are we scanning concurrently when there's no concurrent collection on?");
-       SGEN_ASSERT (9, !sgen_workers_are_working () || sgen_is_worker_thread (mono_native_thread_id_get ()), "We must not scan from two threads at the same time!");
+       SGEN_ASSERT (9, !sgen_workers_are_working () || sgen_thread_pool_is_thread_pool_thread (mono_native_thread_id_get ()), "We must not scan from two threads at the same time!");
 
        g_assert (!SGEN_OBJECT_IS_FORWARDED (obj));
 
@@ -1235,7 +1255,7 @@ sweep_block_for_size (MSBlockInfo *block, int count, int obj_size)
        }
 }
 
-static gboolean
+static inline gboolean
 try_set_block_state (MSBlockInfo *block, gint32 new_state, gint32 expected_state)
 {
        gint32 old_state = SGEN_CAS (&block->state, new_state, expected_state);
@@ -1245,15 +1265,11 @@ try_set_block_state (MSBlockInfo *block, gint32 new_state, gint32 expected_state
        return success;
 }
 
-/*
- * FIXME: This only CASes to catch errors.  It's not needed for correctness.
- */
-static void
+static inline void
 set_block_state (MSBlockInfo *block, gint32 new_state, gint32 expected_state)
 {
-       gboolean success = try_set_block_state (block, new_state, expected_state);
-       SGEN_ASSERT (0, success, "Couldn't set block state");
-       SGEN_ASSERT (0, block->state == new_state, "Block state incorrect after set");
+       SGEN_ASSERT (6, block->state == expected_state, "Block state incorrect before set");
+       block->state = new_state;
 }
 
 /*
@@ -1288,7 +1304,7 @@ sweep_block (MSBlockInfo *block)
                SGEN_ASSERT (0, FALSE, "Illegal block state");
        }
 
-       SGEN_ASSERT (0, block->state == BLOCK_STATE_SWEEPING, "How did we get here without setting state to sweeping?");
+       SGEN_ASSERT (6, block->state == BLOCK_STATE_SWEEPING, "How did we get here without setting state to sweeping?");
 
        count = MS_BLOCK_FREE / block->obj_size;
 
@@ -1390,7 +1406,7 @@ ensure_block_is_checked_for_sweeping (int block_index, gboolean wait, gboolean *
        void *tagged_block;
        MSBlockInfo *block;
 
-       SGEN_ASSERT (0, sweep_in_progress (), "Why do we call this function if there's no sweep in progress?");
+       SGEN_ASSERT (6, sweep_in_progress (), "Why do we call this function if there's no sweep in progress?");
 
        if (have_checked)
                *have_checked = FALSE;
@@ -1415,9 +1431,9 @@ ensure_block_is_checked_for_sweeping (int block_index, gboolean wait, gboolean *
        block_state = block->state;
 
        if (!sweep_in_progress ()) {
-               SGEN_ASSERT (0, block_state != BLOCK_STATE_SWEEPING && block_state != BLOCK_STATE_CHECKING, "Invalid block state.");
+               SGEN_ASSERT (6, block_state != BLOCK_STATE_SWEEPING && block_state != BLOCK_STATE_CHECKING, "Invalid block state.");
                if (!lazy_sweep)
-                       SGEN_ASSERT (0, block_state != BLOCK_STATE_NEED_SWEEPING, "Invalid block state.");
+                       SGEN_ASSERT (6, block_state != BLOCK_STATE_NEED_SWEEPING, "Invalid block state.");
        }
 
        switch (block_state) {
@@ -1435,7 +1451,7 @@ ensure_block_is_checked_for_sweeping (int block_index, gboolean wait, gboolean *
                break;
        }
 
-       SGEN_ASSERT (0, block->state == BLOCK_STATE_MARKING, "When we sweep all blocks must start out marking.");
+       SGEN_ASSERT (6, block->state == BLOCK_STATE_MARKING, "When we sweep all blocks must start out marking.");
        set_block_state (block, BLOCK_STATE_CHECKING, BLOCK_STATE_MARKING);
 
        if (have_checked)
@@ -1489,7 +1505,7 @@ ensure_block_is_checked_for_sweeping (int block_index, gboolean wait, gboolean *
                        MSBlockInfo * volatile *free_blocks = FREE_BLOCKS (block->pinned, block->has_references);
 
                        if (!lazy_sweep)
-                               SGEN_ASSERT (0, block->free_list, "How do we not have a free list when there are free slots?");
+                               SGEN_ASSERT (6, block->free_list, "How do we not have a free list when there are free slots?");
 
                        add_free_block (free_blocks, obj_size_index, block);
                }
@@ -1501,8 +1517,8 @@ ensure_block_is_checked_for_sweeping (int block_index, gboolean wait, gboolean *
                 * Blocks without live objects are removed from the
                 * block list and freed.
                 */
-               SGEN_ASSERT (0, block_index < allocated_blocks.next_slot, "How did the number of blocks shrink?");
-               SGEN_ASSERT (0, allocated_blocks.data [block_index] == BLOCK_TAG_CHECKING (tagged_block), "How did the block move?");
+               SGEN_ASSERT (6, block_index < allocated_blocks.next_slot, "How did the number of blocks shrink?");
+               SGEN_ASSERT (6, allocated_blocks.data [block_index] == BLOCK_TAG_CHECKING (tagged_block), "How did the block move?");
 
                binary_protocol_empty (MS_BLOCK_OBJ (block, 0), (char*)MS_BLOCK_OBJ (block, count) - (char*)MS_BLOCK_OBJ (block, 0));
                ms_free_block (block);
@@ -1517,12 +1533,11 @@ ensure_block_is_checked_for_sweeping (int block_index, gboolean wait, gboolean *
        return !!tagged_block;
 }
 
-static mono_native_thread_return_t
-sweep_loop_thread_func (void *dummy)
+static void
+sweep_job_func (void *thread_data_untyped, SgenThreadPoolJob *job)
 {
        int block_index;
        int num_blocks = num_major_sections_before_sweep;
-       int small_id = mono_thread_info_register_small_id ();
 
        SGEN_ASSERT (0, sweep_in_progress (), "Sweep thread called with wrong state");
        SGEN_ASSERT (0, num_blocks <= allocated_blocks.next_slot, "How did we lose blocks?");
@@ -1543,8 +1558,14 @@ sweep_loop_thread_func (void *dummy)
                        ++num_major_sections_freed_in_sweep;
        }
 
-       while (!try_set_sweep_state (SWEEP_STATE_COMPACTING, SWEEP_STATE_SWEEPING))
+       while (!try_set_sweep_state (SWEEP_STATE_COMPACTING, SWEEP_STATE_SWEEPING)) {
+               /*
+                * The main GC thread is currently iterating over the block array to help us
+                * finish the sweep.  We have already finished, but we don't want to mess up
+                * that iteration, so we just wait for it.
+                */
                g_usleep (100);
+       }
 
        if (SGEN_MAX_ASSERT_LEVEL >= 6) {
                for (block_index = num_blocks; block_index < allocated_blocks.next_slot; ++block_index) {
@@ -1557,9 +1578,7 @@ sweep_loop_thread_func (void *dummy)
 
        sweep_finish ();
 
-       mono_thread_small_id_free (small_id);
-
-       return NULL;
+       sweep_job = NULL;
 }
 
 static void
@@ -1593,8 +1612,6 @@ sweep_finish (void)
        set_sweep_state (SWEEP_STATE_SWEPT, SWEEP_STATE_COMPACTING);
 }
 
-static MonoNativeThreadId sweep_loop_thread;
-
 static void
 major_sweep (void)
 {
@@ -1607,14 +1624,12 @@ major_sweep (void)
        num_major_sections_before_sweep = num_major_sections;
        num_major_sections_freed_in_sweep = 0;
 
-       if (TRUE /*concurrent_mark*/) {
-               /*
-                * FIXME: We can't create a thread while the world is stopped because it
-                * might deadlock.  `finalizer-wait.exe` exposes this.
-                */
-               mono_native_thread_create (&sweep_loop_thread, sweep_loop_thread_func, NULL);
+       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);
        } else {
-               sweep_loop_thread_func (NULL);
+               sweep_job_func (NULL, NULL);
        }
 }
 
@@ -1721,9 +1736,6 @@ major_start_nursery_collection (void)
 #endif
 
        old_num_major_sections = num_major_sections;
-
-       if (sweep_in_progress ())
-               g_print ("sweeping during nursery collection\n");
 }
 
 static void
@@ -1740,7 +1752,7 @@ major_start_major_collection (void)
        MSBlockInfo *block;
        int i;
 
-       SGEN_ASSERT (0, sweep_state == SWEEP_STATE_SWEPT, "Major collection on unswept heap");
+       major_finish_sweep_checking ();
 
        /*
         * Clear the free lists for block sizes where we do evacuation.  For those block
@@ -1802,10 +1814,10 @@ compare_pointers (const void *va, const void *vb) {
  * This is called with sweep completed and the world stopped.
  */
 static void
-major_free_swept_blocks (void)
+major_free_swept_blocks (size_t allowance)
 {
-       /* FIXME: use something sensible here. */
-       size_t section_reserve = DEFAULT_NURSERY_SIZE * 2 / MS_BLOCK_SIZE;
+       /* FIXME: This is probably too much.  It's assuming all objects are small. */
+       size_t section_reserve = allowance / MS_BLOCK_SIZE;
 
        SGEN_ASSERT (0, sweep_state == SWEEP_STATE_SWEPT, "Sweeping must have finished before freeing blocks");
 
@@ -1858,7 +1870,7 @@ major_free_swept_blocks (void)
                        for (i = 0; i < arr_length; ++i) {
                                int d = dest;
                                void *block = empty_block_arr [i];
-                               SGEN_ASSERT (0, block, "we're not shifting correctly");
+                               SGEN_ASSERT (6, block, "we're not shifting correctly");
                                if (i != dest) {
                                        empty_block_arr [dest] = block;
                                        /*
@@ -1874,7 +1886,7 @@ major_free_swept_blocks (void)
                                        continue;
                                }
 
-                               SGEN_ASSERT (0, first >= 0 && d > first, "algorithm is wrong");
+                               SGEN_ASSERT (6, first >= 0 && d > first, "algorithm is wrong");
 
                                if ((char*)block != ((char*)empty_block_arr [d-1]) + MS_BLOCK_SIZE) {
                                        first = d;
@@ -1907,9 +1919,9 @@ major_free_swept_blocks (void)
                                }
                        }
 
-                       SGEN_ASSERT (0, dest <= i && dest <= arr_length, "array length is off");
+                       SGEN_ASSERT (6, dest <= i && dest <= arr_length, "array length is off");
                        arr_length = dest;
-                       SGEN_ASSERT (0, arr_length == num_empty_blocks, "array length is off");
+                       SGEN_ASSERT (6, arr_length == num_empty_blocks, "array length is off");
 
                        num_blocks >>= 1;
                }
@@ -1918,7 +1930,7 @@ major_free_swept_blocks (void)
                rebuild_next = (void**)&empty_blocks;
                for (i = 0; i < arr_length; ++i) {
                        void *block = empty_block_arr [i];
-                       SGEN_ASSERT (0, block, "we're missing blocks");
+                       SGEN_ASSERT (6, block, "we're missing blocks");
                        *rebuild_next = block;
                        rebuild_next = (void**)block;
                }
@@ -1963,7 +1975,7 @@ major_pin_objects (SgenGrayQueue *queue)
 
        FOREACH_BLOCK_NO_LOCK (block) {
                size_t first_entry, last_entry;
-               SGEN_ASSERT (0, block_is_swept_or_marking (block), "All blocks must be swept when we're pinning.");
+               SGEN_ASSERT (6, block_is_swept_or_marking (block), "All blocks must be swept when we're pinning.");
                sgen_find_optimized_pin_queue_area (MS_BLOCK_FOR_BLOCK_INFO (block) + MS_BLOCK_SKIP, MS_BLOCK_FOR_BLOCK_INFO (block) + MS_BLOCK_SIZE,
                                &first_entry, &last_entry);
                mark_pinned_objects_in_block (block, first_entry, last_entry, queue);
@@ -1987,7 +1999,13 @@ major_get_used_size (void)
        gint64 size = 0;
        MSBlockInfo *block;
 
-       FOREACH_BLOCK_NO_LOCK (block) {
+       /*
+        * We're holding the GC lock, but the sweep thread might be running.  Make sure it's
+        * finished, then we can iterate over the block array.
+        */
+       major_finish_sweep_checking ();
+
+       FOREACH_BLOCK_NO_LOCK_CONDITION (TRUE, block) {
                int count = MS_BLOCK_FREE / block->obj_size;
                void **iter;
                size += count * block->obj_size;
@@ -2035,6 +2053,12 @@ major_handle_gc_param (const char *opt)
        } else if (!strcmp (opt, "no-lazy-sweep")) {
                lazy_sweep = FALSE;
                return TRUE;
+       } else if (!strcmp (opt, "concurrent-sweep")) {
+               concurrent_sweep = TRUE;
+               return TRUE;
+       } else if (!strcmp (opt, "no-concurrent-sweep")) {
+               concurrent_sweep = FALSE;
+               return TRUE;
        }
 
        return FALSE;
@@ -2047,6 +2071,7 @@ major_print_gc_param_usage (void)
                        ""
                        "  evacuation-threshold=P (where P is a percentage, an integer in 0-100)\n"
                        "  (no-)lazy-sweep\n"
+                       "  (no-)concurrent-sweep\n"
                        );
 }
 
@@ -2313,7 +2338,7 @@ update_cardtable_mod_union (void)
                block->cardtable_mod_union = sgen_card_table_update_mod_union (block->cardtable_mod_union,
                                MS_BLOCK_FOR_BLOCK_INFO (block), MS_BLOCK_SIZE, &num_cards);
 
-               SGEN_ASSERT (0, num_cards == CARDS_PER_BLOCK, "Number of cards calculation is wrong");
+               SGEN_ASSERT (6, num_cards == CARDS_PER_BLOCK, "Number of cards calculation is wrong");
        } END_FOREACH_BLOCK_NO_LOCK;
 }
 
@@ -2331,6 +2356,7 @@ static void
 post_param_init (SgenMajorCollector *collector)
 {
        collector->sweeps_lazily = lazy_sweep;
+       collector->needs_thread_pool = concurrent_mark || concurrent_sweep;
 }
 
 static void
@@ -2383,13 +2409,12 @@ sgen_marksweep_init_internal (SgenMajorCollector *collector, gboolean is_concurr
        collector->section_size = MAJOR_SECTION_SIZE;
 
        concurrent_mark = is_concurrent;
-       if (is_concurrent) {
-               collector->is_concurrent = TRUE;
+       collector->is_concurrent = is_concurrent;
+       collector->needs_thread_pool = is_concurrent || concurrent_sweep;
+       if (is_concurrent)
                collector->want_synchronous_collection = &want_evacuation;
-       } else {
-               collector->is_concurrent = FALSE;
+       else
                collector->want_synchronous_collection = NULL;
-       }
        collector->get_and_reset_num_major_objects_marked = major_get_and_reset_num_major_objects_marked;
        collector->supports_cardtable = TRUE;