Fix Ractor sharing for too complex generic ivars

This commit is contained in:
Peter Zhu 2023-11-28 09:26:41 -05:00
parent 4938390177
commit 6eb5a9cf8f
2 changed files with 57 additions and 11 deletions

View File

@ -2739,6 +2739,19 @@ obj_traverse_rec(struct obj_traverse_data *data)
return data->rec;
}
static int
obj_traverse_ivar_foreach_i(ID key, VALUE val, st_data_t ptr)
{
struct obj_traverse_callback_data *d = (struct obj_traverse_callback_data *)ptr;
if (obj_traverse_i(val, d->data)) {
d->stop = true;
return ST_STOP;
}
return ST_CONTINUE;
}
static int
obj_traverse_i(VALUE obj, struct obj_traverse_data *data)
{
@ -2755,14 +2768,12 @@ obj_traverse_i(VALUE obj, struct obj_traverse_data *data)
return 0;
}
if (UNLIKELY(FL_TEST_RAW(obj, FL_EXIVAR))) {
struct gen_ivtbl *ivtbl;
rb_ivar_generic_ivtbl_lookup(obj, &ivtbl);
for (uint32_t i = 0; i < ivtbl->as.shape.numiv; i++) {
VALUE val = ivtbl->as.shape.ivptr[i];
if (!UNDEF_P(val) && obj_traverse_i(val, data)) return 1;
}
}
struct obj_traverse_callback_data d = {
.stop = false,
.data = data,
};
rb_ivar_foreach(obj, obj_traverse_ivar_foreach_i, (st_data_t)&d);
if (d.stop) return 1;
switch (BUILTIN_TYPE(obj)) {
// no child node
@ -3229,9 +3240,26 @@ obj_traverse_replace_i(VALUE obj, struct obj_traverse_replace_data *data)
if (UNLIKELY(FL_TEST_RAW(obj, FL_EXIVAR))) {
struct gen_ivtbl *ivtbl;
rb_ivar_generic_ivtbl_lookup(obj, &ivtbl);
for (uint32_t i = 0; i < ivtbl->as.shape.numiv; i++) {
if (!UNDEF_P(ivtbl->as.shape.ivptr[i])) {
CHECK_AND_REPLACE(ivtbl->as.shape.ivptr[i]);
if (UNLIKELY(rb_shape_obj_too_complex(obj))) {
struct obj_traverse_replace_callback_data d = {
.stop = false,
.data = data,
.src = obj,
};
rb_st_foreach_with_replace(
ivtbl->as.complex.table,
obj_iv_hash_traverse_replace_foreach_i,
obj_iv_hash_traverse_replace_i,
(st_data_t)&d
);
if (d.stop) return 1;
}
else {
for (uint32_t i = 0; i < ivtbl->as.shape.numiv; i++) {
if (!UNDEF_P(ivtbl->as.shape.ivptr[i])) {
CHECK_AND_REPLACE(ivtbl->as.shape.ivptr[i]);
}
}
}
}

View File

@ -621,6 +621,24 @@ class TestShapes < Test::Unit::TestCase
end;
end
def test_too_complex_generic_ivar_ractor_share
assert_separately([], "#{<<~"begin;"}\n#{<<~'end;'}")
begin;
$VERBOSE = nil
RubyVM::Shape.exhaust_shapes
r = Ractor.new do
o = []
o.instance_variable_set(:@a, "hello")
Ractor.yield(o)
end
o = r.take
assert_equal "hello", o.instance_variable_get(:@a)
end;
end
def test_read_iv_after_complex
ensure_complex