From cfcd86fd9b0d6aec4d86d2741acb27a9961ceccd Mon Sep 17 00:00:00 2001 From: HASUMI Hitoshi Date: Fri, 13 Feb 2026 13:34:54 +0900 Subject: [PATCH 1/3] Fix mrb_task_run to prevent returning unexpectedly Old code: ```c t = q_ready_; /* No task ready - check if all tasks are done */ if (!t) { /* If there are tasks waiting or suspended, idle */ if (q_waiting_ || q_suspended_) { mrb_hal_task_idle_cpu(mrb); continue; ``` IRQ possibly happens between `t = q_ready_;` and `if (q_waiting_ || q_suspended_) {` and, for example, a waiting task may move to the ready queue. As a result, the infinite loop in mrb_task_run unexpectedly breaks in spite of not all the task is dormant. This patch fixes the issue above by setting the `exitting` condition with a critical section. --- mrbgems/mruby-task/src/task.c | 15 +++++++++------ 1 file changed, 9 insertions(+), 6 deletions(-) diff --git a/mrbgems/mruby-task/src/task.c b/mrbgems/mruby-task/src/task.c index 9aca27260..3ca692487 100644 --- a/mrbgems/mruby-task/src/task.c +++ b/mrbgems/mruby-task/src/task.c @@ -459,13 +459,16 @@ mrb_task_run(mrb_state *mrb) /* No task ready - check if all tasks are done */ if (!t) { - /* If there are tasks waiting or suspended, idle */ - if (q_waiting_ || q_suspended_) { - mrb_hal_task_idle_cpu(mrb); - continue; + mrb_task_disable_irq(); + mrb_bool exitting = !q_ready_ && !q_waiting_ && !q_suspended_; + mrb_task_enable_irq(); + if (exitting) { + /* All tasks are dormant - scheduler done */ + break; } - /* All tasks are dormant - scheduler done */ - break; + /* If there are tasks waiting or suspended, idle */ + mrb_hal_task_idle_cpu(mrb); + continue; } /* Safety check - don't execute terminated tasks */ From ee610cdbb6fdaf54803b075285804541ec233dff Mon Sep 17 00:00:00 2001 From: HASUMI Hitoshi Date: Fri, 13 Feb 2026 13:59:48 +0900 Subject: [PATCH 2/3] Set initial task receiver to top_self for stability The current implementation of `task_init_context` inheriting a receiver from the parent task is unstable and causes critical faults, especially on microcontrollers. - It leads to a HardFault on devices like Raspberry Pi Pico 2 by accessing a potentially NULL `mrb->c->ci`. - Even when `mrb->c->ci` is not NULL, this incomplete context copy causes other memory errors (SEGV). This patch reverts to the safer, previous behavior, that I implemented in picoruby/picoruby, of always initializing a new task's receiver to `top_self`, ensuring predictable and robust operation. The issue was likely masked on POSIX systems due to the unpredictable nature of undefined behavior. --- mrbgems/mruby-task/src/task.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/mrbgems/mruby-task/src/task.c b/mrbgems/mruby-task/src/task.c index 3ca692487..01cd0d4c0 100644 --- a/mrbgems/mruby-task/src/task.c +++ b/mrbgems/mruby-task/src/task.c @@ -278,8 +278,8 @@ task_init_context(mrb_state *mrb, mrb_task *t, const struct RProc *proc) } } - /* Copy receiver from current context */ - c->stbase[0] = mrb->c->ci->stack[0]; + /* Set receiver to top self */ + c->stbase[0] = mrb_top_self(mrb); /* Initialize callinfo stack */ static const mrb_callinfo ci_zero = { 0 }; From 40d6e2e9a452dc06be2b04f68e8e7716ac48b533 Mon Sep 17 00:00:00 2001 From: HASUMI Hitoshi Date: Fri, 13 Feb 2026 14:34:34 +0900 Subject: [PATCH 3/3] Update mrbgems/mruby-task/src/task.c Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> --- mrbgems/mruby-task/src/task.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/mrbgems/mruby-task/src/task.c b/mrbgems/mruby-task/src/task.c index 01cd0d4c0..afefe957c 100644 --- a/mrbgems/mruby-task/src/task.c +++ b/mrbgems/mruby-task/src/task.c @@ -460,9 +460,9 @@ mrb_task_run(mrb_state *mrb) /* No task ready - check if all tasks are done */ if (!t) { mrb_task_disable_irq(); - mrb_bool exitting = !q_ready_ && !q_waiting_ && !q_suspended_; + mrb_bool exiting = !q_ready_ && !q_waiting_ && !q_suspended_; mrb_task_enable_irq(); - if (exitting) { + if (exiting) { /* All tasks are dormant - scheduler done */ break; }