Use mark context when marking execution_context

This commit is contained in:
Luke Gruber authored and Luke Gruber committed 2026-10-16 12:33:52 -04:00
1 parent 67cd6aa51a
commit 4c2968348e
9 files changed
+87 -79
Notes: git 2026-10-16 16:34:24 +00:00

No files matched your search

+29 -21
View File
@@ -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;
+1 -1
View File
@@ -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.
+2 -4
View File
@@ -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
+2 -1
View File
@@ -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 */
+8 -8
View File
@@ -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);
}
+41 -40
View File
@@ -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");
}
+2 -2
View File
@@ -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);
+1 -1
View File
@@ -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.
+1 -1
View File
@@ -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;