diff options
-rw-r--r-- | gc.c | 84 | ||||
-rw-r--r-- | test/ruby/test_gc_compact.rb | 18 |
2 files changed, 86 insertions, 16 deletions
@@ -11054,6 +11054,39 @@ gc_compact(VALUE self) #endif #if GC_CAN_COMPILE_COMPACTION + +struct desired_compaction_pages_i_data { + rb_objspace_t *objspace; + size_t required_slots[SIZE_POOL_COUNT]; +}; + +static int +desired_compaction_pages_i(struct heap_page *page, void *data) +{ + struct desired_compaction_pages_i_data *tdata = data; + rb_objspace_t *objspace = tdata->objspace; + VALUE vstart = (VALUE)page->start; + VALUE vend = vstart + (VALUE)(page->total_slots * page->size_pool->slot_size); + + + for (VALUE v = vstart; v != vend; v += page->size_pool->slot_size) { + /* skip T_NONEs; they won't be moved */ + void *poisoned = asan_unpoison_object_temporary(v); + if (BUILTIN_TYPE(v) == T_NONE) { + if (poisoned) { + asan_poison_object(v); + } + continue; + } + + rb_size_pool_t *dest_pool = gc_compact_destination_pool(objspace, page->size_pool, v); + size_t dest_pool_idx = dest_pool - size_pools; + tdata->required_slots[dest_pool_idx]++; + } + + return 0; +} + static VALUE gc_verify_compaction_references(rb_execution_context_t *ec, VALUE self, VALUE double_heap, VALUE expand_heap, VALUE toward_empty) { @@ -11071,18 +11104,55 @@ gc_verify_compaction_references(rb_execution_context_t *ec, VALUE self, VALUE do gc_rest(objspace); /* if both double_heap and expand_heap are set, expand_heap takes precedence */ - if (RTEST(double_heap) || RTEST(expand_heap)) { + if (RTEST(expand_heap)) { + struct desired_compaction_pages_i_data desired_compaction = { + .objspace = objspace, + .required_slots = {0}, + }; + /* Work out how many objects want to be in each size pool, taking account of moves */ + objspace_each_pages(objspace, desired_compaction_pages_i, &desired_compaction, TRUE); + + /* Find out which pool has the most pages */ + size_t max_existing_pages = 0; + for(int i = 0; i < SIZE_POOL_COUNT; i++) { + rb_size_pool_t *size_pool = &size_pools[i]; + rb_heap_t *heap = SIZE_POOL_EDEN_HEAP(size_pool); + max_existing_pages = MAX(max_existing_pages, heap->total_pages); + } + /* Add pages to each size pool so that compaction is guaranteed to move every object */ for (int i = 0; i < SIZE_POOL_COUNT; i++) { rb_size_pool_t *size_pool = &size_pools[i]; rb_heap_t *heap = SIZE_POOL_EDEN_HEAP(size_pool); - size_t minimum_pages = 0; - if (RTEST(expand_heap)) { - minimum_pages = minimum_pages_for_size_pool(objspace, size_pool); - } - - heap_add_pages(objspace, size_pool, heap, MAX(minimum_pages, heap->total_pages)); + size_t pages_to_add = 0; + /* + * Step 1: Make sure every pool has the same number of pages, by adding empty pages + * to smaller pools. This is required to make sure the compact cursor can advance + * through all of the pools in `gc_sweep_compact` without hitting the "sweep & + * compact cursors met" condition on some pools before fully compacting others + */ + pages_to_add += max_existing_pages - heap->total_pages; + /* + * Step 2: Now add additional free pages to each size pool sufficient to hold all objects + * that want to be in that size pool, whether moved into it or moved within it + */ + pages_to_add += slots_to_pages_for_size_pool(objspace, size_pool, desired_compaction.required_slots[i]); + /* + * Step 3: Add two more pages so that the compact & sweep cursors will meet _after_ all objects + * have been moved, and not on the last iteration of the `gc_sweep_compact` loop + */ + pages_to_add += 2; + + heap_add_pages(objspace, size_pool, heap, pages_to_add); + } + } + else if (RTEST(double_heap)) { + for (int i = 0; i < SIZE_POOL_COUNT; i++) { + rb_size_pool_t *size_pool = &size_pools[i]; + rb_heap_t *heap = SIZE_POOL_EDEN_HEAP(size_pool); + heap_add_pages(objspace, size_pool, heap, heap->total_pages); } + } if (RTEST(toward_empty)) { diff --git a/test/ruby/test_gc_compact.rb b/test/ruby/test_gc_compact.rb index 6d26811cbb..a20e2d67d9 100644 --- a/test/ruby/test_gc_compact.rb +++ b/test/ruby/test_gc_compact.rb @@ -389,14 +389,14 @@ class TestGCCompact < Test::Unit::TestCase def test_moving_strings_up_size_pools omit if GC::INTERNAL_CONSTANTS[:SIZE_POOL_COUNT] == 1 - assert_separately(%w[-robjspace], "#{<<~"begin;"}\n#{<<~"end;"}", timeout: 10, signal: :SEGV) + assert_separately(%w[-robjspace], "#{<<~"begin;"}\n#{<<~"end;"}", timeout: 30, signal: :SEGV) begin; - STR_COUNT = 500 + STR_COUNT = 50000 GC.verify_compaction_references(expand_heap: true, toward: :empty) Fiber.new { - str = "a" * GC::INTERNAL_CONSTANTS[:BASE_SLOT_SIZE] + str = "a" * GC::INTERNAL_CONSTANTS[:BASE_SLOT_SIZE] * 4 $ary = STR_COUNT.times.map { "" << str } }.resume @@ -410,14 +410,14 @@ class TestGCCompact < Test::Unit::TestCase def test_moving_strings_down_size_pools omit if GC::INTERNAL_CONSTANTS[:SIZE_POOL_COUNT] == 1 - assert_separately(%w[-robjspace], "#{<<~"begin;"}\n#{<<~"end;"}", timeout: 10, signal: :SEGV) + assert_separately(%w[-robjspace], "#{<<~"begin;"}\n#{<<~"end;"}", timeout: 30, signal: :SEGV) begin; - STR_COUNT = 500 + STR_COUNT = 50000 GC.verify_compaction_references(expand_heap: true, toward: :empty) Fiber.new { - $ary = STR_COUNT.times.map { ("a" * GC::INTERNAL_CONSTANTS[:BASE_SLOT_SIZE]).squeeze! } + $ary = STR_COUNT.times.map { ("a" * GC::INTERNAL_CONSTANTS[:BASE_SLOT_SIZE] * 4).squeeze! } }.resume stats = GC.verify_compaction_references(expand_heap: true, toward: :empty) @@ -432,9 +432,9 @@ class TestGCCompact < Test::Unit::TestCase # AR and ST hashes are in the same size pool on 32 bit omit unless RbConfig::SIZEOF["uint64_t"] <= RbConfig::SIZEOF["void*"] - assert_separately(%w[-robjspace], "#{<<~"begin;"}\n#{<<~"end;"}", timeout: 10, signal: :SEGV) + assert_separately(%w[-robjspace], "#{<<~"begin;"}\n#{<<~"end;"}", timeout: 30, signal: :SEGV) begin; - HASH_COUNT = 500 + HASH_COUNT = 50000 GC.verify_compaction_references(expand_heap: true, toward: :empty) @@ -446,7 +446,7 @@ class TestGCCompact < Test::Unit::TestCase stats = GC.verify_compaction_references(expand_heap: true, toward: :empty) - assert_operator(stats[:moved_down][:T_HASH], :>=, 500) + assert_operator(stats[:moved_down][:T_HASH], :>=, HASH_COUNT) end; end |