ruby-changes:63743
From: Aaron <ko1@a...>
Date: Wed, 25 Nov 2020 07:48:44 +0900 (JST)
Subject: [ruby-changes:63743] 63ad55cd88 (master): Disable auto compaction on platforms that can't support it
https://git.ruby-lang.org/ruby.git/commit/?id=63ad55cd88 From 63ad55cd882e4010fe313d271af006a430b5ffa8 Mon Sep 17 00:00:00 2001 From: Aaron Patterson <tenderlove@r...> Date: Tue, 24 Nov 2020 14:33:12 -0800 Subject: Disable auto compaction on platforms that can't support it Auto Compaction uses mprotect to implement a read barrier. mprotect can only work on regions of memory that are a multiple of the OS page size. Ruby's pages are a multiple of 4kb, but some platforms (like ppc64le) don't have 4kb page sizes. This commit disables the features on those platforms. Fixes [Bug #17306] diff --git a/gc.c b/gc.c index 17bc9fd..d5ad476 100644 --- a/gc.c +++ b/gc.c @@ -3090,6 +3090,17 @@ Init_heap(void) https://github.com/ruby/ruby/blob/trunk/gc.c#L3090 { rb_objspace_t *objspace = &rb_objspace; +#if defined(HAVE_SYSCONF) && defined(_SC_PAGE_SIZE) + /* If Ruby's heap pages are not a multiple of the system page size, we + * cannot use mprotect for the read barrier, so we must disable automatic + * compaction. */ + int pagesize; + pagesize = (int)sysconf(_SC_PAGE_SIZE); + if ((HEAP_PAGE_SIZE % pagesize) != 0) { + ruby_enable_autocompact = 0; + } +#endif + objspace->next_object_id = INT2FIX(OBJ_ID_INITIAL); objspace->id_to_obj_tbl = st_init_table(&object_id_hash_type); objspace->obj_to_id_tbl = st_init_numtable(); @@ -9890,6 +9901,16 @@ gc_disable(rb_execution_context_t *ec, VALUE _) https://github.com/ruby/ruby/blob/trunk/gc.c#L9901 static VALUE gc_set_auto_compact(rb_execution_context_t *ec, VALUE _, VALUE v) { +#if defined(HAVE_SYSCONF) && defined(_SC_PAGE_SIZE) + /* If Ruby's heap pages are not a multiple of the system page size, we + * cannot use mprotect for the read barrier, so we must disable automatic + * compaction. */ + int pagesize; + pagesize = (int)sysconf(_SC_PAGE_SIZE); + if ((HEAP_PAGE_SIZE % pagesize) != 0) { + rb_raise(rb_eNotImpError, "Automatic compaction isn't available on this platform"); + } +#endif ruby_enable_autocompact = RTEST(v); return v; } diff --git a/test/ruby/test_gc_compact.rb b/test/ruby/test_gc_compact.rb index 3aad9e6..4a8cff3 100644 --- a/test/ruby/test_gc_compact.rb +++ b/test/ruby/test_gc_compact.rb @@ -1,56 +1,82 @@ https://github.com/ruby/ruby/blob/trunk/test/ruby/test_gc_compact.rb#L1 # frozen_string_literal: true require 'test/unit' require 'fiddle' +require 'etc' class TestGCCompact < Test::Unit::TestCase - def test_enable_autocompact - before = GC.auto_compact - GC.auto_compact = true - assert GC.auto_compact - ensure - GC.auto_compact = before - end + class AutoCompact < Test::Unit::TestCase + def setup + skip "autocompact not supported on this platform" unless supports_auto_compact? + super + end - def test_disable_autocompact - before = GC.auto_compact - GC.auto_compact = false - refute GC.auto_compact - ensure - GC.auto_compact = before - end + def test_enable_autocompact + before = GC.auto_compact + GC.auto_compact = true + assert GC.auto_compact + ensure + GC.auto_compact = before + end - def test_major_compacts - before = GC.auto_compact - GC.auto_compact = true - compact = GC.stat :compact_count - GC.start - assert_operator GC.stat(:compact_count), :>, compact - ensure - GC.auto_compact = before - end + def test_disable_autocompact + before = GC.auto_compact + GC.auto_compact = false + refute GC.auto_compact + ensure + GC.auto_compact = before + end - def test_implicit_compaction_does_something - before = GC.auto_compact - list = [] - list2 = [] + def test_major_compacts + before = GC.auto_compact + GC.auto_compact = true + compact = GC.stat :compact_count + GC.start + assert_operator GC.stat(:compact_count), :>, compact + ensure + GC.auto_compact = before + end - # Try to make some fragmentation - 500.times { - list << Object.new - Object.new - Object.new - } - count = GC.stat :compact_count - GC.auto_compact = true - loop do - break if count < GC.stat(:compact_count) - list2 << Object.new + def test_implicit_compaction_does_something + before = GC.auto_compact + list = [] + list2 = [] + + # Try to make some fragmentation + 500.times { + list << Object.new + Object.new + Object.new + } + count = GC.stat :compact_count + GC.auto_compact = true + loop do + break if count < GC.stat(:compact_count) + list2 << Object.new + end + compact_stats = GC.latest_compact_info + refute_predicate compact_stats[:considered], :empty? + refute_predicate compact_stats[:moved], :empty? + ensure + GC.auto_compact = before end - compact_stats = GC.latest_compact_info - refute_predicate compact_stats[:considered], :empty? - refute_predicate compact_stats[:moved], :empty? - ensure - GC.auto_compact = before + + private + + def supports_auto_compact? + return true unless defined?(Etc::SC_PAGE_SIZE) + + begin + return GC::INTERNAL_CONSTANTS[:HEAP_PAGE_SIZE] % Etc.sysconf(Etc::SC_PAGE_SIZE) == 0 + rescue NotImplementedError + rescue ArgumentError + end + + true + end + end + + def os_page_size + return true unless defined?(Etc::SC_PAGE_SIZE) end def test_gc_compact_stats -- cgit v0.10.2 -- ML: ruby-changes@q... Info: http://www.atdot.net/~ko1/quickml/