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.
This commit is contained in:
Luke Gruber authored and Luke Gruber committed 2026-10-26 14:29:57 -04:00
1 parent 6b15825df0
commit 29264f878a
2 files changed
+12 -15
Notes: git 2026-10-26 18:30:31 +00:00

No files matched your search

+6 -5
View File
@@ -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();
+6 -10
View File
@@ -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)) {