From 70618a48f7f9f12943e963bf121b56af4153f394 Mon Sep 17 00:00:00 2001 From: Peter Zhu Date: Sun, 24 Dec 2023 16:23:10 -0500 Subject: [PATCH] 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) --- gc.c | 4 ++-- test/ruby/test_enumerator.rb | 14 ++++++++++++++ 2 files changed, 16 insertions(+), 2 deletions(-) diff --git a/gc.c b/gc.c index b14550f88c..6d62ca293d 100644 --- a/gc.c +++ b/gc.c @@ -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); diff --git a/test/ruby/test_enumerator.rb b/test/ruby/test_enumerator.rb index bb04480b3d..11cfe69864 100644 --- a/test/ruby/test_enumerator.rb +++ b/test/ruby/test_enumerator.rb @@ -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])