Skip to content

Commit

Permalink
Fix off-by-one error for declarative marking
Browse files Browse the repository at this point in the history
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)
  • Loading branch information
peterzhu2118 committed Dec 25, 2023
1 parent 260bf60 commit 70618a4
Show file tree
Hide file tree
Showing 2 changed files with 16 additions and 2 deletions.
4 changes: 2 additions & 2 deletions gc.c
Expand Up @@ -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));
}
}
Expand Down Expand Up @@ -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);
Expand Down
14 changes: 14 additions & 0 deletions test/ruby/test_enumerator.rb
Expand Up @@ -862,6 +862,20 @@ def test_lazy_chain
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])
Expand Down

0 comments on commit 70618a4

Please sign in to comment.