Fix generational GC for weak references

Fixes issue pointed out in https://bugs.ruby-lang.org/issues/21084#note-7.
The following script crashes:

    wmap = ObjectSpace::WeakMap.new

    GC.disable # only manual GCs
    GC.start
    GC.start

    retain = []
    50.times do
      k = Object.new
      wmap[k] = true
      retain << k
    end

    GC.start # wmap promoted, other objects still young

    retain.clear

    GC.start(full_mark: false)

    wmap.keys.each(&:itself) # call method on keys to cause crash
This commit is contained in:
Peter Zhu 2025-12-30 09:46:42 -05:00
parent c05e10605e
commit f2833e358c
Notes: git 2025-12-30 15:59:50 +00:00
2 changed files with 32 additions and 1 deletions

View File

@ -5329,7 +5329,13 @@ rb_gc_impl_handle_weak_references_alive_p(void *objspace_ptr, VALUE obj)
{
rb_objspace_t *objspace = objspace_ptr;
return RVALUE_MARKED(objspace, obj);
bool marked = RVALUE_MARKED(objspace, obj);
if (marked) {
rgengc_check_relation(objspace, obj);
}
return marked;
}
static void
@ -5337,7 +5343,9 @@ gc_update_weak_references(rb_objspace_t *objspace)
{
VALUE *obj_ptr;
rb_darray_foreach(objspace->weak_references, i, obj_ptr) {
gc_mark_set_parent(objspace, *obj_ptr);
rb_gc_handle_weak_references(*obj_ptr);
gc_mark_set_parent_invalid(objspace);
}
size_t capa = rb_darray_capa(objspace->weak_references);

View File

@ -265,4 +265,27 @@ class TestWeakMap < Test::Unit::TestCase
10_000.times { weakmap[Object.new] = Object.new }
RUBY
end
def test_generational_gc
EnvUtil.without_gc do
wmap = ObjectSpace::WeakMap.new
(GC::INTERNAL_CONSTANTS[:RVALUE_OLD_AGE] - 1).times { GC.start }
retain = []
50.times do
k = Object.new
wmap[k] = true
retain << k
end
GC.start # WeakMap promoted, other objects still young
retain.clear
GC.start(full_mark: false)
wmap.keys.each(&:itself) # call method on keys to cause crash
end
end
end