From 29264f878a52d2c36c75bfca6d04adaefeacbb30 Mon Sep 17 00:00:00 2001 From: Luke Gruber Date: Fri, 23 Oct 2026 12:21:55 -0400 Subject: [PATCH] Fix rb_gc_ever_multi_ractor_p to simply read the latch variable In single-ractor mode, we don't want foreign_object_p checks to actually run (there can be no foreign objspaces). This foreign_object_p check return false if there was never a (non-main) Ractor created. Previously, rb_gc_ever_multi_ractor_p() was needlessly complex. This change should speed up marking and WB implementations in single-ractor mode. --- gc.c | 11 ++++++----- gc/default/default.c | 16 ++++++---------- 2 files changed, 12 insertions(+), 15 deletions(-) diff --git a/gc.c b/gc.c index bee89f5c185..38af88db150 100644 --- a/gc.c +++ b/gc.c @@ -1083,15 +1083,14 @@ rb_gc_init_objspaces(void) #endif } -/* Stays true once the process has gone multi-Ractor (rb_multi_ractor_p goes back to - * false when the other Ractors finish). Used by verification that spans generation - * state built while multiple Ractors ran. */ +/* Stays true once a second objspace has existed (rb_multi_ractor_p goes back to false + * when the other Ractors finish, and is false for the whole window in which the creator + * builds the child objspace). */ static bool gc_ever_multi_ractor = false; bool rb_gc_ever_multi_ractor_p(void) { - if (!gc_ever_multi_ractor && rb_multi_ractor_p()) gc_ever_multi_ractor = true; return gc_ever_multi_ractor; } @@ -1100,7 +1099,9 @@ rb_gc_ever_multi_ractor_p(void) void * rb_gc_objspace_alloc(void) { - gc_ever_multi_ractor = true; + /* The one store that ever runs is the first Ractor creation, which is by definition + * single-Ractor and so races no reader. */ + if (!gc_ever_multi_ractor) gc_ever_multi_ractor = true; if (!rb_gc_impl_multi_objspace_p()) { /* One objspace shared by every Ractor. */ return rb_gc_get_objspace(); diff --git a/gc/default/default.c b/gc/default/default.c index 44482a6e5c2..59b84aea127 100644 --- a/gc/default/default.c +++ b/gc/default/default.c @@ -1271,6 +1271,8 @@ slot_index_for_offset(size_t offset, uint64_t reciprocal) static inline bool gc_foreign_object_p(const rb_objspace_t *objspace, VALUE obj) { + if (RB_LIKELY(!rb_gc_ever_multi_ractor_p())) return false; + return RB_UNLIKELY(GET_HEAP_OBJSPACE(obj) != objspace); } @@ -8501,13 +8503,8 @@ rb_gc_impl_writebarrier(void *objspace_ptr, VALUE a, VALUE b) if (!is_incremental_marking(objspace)) { /* The generational barrier covers old->young edges within one objspace only. * NOTE: we shouldn't even check the age of `a` or `b` if they are in another - * objspace, so check locality first. The test is rb_gc_ever_multi_ractor_p, not - * rb_gc_multi_ractor_p: a foreign objspace exists before the process is - * multi-Ractor (rb_gc_objspace_alloc runs while the creator is still the only - * Ractor) and outlives the return to one (a fork parks the others in - * zombie_objspaces). */ - if ((rb_gc_ever_multi_ractor_p() && - (GET_HEAP_OBJSPACE(a) != objspace || GET_HEAP_OBJSPACE(b) != objspace)) || + * objspace, so check locality first. */ + if (gc_foreign_object_p(objspace, a) || gc_foreign_object_p(objspace, b) || !RVALUE_OLD_P(objspace, a) || RVALUE_OLD_P(objspace, b)) { // do nothing } @@ -8517,8 +8514,7 @@ rb_gc_impl_writebarrier(void *objspace_ptr, VALUE a, VALUE b) } else { // Shareable objects from different object spaces are kept alive by shareable bits - if (rb_gc_ever_multi_ractor_p() && - (GET_HEAP_OBJSPACE(a) != objspace || GET_HEAP_OBJSPACE(b) != objspace)) { + if (gc_foreign_object_p(objspace, a) || gc_foreign_object_p(objspace, b)) { // do nothing } else { @@ -8615,7 +8611,7 @@ rb_gc_impl_writebarrier_remember(void *objspace_ptr, VALUE obj) // Shareable objects from other object spaces don't need to be put on the remembered set // and are only collected during global GC, so not while incremental marking. - if (RB_LIKELY(!rb_gc_ever_multi_ractor_p() || GET_HEAP_OBJSPACE(obj) == objspace)) { + if (RB_LIKELY(!gc_foreign_object_p(objspace, obj))) { gc_report(1, objspace, "rb_gc_writebarrier_remember: %s\n", rb_obj_info(obj)); if (is_incremental_marking(objspace)) { if (RVALUE_BLACK_P(objspace, obj)) {