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]
This commit is contained in:
parent
87d21ee996
commit
63ad55cd88
21
gc.c
21
gc.c
@ -3090,6 +3090,17 @@ Init_heap(void)
|
|||||||
{
|
{
|
||||||
rb_objspace_t *objspace = &rb_objspace;
|
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->next_object_id = INT2FIX(OBJ_ID_INITIAL);
|
||||||
objspace->id_to_obj_tbl = st_init_table(&object_id_hash_type);
|
objspace->id_to_obj_tbl = st_init_table(&object_id_hash_type);
|
||||||
objspace->obj_to_id_tbl = st_init_numtable();
|
objspace->obj_to_id_tbl = st_init_numtable();
|
||||||
@ -9890,6 +9901,16 @@ gc_disable(rb_execution_context_t *ec, VALUE _)
|
|||||||
static VALUE
|
static VALUE
|
||||||
gc_set_auto_compact(rb_execution_context_t *ec, VALUE _, VALUE v)
|
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);
|
ruby_enable_autocompact = RTEST(v);
|
||||||
return v;
|
return v;
|
||||||
}
|
}
|
||||||
|
@ -1,56 +1,82 @@
|
|||||||
# frozen_string_literal: true
|
# frozen_string_literal: true
|
||||||
require 'test/unit'
|
require 'test/unit'
|
||||||
require 'fiddle'
|
require 'fiddle'
|
||||||
|
require 'etc'
|
||||||
|
|
||||||
class TestGCCompact < Test::Unit::TestCase
|
class TestGCCompact < Test::Unit::TestCase
|
||||||
def test_enable_autocompact
|
class AutoCompact < Test::Unit::TestCase
|
||||||
before = GC.auto_compact
|
def setup
|
||||||
GC.auto_compact = true
|
skip "autocompact not supported on this platform" unless supports_auto_compact?
|
||||||
assert GC.auto_compact
|
super
|
||||||
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_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_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
|
end
|
||||||
compact_stats = GC.latest_compact_info
|
|
||||||
refute_predicate compact_stats[:considered], :empty?
|
def test_enable_autocompact
|
||||||
refute_predicate compact_stats[:moved], :empty?
|
before = GC.auto_compact
|
||||||
ensure
|
GC.auto_compact = true
|
||||||
GC.auto_compact = before
|
assert GC.auto_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_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_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
|
||||||
|
|
||||||
|
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
|
end
|
||||||
|
|
||||||
def test_gc_compact_stats
|
def test_gc_compact_stats
|
||||||
|
Loading…
x
Reference in New Issue
Block a user