From 4c2968348e8409ec3a750bdf99e8a914c10cfc3f Mon Sep 17 00:00:00 2001 From: Luke Gruber Date: Thu, 8 Oct 2026 17:31:58 -0400 Subject: [PATCH] Use mark context when marking execution_context --- cont.c | 50 ++++++++++++---------- ext/objspace/objspace_dump.c | 2 +- gc.c | 6 +-- internal/thread.h | 3 +- ractor.c | 16 +++---- vm.c | 81 ++++++++++++++++++------------------ vm_core.h | 4 +- zjit.h | 2 +- zjit/src/gc.rs | 2 +- 9 files changed, 87 insertions(+), 79 deletions(-) diff --git a/cont.c b/cont.c index 28d285fd3f1..1853a9acae5 100644 --- a/cont.c +++ b/cont.c @@ -1268,44 +1268,50 @@ cont_compact(void *ptr) } static void -cont_mark(void *ptr) +cont_mark_ctx(const struct rb_gc_mark_ctx *ctx, rb_context_t *cont) { - rb_context_t *cont = ptr; - RUBY_MARK_ENTER("cont"); if (cont->self) { - rb_gc_mark_movable(cont->self); + rb_gc_mark_movable_ctx(ctx, cont->self); } - rb_gc_mark_movable(cont->value); + rb_gc_mark_movable_ctx(ctx, cont->value); - rb_execution_context_mark(&cont->saved_ec); - rb_gc_mark(cont_thread_value(cont)); + rb_execution_context_mark_ctx(ctx, &cont->saved_ec); + rb_gc_mark_ctx(ctx, cont_thread_value(cont)); if (cont->saved_vm_stack.ptr) { #ifdef CAPTURE_JUST_VALID_VM_STACK - rb_gc_mark_locations(cont->saved_vm_stack.ptr, - cont->saved_vm_stack.ptr + cont->saved_vm_stack.slen + cont->saved_vm_stack.clen); + rb_gc_mark_locations_ctx(ctx, cont->saved_vm_stack.ptr, + cont->saved_vm_stack.ptr + cont->saved_vm_stack.slen + cont->saved_vm_stack.clen); #else - rb_gc_mark_locations(cont->saved_vm_stack.ptr, - cont->saved_vm_stack.ptr, cont->saved_ec.stack_size); + rb_gc_mark_locations_ctx(ctx, cont->saved_vm_stack.ptr, + cont->saved_vm_stack.ptr, cont->saved_ec.stack_size); #endif } if (cont->machine.stack) { if (cont->type == CONTINUATION_CONTEXT) { /* cont */ - rb_gc_mark_locations(cont->machine.stack, - cont->machine.stack + cont->machine.stack_size); + rb_gc_mark_locations_ctx(ctx, cont->machine.stack, + cont->machine.stack + cont->machine.stack_size); } else { - /* fiber machine context is marked as part of rb_execution_context_mark, no need to - * do anything here. */ + /* fiber machine context is marked as part of rb_execution_context_mark_ctx, no need + * to do anything here. */ } } RUBY_MARK_LEAVE("cont"); } +static void +cont_mark(void *ptr) +{ + const struct rb_gc_mark_ctx ctx = rb_gc_current_mark_ctx(); + + cont_mark_ctx(&ctx, (rb_context_t *)ptr); +} + #if 0 static int fiber_is_root_p(const rb_fiber_t *fiber) @@ -1382,9 +1388,9 @@ rb_fiber_update_self(rb_fiber_t *fiber) } void -rb_fiber_mark_self(const rb_fiber_t *fiber) +rb_fiber_mark_self(const struct rb_gc_mark_ctx *ctx, const rb_fiber_t *fiber) { - rb_gc_mark_movable(fiber->cont.self); + rb_gc_mark_movable_ctx(ctx, fiber->cont.self); } static void @@ -1403,11 +1409,13 @@ static void fiber_mark(void *ptr) { rb_fiber_t *fiber = ptr; + const struct rb_gc_mark_ctx ctx = rb_gc_current_mark_ctx(); + RUBY_MARK_ENTER("cont"); fiber_verify(fiber); - rb_gc_mark_movable(fiber->first_proc); - if (fiber->prev) rb_fiber_mark_self(fiber->prev); - cont_mark(&fiber->cont); + rb_gc_mark_movable_ctx(&ctx, fiber->first_proc); + if (fiber->prev) rb_fiber_mark_self(&ctx, fiber->prev); + cont_mark_ctx(&ctx, &fiber->cont); RUBY_MARK_LEAVE("cont"); } @@ -1901,7 +1909,7 @@ fiber_setcontext(rb_fiber_t *new_fiber, rb_fiber_t *old_fiber) } } - /* these values are used in rb_gc_mark_machine_context to mark the fiber's stack. */ + /* these values are used in rb_gc_mark_machine_context_ctx to mark the fiber's stack. */ old_fiber->cont.saved_ec.machine.stack_start = th->ec->machine.stack_start; old_fiber->cont.saved_ec.machine.stack_end = FIBER_TERMINATED_P(old_fiber) ? NULL : th->ec->machine.stack_end; diff --git a/ext/objspace/objspace_dump.c b/ext/objspace/objspace_dump.c index bcd6f014f24..2acb80a89a3 100644 --- a/ext/objspace/objspace_dump.c +++ b/ext/objspace/objspace_dump.c @@ -759,7 +759,7 @@ dump_output(struct dump_config *dc, VALUE output, VALUE full, VALUE since, VALUE // Mark functions generally can't handle these possibilities so // the usual IO code is unsafe in this context. (For example, // there are many ways to crash when ruby code runs and mutates - // the execution context while rb_execution_context_mark() is in + // the execution context while rb_execution_context_mark_ctx() is in // progress.) // // Using FILE* isn't perfect, but it avoids the most acute problems. diff --git a/gc.c b/gc.c index 5d3fd71c31c..aee89616a9a 100644 --- a/gc.c +++ b/gc.c @@ -3431,11 +3431,9 @@ gc_mark_machine_context(const struct rb_gc_mark_ctx *ctx, const rb_execution_con } void -rb_gc_mark_machine_context(const rb_execution_context_t *ec) +rb_gc_mark_machine_context_ctx(const struct rb_gc_mark_ctx *ctx, const rb_execution_context_t *ec) { - const struct rb_gc_mark_ctx ctx = gc_current_mark_ctx(); - - gc_mark_machine_context(&ctx, ec); + gc_mark_machine_context(ctx, ec); } static int diff --git a/internal/thread.h b/internal/thread.h index f1092ea7c4a..bbcbb1235c0 100644 --- a/internal/thread.h +++ b/internal/thread.h @@ -13,6 +13,7 @@ #include "ccan/list/list.h" /* for list in rb_io_close_wait_list */ struct rb_execution_context_struct; /* in vm_core.h */ +struct rb_gc_mark_ctx; /* in internal/gc.h */ struct rb_thread_struct; /* in vm_core.h */ struct rb_io; @@ -104,6 +105,6 @@ void rb_threadptr_interrupt_exec_task_mark(struct rb_thread_struct *th); /* Mark the roots of the heap objects a thread owns, excluding ec and fiber. Used by * thread_mark and by the Ractor's local-root marking (rb_ractor_mark_local_roots). */ -void rb_thread_mark_owned_roots(struct rb_thread_struct *th); +void rb_thread_mark_owned_roots(const struct rb_gc_mark_ctx *ctx, struct rb_thread_struct *th); #endif /* INTERNAL_THREAD_H */ diff --git a/ractor.c b/ractor.c index d79b96e28f1..2f41ab61986 100644 --- a/ractor.c +++ b/ractor.c @@ -243,30 +243,30 @@ mark_targeted_hook_list(st_data_t key, st_data_t value, st_data_t _arg) } static void -ractor_mark_thread(rb_thread_t *th) +ractor_mark_thread(const struct rb_gc_mark_ctx *ctx, rb_thread_t *th) { - rb_gc_mark(th->self); + rb_gc_mark_ctx(ctx, th->self); /* A thread's ec lives inside the root fiber struct and is freed with that * fiber's wrapper object, so keep the fiber wrappers alive from here too. */ if (th->root_fiber) { VALUE root_fiber_self = rb_fiberptr_self(th->root_fiber); - if (root_fiber_self) rb_gc_mark(root_fiber_self); + if (root_fiber_self) rb_gc_mark_ctx(ctx, root_fiber_self); } /* The ec sits inside its fiber, so marking that fiber's wrapper scans the ec * as well. Only when there is no wrapper yet (mid-creation, teardown) does * the ec need marking of its own. */ VALUE ec_fiber_self = (th->ec && th->ec->fiber_ptr) ? rb_fiberptr_self(th->ec->fiber_ptr) : 0; if (ec_fiber_self) { - rb_gc_mark(ec_fiber_self); + rb_gc_mark_ctx(ctx, ec_fiber_self); } else if (th->ec) { - rb_execution_context_mark(th->ec); + rb_execution_context_mark_ctx(ctx, th->ec); } /* Root the thread's remaining possessions directly as well; thgroup in * particular has no other root. */ - rb_thread_mark_owned_roots(th); + rb_thread_mark_owned_roots(ctx, th); } static void @@ -295,7 +295,7 @@ ractor_mark_unshareable_parts(const struct rb_gc_mark_ctx *ctx, rb_ractor_t *r) rb_thread_t *th = 0; ccan_list_for_each(&r->threads.set, th, lt_node) { VM_ASSERT(th != NULL); - ractor_mark_thread(th); + ractor_mark_thread(ctx, th); } } @@ -306,7 +306,7 @@ ractor_mark_unshareable_parts(const struct rb_gc_mark_ctx *ctx, rb_ractor_t *r) * thread_mark walks whenever a terminated Thread's wrapper is still * referenced, and ractor_mark_thread performs the same marks. */ rb_thread_t *dying_th = RUBY_ATOMIC_PTR_LOAD(r->threads.dying_th); - if (dying_th) ractor_mark_thread(dying_th); + if (dying_th) ractor_mark_thread(ctx, dying_th); ractor_local_storage_mark(r); } diff --git a/vm.c b/vm.c index 7267109233c..ae19d950e85 100644 --- a/vm.c +++ b/vm.c @@ -3918,12 +3918,12 @@ rb_execution_context_update(rb_execution_context_t *ec) static enum rb_id_table_iterator_result mark_local_storage_i(VALUE local, void *data) { - rb_gc_mark(local); + rb_gc_mark_ctx((const struct rb_gc_mark_ctx *)data, local); return ID_TABLE_CONTINUE; } void -rb_execution_context_mark(const rb_execution_context_t *ec) +rb_execution_context_mark_ctx(const struct rb_gc_mark_ctx *ctx, const rb_execution_context_t *ec) { /* mark VM stack */ if (ec->vm_stack) { @@ -3937,10 +3937,10 @@ rb_execution_context_mark(const rb_execution_context_t *ec) if (rb_zjit_enabled_p) { // ZJIT leaves uninitialized slots on the VM stack, so we need // to mark such slots conservatively. - rb_gc_mark_maybe(p[i]); + rb_gc_mark_maybe_ctx(ctx, p[i]); } else { - rb_gc_mark_movable(p[i]); + rb_gc_mark_movable_ctx(ctx, p[i]); } } @@ -3948,39 +3948,39 @@ rb_execution_context_mark(const rb_execution_context_t *ec) const VALUE *ep = cfp->ep; VM_ASSERT(!!VM_ENV_FLAGS(ep, VM_ENV_FLAG_ESCAPED) == vm_ep_in_heap_p_(ec, ep)); - rb_gc_mark_movable(cfp->self); + rb_gc_mark_movable_ctx(ctx, cfp->self); if (CFP_ZJIT_FRAME_P(cfp)) { const zjit_jit_frame_t *jit_frame = CFP_ZJIT_FRAME(cfp); - rb_gc_mark_movable((VALUE)jit_frame->iseq); + rb_gc_mark_movable_ctx(ctx, (VALUE)jit_frame->iseq); // materialize_block_code means cfp->block_code is lazy and may // still contain stale data from a previous frame. Otherwise it // was initialized by ZJIT and may have been written later by // vm_caller_setup_arg_block (ISEQ frames) or rb_iterate0 (C frames). if (!jit_frame->materialize_block_code) { - rb_gc_mark_movable((VALUE)cfp->block_code); + rb_gc_mark_movable_ctx(ctx, (VALUE)cfp->block_code); } } else { - rb_gc_mark_movable((VALUE)cfp->_iseq); - rb_gc_mark_movable((VALUE)cfp->block_code); + rb_gc_mark_movable_ctx(ctx, (VALUE)cfp->_iseq); + rb_gc_mark_movable_ctx(ctx, (VALUE)cfp->block_code); } if (VM_ENV_LOCAL_P(ep) && VM_ENV_BOXED_P(ep)) { const rb_box_t *box = VM_ENV_BOX(ep); if (BOX_USER_P(box)) { - rb_gc_mark_movable(box->box_object); + rb_gc_mark_movable_ctx(ctx, box->box_object); } } if (!VM_ENV_LOCAL_P(ep)) { const VALUE *prev_ep = VM_ENV_PREV_EP(ep); if (VM_ENV_FLAGS(prev_ep, VM_ENV_FLAG_ESCAPED)) { - rb_gc_mark_movable(prev_ep[VM_ENV_DATA_INDEX_ENV]); + rb_gc_mark_movable_ctx(ctx, prev_ep[VM_ENV_DATA_INDEX_ENV]); } if (VM_ENV_FLAGS(ep, VM_ENV_FLAG_ESCAPED)) { - rb_gc_mark_movable(ep[VM_ENV_DATA_INDEX_ENV]); - rb_gc_mark(ep[VM_ENV_DATA_INDEX_ME_CREF]); + rb_gc_mark_movable_ctx(ctx, ep[VM_ENV_DATA_INDEX_ENV]); + rb_gc_mark_ctx(ctx, ep[VM_ENV_DATA_INDEX_ME_CREF]); } } @@ -3992,22 +3992,22 @@ rb_execution_context_mark(const rb_execution_context_t *ec) if (ec->machine.stack_start && ec->machine.stack_end && /* marked for current ec at the first stage of marking */ ec != rb_gc_get_ec()) { - rb_gc_mark_machine_context(ec); + rb_gc_mark_machine_context_ctx(ctx, ec); } - rb_gc_mark(ec->errinfo); - rb_gc_mark(ec->root_svar); + rb_gc_mark_ctx(ctx, ec->errinfo); + rb_gc_mark_ctx(ctx, ec->root_svar); if (ec->local_storage) { - rb_id_table_foreach_values(ec->local_storage, mark_local_storage_i, NULL); + rb_id_table_foreach_values(ec->local_storage, mark_local_storage_i, (void *)ctx); } - rb_gc_mark(ec->local_storage_recursive_hash); - rb_gc_mark(ec->local_storage_recursive_hash_for_trace); - rb_gc_mark(ec->private_const_reference); + rb_gc_mark_ctx(ctx, ec->local_storage_recursive_hash); + rb_gc_mark_ctx(ctx, ec->local_storage_recursive_hash_for_trace); + rb_gc_mark_ctx(ctx, ec->private_const_reference); - rb_gc_mark_movable(ec->storage); + rb_gc_mark_movable_ctx(ctx, ec->storage); } -void rb_fiber_mark_self(rb_fiber_t *fib); +void rb_fiber_mark_self(const struct rb_gc_mark_ctx *ctx, const rb_fiber_t *fib); void rb_fiber_update_self(rb_fiber_t *fib); void rb_threadptr_root_fiber_setup(rb_thread_t *th); void rb_root_fiber_obj_setup(rb_thread_t *th, void *objspace); @@ -4025,32 +4025,32 @@ thread_compact(void *ptr) * out of thread_mark so that a local GC can root them straight from the Ractor's * local roots (rb_ractor_mark_local_roots). */ void -rb_thread_mark_owned_roots(rb_thread_t *th) +rb_thread_mark_owned_roots(const struct rb_gc_mark_ctx *ctx, rb_thread_t *th) { /* mark ruby objects */ switch (th->invoke_type) { case thread_invoke_type_proc: case thread_invoke_type_ractor_proc: - rb_gc_mark(th->invoke_arg.proc.proc); - rb_gc_mark(th->invoke_arg.proc.args); + rb_gc_mark_ctx(ctx, th->invoke_arg.proc.proc); + rb_gc_mark_ctx(ctx, th->invoke_arg.proc.args); break; case thread_invoke_type_func: - rb_gc_mark_maybe((VALUE)th->invoke_arg.func.arg); + rb_gc_mark_maybe_ctx(ctx, (VALUE)th->invoke_arg.func.arg); break; default: break; } - rb_gc_mark(th->thgroup); - rb_gc_mark(th->value); - rb_gc_mark(th->pending_interrupt_queue); - rb_gc_mark(th->pending_interrupt_mask_stack); - rb_gc_mark(th->top_self); - rb_gc_mark(th->top_wrapper); - rb_gc_mark(th->last_status); - rb_gc_mark(th->locking_mutex); - rb_gc_mark(th->name); - rb_gc_mark(th->scheduler); + rb_gc_mark_ctx(ctx, th->thgroup); + rb_gc_mark_ctx(ctx, th->value); + rb_gc_mark_ctx(ctx, th->pending_interrupt_queue); + rb_gc_mark_ctx(ctx, th->pending_interrupt_mask_stack); + rb_gc_mark_ctx(ctx, th->top_self); + rb_gc_mark_ctx(ctx, th->top_wrapper); + rb_gc_mark_ctx(ctx, th->last_status); + rb_gc_mark_ctx(ctx, th->locking_mutex); + rb_gc_mark_ctx(ctx, th->name); + rb_gc_mark_ctx(ctx, th->scheduler); rb_threadptr_interrupt_exec_task_mark(th); } @@ -4059,22 +4059,23 @@ static void thread_mark(void *ptr) { rb_thread_t *th = ptr; + const struct rb_gc_mark_ctx ctx = rb_gc_current_mark_ctx(); RUBY_MARK_ENTER("thread"); // ec is null when setting up the thread in rb_threadptr_root_fiber_setup if (th->ec) { - rb_fiber_mark_self(th->ec->fiber_ptr); + rb_fiber_mark_self(&ctx, th->ec->fiber_ptr); } /* A live thread wrapper keeps its Ractor object alive (and through its dfree the * rb_ractor_t), so an inherited Thread keeps a dead Ractor alive just as it does * upstream. */ - if (th->ractor) rb_gc_mark(rb_ractor_self(th->ractor)); - if (th->root_fiber) rb_fiber_mark_self(th->root_fiber); + if (th->ractor) rb_gc_mark_ctx(&ctx, rb_ractor_self(th->ractor)); + if (th->root_fiber) rb_fiber_mark_self(&ctx, th->root_fiber); RUBY_ASSERT(th->ec == NULL || th->ec == rb_fiberptr_get_ec(th->ec->fiber_ptr)); - rb_thread_mark_owned_roots(th); + rb_thread_mark_owned_roots(&ctx, th); RUBY_MARK_LEAVE("thread"); } diff --git a/vm_core.h b/vm_core.h index 98ee004d51b..a23c2ed333f 100644 --- a/vm_core.h +++ b/vm_core.h @@ -2125,7 +2125,7 @@ void rb_vm_register_special_exception_str(enum ruby_special_exceptions sp, VALUE #define rb_vm_register_special_exception(sp, e, m) \ rb_vm_register_special_exception_str(sp, e, rb_usascii_str_new_static((m), (rb_len_t)rb_strlen_lit(m))) -void rb_gc_mark_machine_context(const rb_execution_context_t *ec); +void rb_gc_mark_machine_context_ctx(const struct rb_gc_mark_ctx *ctx, const rb_execution_context_t *ec); const rb_callable_method_entry_t *rb_vm_frame_method_entry(const rb_control_frame_t *cfp); const rb_callable_method_entry_t *rb_vm_frame_method_entry_unchecked(const rb_control_frame_t *cfp); @@ -2362,7 +2362,7 @@ void rb_threadptr_pending_interrupt_enque(rb_thread_t *th, VALUE v); VALUE rb_ec_get_errinfo(const rb_execution_context_t *ec); void rb_ec_error_print(rb_execution_context_t * volatile ec, volatile VALUE errinfo); void rb_execution_context_update(rb_execution_context_t *ec); -void rb_execution_context_mark(const rb_execution_context_t *ec); +void rb_execution_context_mark_ctx(const struct rb_gc_mark_ctx *ctx, const rb_execution_context_t *ec); void rb_fiber_close(rb_fiber_t *fib); void Init_native_thread(rb_thread_t *th); int rb_vm_check_ints_blocking(rb_execution_context_t *ec); diff --git a/zjit.h b/zjit.h index 28e00f8e8a1..3ed2c98be5c 100644 --- a/zjit.h +++ b/zjit.h @@ -17,7 +17,7 @@ typedef struct zjit_jit_frame { // Program counter for this frame, used for backtraces and GC. // NULL for C frames (they don't have a Ruby PC). const VALUE *pc; - // The ISEQ this frame belongs to. Marked via rb_execution_context_mark. + // The ISEQ this frame belongs to. Marked via rb_execution_context_mark_ctx. // NULL for C frames. const rb_iseq_t *iseq; // Whether to materialize block_code when this frame is materialized. diff --git a/zjit/src/gc.rs b/zjit/src/gc.rs index ce9031f3f0d..bbf5f55a9b5 100644 --- a/zjit/src/gc.rs +++ b/zjit/src/gc.rs @@ -245,7 +245,7 @@ pub extern "C" fn rb_zjit_mark_all_executable() { #[unsafe(no_mangle)] pub extern "C" fn rb_zjit_root_mark() { // Mark iseq pointers in all JITFrames. JITFrames that are currently on the - // stack are also marked via rb_execution_context_mark, but JITFrames not on + // stack are also marked via rb_execution_context_mark_ctx, but JITFrames not on // the stack still need their iseqs kept alive because JIT code will reuse them. if !ZJITState::has_instance() { return;