[JIT] Only call JIT single ractor mode invalidation once per process

Previously, JIT single-ractor mode invalidation happened on every `Ractor.new` call.
This invalidation takes the VM lock + VM barrier so slows down these calls. We only
need to invalidate compiled code that makes these single-ractor assumptions on the
transition from single-ractor to multi-ractor mode. The JITs never make single-ractor
assumptions when multi-ractor mode has been enabled.

If a process in multi-ractor mode forks, the forked process is set back to single-ractor
mode. The JITs can therefore make single-ractor assumptions in this new process, and the
invalidation can occur again when the first non-main Ractor is created in that process.
This commit is contained in:
Luke Gruber authored and Luke Gruber committed 2026-08-05 12:52:24 -04:00
1 parent 70874cc592
commit f688f1cb2c
5 files changed
+15 -12
Notes: git 2026-08-05 16:52:57 +00:00

No files matched your search

+3 -2
View File
@@ -415,6 +415,9 @@ cancel_single_ractor_mode(void)
RUBY_DEBUG_LOG("enable multi-ractor mode");
ruby_single_main_ractor = NULL;
rb_yjit_invalidate_single_ractor();
rb_zjit_invalidate_single_ractor();
rb_funcall(rb_cRactor, rb_intern("_activated"), 0);
}
@@ -601,8 +604,6 @@ ractor_create(rb_execution_context_t *ec, VALUE self, VALUE loc, VALUE name, VAL
r->verbose = cr->verbose;
r->debug = cr->debug;
rb_yjit_before_ractor_spawn();
rb_zjit_before_ractor_spawn();
rb_thread_create_ractor(r, args, block);
RB_GC_GUARD(rv);
+2 -2
View File
@@ -43,7 +43,7 @@ void rb_yjit_constant_state_changed(ID id);
void rb_yjit_iseq_mark(void *payload);
void rb_yjit_iseq_update_references(const rb_iseq_t *iseq);
void rb_yjit_iseq_free(const rb_iseq_t *iseq);
void rb_yjit_before_ractor_spawn(void);
void rb_yjit_invalidate_single_ractor(void);
void rb_yjit_constant_ic_update(const rb_iseq_t *const iseq, IC ic, unsigned insn_idx);
void rb_yjit_tracing_invalidate_all(void);
void rb_yjit_show_usage(int help, int highlight, unsigned int width, int columns);
@@ -71,7 +71,7 @@ static inline void rb_yjit_constant_state_changed(ID id) {}
static inline void rb_yjit_iseq_mark(void *payload) {}
static inline void rb_yjit_iseq_update_references(const rb_iseq_t *iseq) {}
static inline void rb_yjit_iseq_free(const rb_iseq_t *iseq) {}
static inline void rb_yjit_before_ractor_spawn(void) {}
static inline void rb_yjit_invalidate_single_ractor(void) {}
static inline void rb_yjit_constant_ic_update(const rb_iseq_t *const iseq, IC ic, unsigned insn_idx) {}
static inline void rb_yjit_tracing_invalidate_all(void) {}
static inline void rb_yjit_lazy_push_frame(const VALUE *pc) {}
+4 -3
View File
@@ -303,10 +303,11 @@ pub extern "C" fn rb_yjit_cme_invalidate(callee_cme: *const rb_callable_method_e
});
}
/// Callback for when Ruby is about to spawn a ractor. In that case we need to
/// invalidate every block that is assuming single ractor mode.
/// Invalidate every block that assumes single-ractor mode. Called when Ruby
/// transitions from single-ractor to multi-ractor mode (i.e. a second ractor
/// is spawned).
#[no_mangle]
pub extern "C" fn rb_yjit_before_ractor_spawn() {
pub extern "C" fn rb_yjit_invalidate_single_ractor() {
// If YJIT isn't enabled, do nothing
if !yjit_enabled_p() {
return;
+2 -2
View File
@@ -86,7 +86,7 @@ void rb_zjit_iseq_update_references(void *payload);
void rb_zjit_mark_all_writable(void);
void rb_zjit_mark_all_executable(void);
void rb_zjit_iseq_free(const rb_iseq_t *iseq);
void rb_zjit_before_ractor_spawn(void);
void rb_zjit_invalidate_single_ractor(void);
void rb_zjit_tracing_invalidate_all(void);
void rb_zjit_invalidate_no_singleton_class(VALUE klass);
void rb_zjit_invalidate_root_box(void);
@@ -133,7 +133,7 @@ static inline void rb_zjit_bop_redefined(int redefined_flag, enum ruby_basic_ope
static inline void rb_zjit_cme_invalidate(const rb_callable_method_entry_t *cme) {}
static inline void rb_zjit_invalidate_no_ep_escape(const rb_iseq_t *iseq) {}
static inline void rb_zjit_constant_state_changed(ID id) {}
static inline void rb_zjit_before_ractor_spawn(void) {}
static inline void rb_zjit_invalidate_single_ractor(void) {}
static inline void rb_zjit_tracing_invalidate_all(void) {}
static inline void rb_zjit_invalidate_no_singleton_class(VALUE klass) {}
static inline void rb_zjit_invalidate_root_box(void) {}
+4 -3
View File
@@ -398,10 +398,11 @@ pub fn track_single_ractor_assumption(
));
}
/// Callback for when Ruby is about to spawn a ractor. In that case we need to
/// invalidate every block that is assuming single ractor mode.
/// Invalidate every block that assumes single-ractor mode. Called when Ruby
/// transitions from single-ractor to multi-ractor mode (i.e. a second ractor
/// is spawned).
#[unsafe(no_mangle)]
pub extern "C" fn rb_zjit_before_ractor_spawn() {
pub extern "C" fn rb_zjit_invalidate_single_ractor() {
// If ZJIT isn't enabled, do nothing
if !zjit_enabled_p() {
return;