From 2c7ec3d155ba9d3e0589f716c1522f2c26371586 Mon Sep 17 00:00:00 2001 From: Peter Zhu Date: Wed, 20 Aug 2025 10:53:18 -0400 Subject: [PATCH 1/4] Fix race condition in method invalidation for Ractors We lock the VM to invalidate method entries. However, we do not lock the VM to call methods, so it's possible that during a method call the method entry gets invalidated. We only check that the method entry in the callcache is not invalidated at the beginning of the method call, which makes it possible to have race conditions. This causes crashes like: vm_callinfo.h:421: Assertion Failed: vm_cc_cme:cc->klass != Qundef || !vm_cc_markable(cc) vm_insnhelper.c:2200: Assertion Failed: vm_lookup_cc:!METHOD_ENTRY_INVALIDATED(vm_cc_cme(ccs_cc)) This commit adds a VM barrier to method cache invalidation to ensure that other Ractors are stopped at a safe-point before invalidating the method entry. --- bootstraptest/test_ractor.rb | 27 +++++++++++++++++++++++++++ vm_callinfo.h | 2 +- vm_method.c | 4 ++++ 3 files changed, 32 insertions(+), 1 deletion(-) diff --git a/bootstraptest/test_ractor.rb b/bootstraptest/test_ractor.rb index b1e9e3a79d02cb..4a58ece8ac8099 100644 --- a/bootstraptest/test_ractor.rb +++ b/bootstraptest/test_ractor.rb @@ -1573,6 +1573,33 @@ class C8; def self.foo = 17; end rs.map{|r| r.value} == Array.new(RN){n} } +# check method cache invalidation +assert_equal 'true', %q{ + class Foo + def hello = nil + end + + r1 = Ractor.new do + 1000.times do + class Foo + def hello = nil + end + end + end + + r2 = Ractor.new do + 1000.times do + o = Foo.new + o.hello + end + end + + r1.value + r2.value + + true +} + # check experimental warning assert_match /\Atest_ractor\.rb:1:\s+warning:\s+Ractor is experimental/, %q{ Warning[:experimental] = $VERBOSE = true diff --git a/vm_callinfo.h b/vm_callinfo.h index 79ccbfa7abb7c3..e52b2f9b1ab8b3 100644 --- a/vm_callinfo.h +++ b/vm_callinfo.h @@ -613,7 +613,7 @@ static inline bool vm_cc_check_cme(const struct rb_callcache *cc, const rb_callable_method_entry_t *cme) { bool valid; - RB_VM_LOCKING() { + RB_VM_LOCKING_NO_BARRIER() { valid = vm_cc_cme(cc) == cme || (cme->def->iseq_overload && vm_cc_cme(cc) == rb_vm_lookup_overloaded_cme(cme)); } diff --git a/vm_method.c b/vm_method.c index 722daf0a6a902c..73a431ce93602c 100644 --- a/vm_method.c +++ b/vm_method.c @@ -428,6 +428,8 @@ clear_method_cache_by_id_in_class(VALUE klass, ID mid) if (rb_objspace_garbage_object_p(klass)) return; RB_VM_LOCKING() { + rb_vm_barrier(); + if (LIKELY(RCLASS_SUBCLASSES_FIRST(klass) == NULL)) { // no subclasses // check only current class @@ -1752,6 +1754,8 @@ cached_callable_method_entry(VALUE klass, ID mid) return ccs->cme; } else { + rb_vm_barrier(); + rb_managed_id_table_delete(cc_tbl, mid); rb_vm_ccs_invalidate_and_free(ccs); } From a7a026ae9b6dbf5ff4ff40021b0bb5d15bd49fc7 Mon Sep 17 00:00:00 2001 From: Stan Lo Date: Wed, 20 Aug 2025 15:53:16 -0400 Subject: [PATCH 2/4] YJIT: Improve locals names (#14285) --- yjit/src/codegen.rs | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/yjit/src/codegen.rs b/yjit/src/codegen.rs index 44d458020db307..9c0601052700ea 100644 --- a/yjit/src/codegen.rs +++ b/yjit/src/codegen.rs @@ -4315,11 +4315,11 @@ fn gen_opt_ary_freeze( return None; } - let str = jit.get_arg(0); + let ary = jit.get_arg(0); // Push the return value onto the stack let stack_ret = asm.stack_push(Type::CArray); - asm.mov(stack_ret, str.into()); + asm.mov(stack_ret, ary.into()); Some(KeepCompiling) } @@ -4332,11 +4332,11 @@ fn gen_opt_hash_freeze( return None; } - let str = jit.get_arg(0); + let hash = jit.get_arg(0); // Push the return value onto the stack let stack_ret = asm.stack_push(Type::CHash); - asm.mov(stack_ret, str.into()); + asm.mov(stack_ret, hash.into()); Some(KeepCompiling) } From 19ad72d2eb08363f2a30e2ce50ce5eb9bf4ad275 Mon Sep 17 00:00:00 2001 From: Stan Lo Date: Wed, 20 Aug 2025 16:17:43 -0400 Subject: [PATCH 3/4] ZJIT: Remove unnecessary option return type on gen_branch_params (#14286) --- zjit/src/codegen.rs | 33 +++++++++++++++++---------------- 1 file changed, 17 insertions(+), 16 deletions(-) diff --git a/zjit/src/codegen.rs b/zjit/src/codegen.rs index 958efd49fafaa0..db56db09275d47 100644 --- a/zjit/src/codegen.rs +++ b/zjit/src/codegen.rs @@ -737,25 +737,26 @@ fn gen_entry_params(asm: &mut Assembler, iseq: IseqPtr, entry_block: &Block) { } /// Set branch params to basic block arguments -fn gen_branch_params(jit: &mut JITState, asm: &mut Assembler, branch: &BranchEdge) -> Option<()> { - if !branch.args.is_empty() { - asm_comment!(asm, "set branch params: {}", branch.args.len()); - let mut moves: Vec<(Reg, Opnd)> = vec![]; - for (idx, &arg) in branch.args.iter().enumerate() { - match param_opnd(idx) { - Opnd::Reg(reg) => { - // If a parameter is a register, we need to parallel-move it - moves.push((reg, jit.get_opnd(arg))); - }, - param => { - // If a parameter is memory, we set it beforehand - asm.mov(param, jit.get_opnd(arg)); - } +fn gen_branch_params(jit: &mut JITState, asm: &mut Assembler, branch: &BranchEdge) { + if branch.args.is_empty() { + return; + } + + asm_comment!(asm, "set branch params: {}", branch.args.len()); + let mut moves: Vec<(Reg, Opnd)> = vec![]; + for (idx, &arg) in branch.args.iter().enumerate() { + match param_opnd(idx) { + Opnd::Reg(reg) => { + // If a parameter is a register, we need to parallel-move it + moves.push((reg, jit.get_opnd(arg))); + }, + param => { + // If a parameter is memory, we set it beforehand + asm.mov(param, jit.get_opnd(arg)); } } - asm.parallel_mov(moves); } - Some(()) + asm.parallel_mov(moves); } /// Get a method parameter on JIT entry. As of entry, whether EP is escaped or not solely From 426cdb2c01108f3ff367b323f2a446a3da4647b9 Mon Sep 17 00:00:00 2001 From: Takashi Kokubun Date: Wed, 20 Aug 2025 13:28:08 -0700 Subject: [PATCH 4/4] .gdbinit: rb_shape_get_shape no longer exists --- .gdbinit | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.gdbinit b/.gdbinit index f624456d048352..f204b3a235dacc 100644 --- a/.gdbinit +++ b/.gdbinit @@ -51,7 +51,7 @@ define rp printf "%sT_OBJECT%s: ", $color_type, $color_end print ((struct RObject *)($arg0))->basic if ($flags & ROBJECT_EMBED) - print/x *((VALUE*)((struct RObject*)($arg0))->as.ary) @ (rb_shape_get_shape($arg0)->capacity) + print/x *((VALUE*)((struct RObject*)($arg0))->as.ary) @ (RSHAPE_CAPACITY(rb_obj_shape_id($arg0))) else print (((struct RObject *)($arg0))->as.heap) if (((struct RObject*)($arg0))->as.heap.numiv) > 0