Skip to content

Commit 31a1a39

Browse files
committed
ZJIT: Never yield to the GC while compiling
This fixes a reliable "ZJIT saw a dead object" repro on my machine, and should fix the flaky ones on CI. The code for disabling the GC is the same as the code in newobj_of(). See: https://github.com/ruby/ruby/actions/runs/18511676257/job/52753782036
1 parent 63a58c7 commit 31a1a39

3 files changed

Lines changed: 18 additions & 1 deletion

File tree

zjit/bindgen/src/main.rs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -130,6 +130,8 @@ fn main() {
130130
.allowlist_function("rb_singleton_class")
131131
.allowlist_function("rb_define_class")
132132
.allowlist_function("rb_class_get_superclass")
133+
.allowlist_function("rb_gc_disable_no_rest")
134+
.allowlist_function("rb_gc_enable")
133135
.allowlist_function("rb_gc_mark")
134136
.allowlist_function("rb_gc_mark_movable")
135137
.allowlist_function("rb_gc_location")

zjit/src/cruby.rs

Lines changed: 14 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -890,6 +890,14 @@ where
890890
let mut recursive_lock_level: c_uint = 0;
891891

892892
unsafe { rb_jit_vm_lock_then_barrier(&mut recursive_lock_level, file, line) };
893+
// Ensure GC is off while we have the VM lock because:
894+
// 1. We create many transient Rust collections that hold VALUEs during compilation.
895+
// It's extremely tricky to properly marked and reference update these, not to
896+
// mention the overhead and ergonomics issues.
897+
// 2. If we yield to the GC while compiling, it re-enters our mark and update functions.
898+
// This breaks `&mut` exclusivity since mark functions derive fresh `&mut` from statics
899+
// while there is a stack frame below it that has an overlapping `&mut`. That's UB.
900+
let gc_disabled_pre_call = unsafe { rb_gc_disable_no_rest() }.test();
893901

894902
let ret = match catch_unwind(func) {
895903
Ok(result) => result,
@@ -909,7 +917,12 @@ where
909917
}
910918
};
911919

912-
unsafe { rb_jit_vm_unlock(&mut recursive_lock_level, file, line) };
920+
unsafe {
921+
if !gc_disabled_pre_call {
922+
rb_gc_enable();
923+
}
924+
rb_jit_vm_unlock(&mut recursive_lock_level, file, line);
925+
};
913926

914927
ret
915928
}

zjit/src/cruby_bindings.inc.rs

Lines changed: 2 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

0 commit comments

Comments
 (0)