Fix off-by-one error for declarative marking

The for loops for marking and reference updating declaratively marked
TypedData objects did not mark/reference update the very last element.

When RGENGC_CHECK_MODE is turned on, this caused the test in Enumerator
to fail with:

    tool/lib/test/unit/testcase.rb:173:in `rescue in run': failed to allocate memory (NoMemoryError)
This commit is contained in:
Peter Zhu 2023-12-24 16:23:10 -05:00
parent 260bf60e52
commit 70618a48f7
2 changed files with 16 additions and 2 deletions

4
gc.c
View File

@ -7444,7 +7444,7 @@ gc_mark_children(rb_objspace_t *objspace, VALUE obj)
if (RTYPEDDATA_P(obj) && gc_declarative_marking_p(any->as.typeddata.type)) {
size_t *offset_list = (size_t *)RANY(obj)->as.typeddata.type->function.dmark;
for (size_t offset = *offset_list; *offset_list != RUBY_REF_END; offset = *offset_list++) {
for (size_t offset = *offset_list; offset != RUBY_REF_END; offset = *offset_list++) {
rb_gc_mark_movable(*(VALUE *)((char *)ptr + offset));
}
}
@ -10814,7 +10814,7 @@ gc_update_object_references(rb_objspace_t *objspace, VALUE obj)
if (RTYPEDDATA_P(obj) && gc_declarative_marking_p(any->as.typeddata.type)) {
size_t *offset_list = (size_t *)RANY(obj)->as.typeddata.type->function.dmark;
for (size_t offset = *offset_list; *offset_list != RUBY_REF_END; offset = *offset_list++) {
for (size_t offset = *offset_list; offset != RUBY_REF_END; offset = *offset_list++) {
VALUE *ref = (VALUE *)((char *)ptr + offset);
if (SPECIAL_CONST_P(*ref)) continue;
*ref = rb_gc_location(*ref);

View File

@ -862,6 +862,20 @@ class TestEnumerator < Test::Unit::TestCase
assert_equal(33, chain.next)
end
def test_lazy_chain_under_gc_compact_stress
EnvUtil.under_gc_compact_stress do
ea = (10..).lazy.select(&:even?).take(10)
ed = (20..).lazy.select(&:odd?)
chain = (ea + ed).select{|x| x % 3 == 0}
assert_equal(12, chain.next)
assert_equal(18, chain.next)
assert_equal(24, chain.next)
assert_equal(21, chain.next)
assert_equal(27, chain.next)
assert_equal(33, chain.next)
end
end
def test_chain_undef_methods
chain = [1].to_enum + [2].to_enum
meths = (chain.methods & [:feed, :next, :next_values, :peek, :peek_values])