Skip to content

Commit d80c03d

Browse files
jhawthorntenderlove
andcommitted
Fix id2ref table build when GC in progress
Previously, if GC was in progress when we're initially building the id2ref table, it could see the empty table and then crash when trying to remove ids from it. This commit fixes the bug by only publishing the table after GC is done. Co-authored-by: Aaron Patterson <[email protected]>
1 parent 07878eb commit d80c03d

File tree

2 files changed

+20
-2
lines changed

2 files changed

+20
-2
lines changed

gc.c

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1975,14 +1975,17 @@ object_id_to_ref(void *objspace_ptr, VALUE object_id)
19751975
// GC Must not trigger while we build the table, otherwise if we end
19761976
// up freeing an object that had an ID, we might try to delete it from
19771977
// the table even though it wasn't inserted yet.
1978-
id2ref_tbl = st_init_table(&object_id_hash_type);
1979-
id2ref_value = TypedData_Wrap_Struct(0, &id2ref_tbl_type, id2ref_tbl);
1978+
st_table *tmp_id2ref_tbl = st_init_table(&object_id_hash_type);
1979+
VALUE tmp_id2ref_value = TypedData_Wrap_Struct(0, &id2ref_tbl_type, tmp_id2ref_tbl);
19801980

19811981
// build_id2ref_i will most certainly malloc, which could trigger GC and sweep
19821982
// objects we just added to the table.
19831983
// By calling rb_gc_disable() we also save having to handle potentially garbage objects.
19841984
bool gc_disabled = RTEST(rb_gc_disable());
19851985
{
1986+
id2ref_tbl = tmp_id2ref_tbl;
1987+
id2ref_value = tmp_id2ref_value;
1988+
19861989
rb_gc_impl_each_object(objspace, build_id2ref_i, (void *)id2ref_tbl);
19871990
}
19881991
if (!gc_disabled) rb_gc_enable();

test/ruby/test_objectspace.rb

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -284,6 +284,21 @@ def test_each_object_recursive_key
284284
end;
285285
end
286286

287+
def test_id2ref_table_build
288+
assert_separately([], <<-End)
289+
10.times do
290+
Object.new.object_id
291+
end
292+
293+
GC.start(immediate_mark: false)
294+
295+
obj = Object.new
296+
EnvUtil.suppress_warning do
297+
assert_equal obj, ObjectSpace._id2ref(obj.object_id)
298+
end
299+
End
300+
end
301+
287302
def test_each_object_singleton_class
288303
assert_separately([], <<-End)
289304
class C

0 commit comments

Comments
 (0)