Skip to content

Commit 0d83a93

Browse files
Unshare arrays only when they become shareable
obj_traverse_i called rb_ary_cancel_sharing on every array it visited, before the traversal knew whether the object graph was shareable. So Ractor.shareable? modified frozen arrays even when it returned false. This commit moves rb_ary_cancel_sharing into mark_shareable, which runs only after the traversal succeeds. [Bug #22460]
1 parent 9fa6602 commit 0d83a93

3 files changed

Lines changed: 18 additions & 4 deletions

File tree

‎array.c‎

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -579,8 +579,8 @@ rb_ary_cancel_sharing(VALUE ary)
579579
else if (ARY_SHARED_ROOT_OCCUPIED(shared_root) && len > ((shared_len = RARRAY_LEN(shared_root))>>1)) {
580580
rb_len_t shift = RARRAY_CONST_PTR(ary) - RARRAY_CONST_PTR(shared_root);
581581
FL_UNSET_SHARED(ary);
582-
ARY_SET_PTR(ary, RARRAY_CONST_PTR(shared_root));
583-
ARY_SET_CAPA(ary, shared_len);
582+
ARY_SET_PTR_FORCE(ary, RARRAY_CONST_PTR(shared_root));
583+
ARY_SET_CAPA_FORCE(ary, shared_len);
584584
RARRAY_PTR_USE(ary, ptr, {
585585
MEMMOVE(ptr, ptr+shift, VALUE, len);
586586
});

‎ractor.c‎

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1754,8 +1754,6 @@ obj_traverse_i(VALUE obj, struct obj_traverse_data *data)
17541754

17551755
case T_ARRAY:
17561756
{
1757-
rb_ary_cancel_sharing(obj);
1758-
17591757
for (rb_len_t i = 0; i < RARRAY_LEN(obj); i++) {
17601758
VALUE e = RARRAY_AREF(obj, i);
17611759
if (obj_traverse_i(e, data)) return 1;
@@ -2002,6 +2000,9 @@ mark_shareable(VALUE obj)
20022000
if (RB_BUILTIN_TYPE(obj) == T_STRING) {
20032001
rb_str_make_independent(obj);
20042002
}
2003+
else if (RB_BUILTIN_TYPE(obj) == T_ARRAY) {
2004+
rb_ary_cancel_sharing(obj);
2005+
}
20052006

20062007
rb_obj_set_shareable_no_assert(obj);
20072008
return traverse_cont;

‎test/ruby/test_ractor.rb‎

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -955,6 +955,19 @@ def test_move_shared_root_string_keeps_buffer
955955
RUBY
956956
end
957957

958+
# [Bug #22460] Ractor.shareable? must not replace the buffer of a frozen shared array.
959+
def test_shareable_p_keeps_frozen_shared_array_buffer
960+
assert_ractor(<<~'RUBY')
961+
o = Object.new
962+
a = Array.new(200) { it }
963+
a[50] = o
964+
b = a.pop(150)
965+
o.define_singleton_method(:to_s) { Ractor.shareable?(b); "x" }
966+
b.freeze
967+
assert_nothing_raised { assert_equal "x 51", "%1$s %2$s" % b }
968+
RUBY
969+
end
970+
958971
# Ractor::Port.new must not deadlock under GC.stress (regression guard for a stress GC
959972
# triggered by malloc while the ractor lock is held).
960973
def test_port_new_under_gc_stress

0 commit comments

Comments
 (0)