Change bmethod defined_ractor to use id instead

When defining a bmethod, we recorded the current Ractor's object in the
method. However that was never marked and so could be GC'd and reused by
a future Ractor. Instead we can use the Ractor's id, which we expect to
be unique forever.

Co-authored-by: Luke Gruber <luke.gru@gmail.com>
This commit is contained in:
John Hawthorn 2025-10-30 12:05:25 -07:00
parent de2c2bd60f
commit 1d3fe2c382
Notes: git 2025-12-04 21:26:16 +00:00
4 changed files with 29 additions and 5 deletions

View File

@ -2413,3 +2413,28 @@ assert_equal 'ok', <<~'RUBY'
:ok
end
RUBY
# When creating bmethods in Ractors, they should only be usable from their
# defining ractor, even if it is GC'd
assert_equal 'ok', <<~'RUBY'
CLASSES = 1000.times.map do
Ractor.new do
Class.new do
define_method(:foo) {}
end
end
end.map(&:value).freeze
any = 1000.times.map do
Ractor.new do
CLASSES.any? do |klass|
begin
klass.new.foo
true
rescue RuntimeError
false
end
end
end
end.map(&:value).none? && :ok
RUBY

View File

@ -167,7 +167,7 @@ typedef struct rb_method_refined_struct {
typedef struct rb_method_bmethod_struct {
VALUE proc; /* should be marked */
struct rb_hook_list_struct *hooks;
VALUE defined_ractor;
rb_serial_t defined_ractor_id;
} rb_method_bmethod_t;
enum method_optimized_type {

View File

@ -4121,7 +4121,7 @@ vm_call_bmethod_body(rb_execution_context_t *ec, struct rb_calling_info *calling
VALUE procv = cme->def->body.bmethod.proc;
if (!RB_OBJ_SHAREABLE_P(procv) &&
cme->def->body.bmethod.defined_ractor != rb_ractor_self(rb_ec_ractor_ptr(ec))) {
cme->def->body.bmethod.defined_ractor_id != rb_ractor_id(rb_ec_ractor_ptr(ec))) {
rb_raise(rb_eRuntimeError, "defined with an un-shareable Proc in a different Ractor");
}
@ -4144,7 +4144,7 @@ vm_call_iseq_bmethod(rb_execution_context_t *ec, rb_control_frame_t *cfp, struct
VALUE procv = cme->def->body.bmethod.proc;
if (!RB_OBJ_SHAREABLE_P(procv) &&
cme->def->body.bmethod.defined_ractor != rb_ractor_self(rb_ec_ractor_ptr(ec))) {
cme->def->body.bmethod.defined_ractor_id != rb_ractor_id(rb_ec_ractor_ptr(ec))) {
rb_raise(rb_eRuntimeError, "defined with an un-shareable Proc in a different Ractor");
}

View File

@ -1020,7 +1020,7 @@ rb_method_definition_set(const rb_method_entry_t *me, rb_method_definition_t *de
}
case VM_METHOD_TYPE_BMETHOD:
RB_OBJ_WRITE(me, &def->body.bmethod.proc, (VALUE)opts);
RB_OBJ_WRITE(me, &def->body.bmethod.defined_ractor, rb_ractor_self(GET_RACTOR()));
def->body.bmethod.defined_ractor_id = rb_ractor_id(rb_ec_ractor_ptr(GET_EC()));
return;
case VM_METHOD_TYPE_NOTIMPLEMENTED:
setup_method_cfunc_struct(UNALIGNED_MEMBER_PTR(def, body.cfunc), (VALUE(*)(ANYARGS))rb_f_notimplement_internal, -1);
@ -1060,7 +1060,6 @@ method_definition_reset(const rb_method_entry_t *me)
break;
case VM_METHOD_TYPE_BMETHOD:
RB_OBJ_WRITTEN(me, Qundef, def->body.bmethod.proc);
RB_OBJ_WRITTEN(me, Qundef, def->body.bmethod.defined_ractor);
/* give up to check all in a list */
if (def->body.bmethod.hooks) rb_gc_writebarrier_remember((VALUE)me);
break;