Fix the exception when trying to copy procs for Ractors

`rb_obj_clone` can possibly raise an exception, and the exception isn't
always helpful.  This commit catches the exception and raises an
IsolationError telling the user what went wrong.

Before this commit:

```
> ruby -e'Ractor.make_shareable(->{}, copy:true)'
<internal:ractor>:828:in 'Ractor.make_shareable': allocator undefined for Proc (TypeError)
	from -e:1:in '<main>'
```

After this commit:

```
> ./miniruby -e'Ractor.make_shareable(->{}, copy:true)'
-e:1:in 'Ractor.make_shareable': cannot copy #<Proc:0x000000011f311a80 -e:1 (lambda)> (Ractor::IsolationError)
	from -e:1:in '<main>'
-e:1:in 'Ractor.make_shareable': allocator undefined for Proc (TypeError)
	from -e:1:in '<main>'
```

[Bug #21451]
This commit is contained in:
Aaron Patterson 2025-06-25 11:57:58 -07:00
parent a1996b32a9
commit 3f70bf14e3
No known key found for this signature in database
GPG key ID: 953170BCB4FFAFC6
3 changed files with 15 additions and 6 deletions

View file

@ -408,8 +408,8 @@ assert_equal "allocator undefined for Thread", %q{
r = Ractor.new obj do |msg|
msg
end
rescue TypeError => e
e.message #=> no _dump_data is defined for class Thread
rescue Ractor::IsolationError => e
e.cause.message #=> no _dump_data is defined for class Thread
else
'ng'
end
@ -1403,7 +1403,7 @@ assert_equal "ok", %q{
end
begin
Ractor.new{} << err
rescue TypeError
rescue Ractor::IsolationError
'ok'
end
}

View file

@ -1925,7 +1925,12 @@ copy_enter(VALUE obj, struct obj_traverse_replace_data *data)
return traverse_skip;
}
else {
data->replacement = rb_obj_clone(obj);
int state;
VALUE result = rb_protect(rb_obj_clone, obj, &state);
if (state) {
rb_raise(rb_eRactorIsolationError, "cannot copy %"PRIsVALUE"", obj);
}
data->replacement = result;
return traverse_cont;
}
}

View file

@ -30,6 +30,10 @@ class TestRactor < Test::Unit::TestCase
assert_make_shareable(x)
end
def test_cannot_copy_proc
assert_unshareable(-> { }, /cannot copy/, copy: true)
end
def test_shareability_of_method_proc
str = +""
@ -150,10 +154,10 @@ class TestRactor < Test::Unit::TestCase
assert Ractor.shareable?(obj), "object didn't become shareable"
end
def assert_unshareable(obj, msg=nil, exception: Ractor::IsolationError)
def assert_unshareable(obj, msg=nil, copy: false, exception: Ractor::IsolationError)
refute Ractor.shareable?(obj), "object is already shareable"
assert_raise_with_message(exception, msg) do
Ractor.make_shareable(obj)
Ractor.make_shareable(obj, copy:)
end
refute Ractor.shareable?(obj), "despite raising, object became shareable"
end