mirror of
https://github.com/mruby/mruby
synced 2026-06-08 16:11:16 +00:00
mruby-task: prevent segfault when sleep is called from C function; fix #6642
when sleep() was called from within a C function (such as module.new's block evaluation), the task scheduler would segfault while attempting to resume the task. this occurred because C functions don't execute bytecode and thus their callinfo has no valid program counter (pc). when the task tried to resume execution, mrb_vm_exec() received a null pc, causing a segmentation fault. the fix adds two safeguards in task.c: 1. C function boundary detection: before suspending a task for sleep, check if we're inside a C function by examining the cci (c call info) field. if cci > 0, fall back to blocking sleep via HAL instead of attempting cooperative context switch. this preserves sleep functionality without raising exceptions, though it blocks other tasks during the sleep period. 2. proc fallback in execute_task(): use the task's stored proc if the current callinfo's proc is null, ensuring mrb_vm_exec() always receives a valid proc pointer. this approach prioritizes functionality over strict cooperative multitasking semantics - tasks can still sleep inside C functions, but the sleep becomes blocking. the alternative would be raising an exception like fiber does, but that would break existing code unexpectedly. Co-authored-by: Claude <noreply@anthropic.com>
This commit is contained in:
@@ -336,7 +336,8 @@ execute_task(mrb_state *mrb, mrb_task *t)
|
||||
t->c.status = MRB_FIBER_RUNNING;
|
||||
|
||||
/* Save proc and PC to locals before calling mrb_vm_exec */
|
||||
const struct RProc *proc = t->c.ci->proc;
|
||||
/* Use current callinfo's proc if available, otherwise use task's stored proc */
|
||||
const struct RProc *proc = t->c.ci->proc ? t->c.ci->proc : mrb_proc_ptr(t->proc);
|
||||
const mrb_code *pc = t->c.ci->pc;
|
||||
|
||||
/* Set vmexec flag to prevent fiber_terminate from being called */
|
||||
@@ -491,6 +492,17 @@ sleep_us_impl(mrb_state *mrb, mrb_int usec)
|
||||
return;
|
||||
}
|
||||
|
||||
/* Check for C function boundary - cannot do cooperative context switch */
|
||||
mrb_callinfo *ci;
|
||||
for (ci = mrb->c->ci; ci >= mrb->c->cibase; ci--) {
|
||||
if (ci->cci > 0) {
|
||||
/* Inside C function - fall back to blocking sleep without context switch */
|
||||
mrb_hal_task_sleep_us(mrb, usec);
|
||||
switching_ = FALSE;
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
/* In task context - get current running task */
|
||||
t = MRB2TASK(mrb);
|
||||
|
||||
|
||||
Reference in New Issue
Block a user