mirror of
https://github.com/mruby/mruby
synced 2026-06-08 16:11:16 +00:00
vm.c: defer task switches during gc.iterating
RETURN_IF_TASK_STOPPED used to bail out of mrb_vm_exec as soon as task.switching was set, even when the exec was called re-entrantly from inside a heap-walk callback (e.g. ObjectSpace.each_object via mrb_yield). Bailing in that situation only unwinds the inner exec, leaving the outer C iteration to keep calling the callback. The call-info stack drifts on each subsequent mrb_yield, and the program either trips the cibase assertion in mrb_vm_run or crashes in __longjmp. Hold off the switch while mrb->gc.iterating is set so the heap walk finishes intact; the switch then fires at the next OP boundary once the walk releases the flag. MRB_TASK_STOPPED is intentionally not deferred, since exiting promptly is still correct when the task itself is going away. Fixes #6862. Co-authored-by: Claude <noreply@anthropic.com>
This commit is contained in:
@@ -1588,8 +1588,16 @@ prepare_tagged_break(mrb_state *mrb, uint32_t tag, const mrb_callinfo *return_ci
|
||||
#define CALL_CODE_HOOKS() do { insn = BYTECODE_DECODER(*ci->pc); CODE_FETCH_HOOK(mrb, irep, ci->pc, regs); } while (0)
|
||||
|
||||
#ifdef MRB_USE_TASK_SCHEDULER
|
||||
/* Defer task switches while a C-level ObjectSpace walk holds gc.iterating
|
||||
true. The walk runs callbacks (which may call back into mrb_vm_exec via
|
||||
mrb_yield); returning early from an inner exec while the outer C
|
||||
iteration is still active drifts the call-info stack and eventually
|
||||
crashes (issue #6862). Switches resume at the next OP boundary after
|
||||
the walk releases gc.iterating. A pending MRB_TASK_STOPPED is not
|
||||
deferred -- the task is going away. */
|
||||
#define RETURN_IF_TASK_STOPPED(mrb) do { \
|
||||
if ((mrb)->task.switching || (mrb)->c->status == MRB_TASK_STOPPED) \
|
||||
if (((mrb)->task.switching && !(mrb)->gc.iterating) || \
|
||||
(mrb)->c->status == MRB_TASK_STOPPED) \
|
||||
return mrb_nil_value(); \
|
||||
} while (0)
|
||||
#define TASK_STOP(mrb) do { \
|
||||
|
||||
Reference in New Issue
Block a user