Skip to content

Commit 2c7ec3d

Browse files
committed
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.
1 parent 5c96bbf commit 2c7ec3d

File tree

3 files changed

+32
-1
lines changed

3 files changed

+32
-1
lines changed

bootstraptest/test_ractor.rb

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1573,6 +1573,33 @@ class C8; def self.foo = 17; end
15731573
rs.map{|r| r.value} == Array.new(RN){n}
15741574
}
15751575

1576+
# check method cache invalidation
1577+
assert_equal 'true', %q{
1578+
class Foo
1579+
def hello = nil
1580+
end
1581+
1582+
r1 = Ractor.new do
1583+
1000.times do
1584+
class Foo
1585+
def hello = nil
1586+
end
1587+
end
1588+
end
1589+
1590+
r2 = Ractor.new do
1591+
1000.times do
1592+
o = Foo.new
1593+
o.hello
1594+
end
1595+
end
1596+
1597+
r1.value
1598+
r2.value
1599+
1600+
true
1601+
}
1602+
15761603
# check experimental warning
15771604
assert_match /\Atest_ractor\.rb:1:\s+warning:\s+Ractor is experimental/, %q{
15781605
Warning[:experimental] = $VERBOSE = true

vm_callinfo.h

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -613,7 +613,7 @@ static inline bool
613613
vm_cc_check_cme(const struct rb_callcache *cc, const rb_callable_method_entry_t *cme)
614614
{
615615
bool valid;
616-
RB_VM_LOCKING() {
616+
RB_VM_LOCKING_NO_BARRIER() {
617617
valid = vm_cc_cme(cc) == cme ||
618618
(cme->def->iseq_overload && vm_cc_cme(cc) == rb_vm_lookup_overloaded_cme(cme));
619619
}

vm_method.c

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -428,6 +428,8 @@ clear_method_cache_by_id_in_class(VALUE klass, ID mid)
428428
if (rb_objspace_garbage_object_p(klass)) return;
429429

430430
RB_VM_LOCKING() {
431+
rb_vm_barrier();
432+
431433
if (LIKELY(RCLASS_SUBCLASSES_FIRST(klass) == NULL)) {
432434
// no subclasses
433435
// check only current class
@@ -1752,6 +1754,8 @@ cached_callable_method_entry(VALUE klass, ID mid)
17521754
return ccs->cme;
17531755
}
17541756
else {
1757+
rb_vm_barrier();
1758+
17551759
rb_managed_id_table_delete(cc_tbl, mid);
17561760
rb_vm_ccs_invalidate_and_free(ccs);
17571761
}

0 commit comments

Comments
 (0)