Ractor.make_shareable(proc_obj) makes inner structure shareable

Proc objects are now traversed like other objects when making them
shareable.

Fixes [Bug #19372]
Fixes [Bug #19374]
This commit is contained in:
lukeg 2023-01-25 07:45:19 -05:00 committed by John Hawthorn
parent 2183899fd1
commit d80f3a287c
Notes: git 2025-03-26 23:05:18 +00:00
3 changed files with 48 additions and 14 deletions

View File

@ -1361,6 +1361,28 @@ assert_equal 'true', %q{
Ractor.shareable?(pr) Ractor.shareable?(pr)
} }
# Ractor.make_shareable(a_proc) makes inner structure shareable and freezes it
assert_equal 'true,true,true,true', %q{
class Proc
attr_reader :obj
def initialize
@obj = Object.new
end
end
pr = Ractor.current.instance_eval do
Proc.new {}
end
results = []
Ractor.make_shareable(pr)
results << Ractor.shareable?(pr)
results << pr.frozen?
results << Ractor.shareable?(pr.obj)
results << pr.obj.frozen?
results.map(&:to_s).join(',')
}
# Ractor.shareable?(recursive_objects) # Ractor.shareable?(recursive_objects)
assert_equal '[false, false]', %q{ assert_equal '[false, false]', %q{
y = [] y = []
@ -1389,6 +1411,21 @@ assert_equal '[C, M]', %q{
Ractor.make_shareable(ary = [C, M]) Ractor.make_shareable(ary = [C, M])
} }
# Ractor.make_shareable with curried proc checks isolation of original proc
assert_equal 'isolation error', %q{
a = Object.new
orig = proc { a }
curried = orig.curry
begin
Ractor.make_shareable(curried)
rescue Ractor::IsolationError
'isolation error'
else
'no error'
end
}
# define_method() can invoke different Ractor's proc if the proc is shareable. # define_method() can invoke different Ractor's proc if the proc is shareable.
assert_equal '1', %q{ assert_equal '1', %q{
class C class C

View File

@ -3045,7 +3045,7 @@ rb_obj_traverse(VALUE obj,
} }
static int static int
frozen_shareable_p(VALUE obj, bool *made_shareable) allow_frozen_shareable_p(VALUE obj)
{ {
if (!RB_TYPE_P(obj, T_DATA)) { if (!RB_TYPE_P(obj, T_DATA)) {
return true; return true;
@ -3055,13 +3055,6 @@ frozen_shareable_p(VALUE obj, bool *made_shareable)
if (type->flags & RUBY_TYPED_FROZEN_SHAREABLE) { if (type->flags & RUBY_TYPED_FROZEN_SHAREABLE) {
return true; return true;
} }
else if (made_shareable && rb_obj_is_proc(obj)) {
// special path to make shareable Proc.
rb_proc_ractor_make_shareable(obj);
*made_shareable = true;
VM_ASSERT(RB_OBJ_SHAREABLE_P(obj));
return false;
}
} }
return false; return false;
@ -3071,20 +3064,24 @@ static enum obj_traverse_iterator_result
make_shareable_check_shareable(VALUE obj) make_shareable_check_shareable(VALUE obj)
{ {
VM_ASSERT(!SPECIAL_CONST_P(obj)); VM_ASSERT(!SPECIAL_CONST_P(obj));
bool made_shareable = false;
if (rb_ractor_shareable_p(obj)) { if (rb_ractor_shareable_p(obj)) {
return traverse_skip; return traverse_skip;
} }
if (!frozen_shareable_p(obj, &made_shareable)) { else if (!allow_frozen_shareable_p(obj)) {
if (made_shareable) { if (rb_obj_is_proc(obj)) {
return traverse_skip; rb_proc_ractor_make_shareable(obj);
return traverse_cont;
} }
else { else {
rb_raise(rb_eRactorError, "can not make shareable object for %"PRIsVALUE, obj); rb_raise(rb_eRactorError, "can not make shareable object for %"PRIsVALUE, obj);
} }
} }
if (RB_TYPE_P(obj, T_IMEMO)) {
return traverse_skip;
}
if (!RB_OBJ_FROZEN_RAW(obj)) { if (!RB_OBJ_FROZEN_RAW(obj)) {
rb_funcall(obj, idFreeze, 0); rb_funcall(obj, idFreeze, 0);
@ -3156,7 +3153,7 @@ shareable_p_enter(VALUE obj)
return traverse_skip; return traverse_skip;
} }
else if (RB_OBJ_FROZEN_RAW(obj) && else if (RB_OBJ_FROZEN_RAW(obj) &&
frozen_shareable_p(obj, NULL)) { allow_frozen_shareable_p(obj)) {
return traverse_cont; return traverse_cont;
} }

2
vm.c
View File

@ -1415,7 +1415,7 @@ rb_proc_ractor_make_shareable(VALUE self)
proc->is_isolated = TRUE; proc->is_isolated = TRUE;
} }
FL_SET_RAW(self, RUBY_FL_SHAREABLE); rb_obj_freeze(self);
return self; return self;
} }