hasumikin · GitHub

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.
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.

@hasumikin

@hasumikin

gemini-code-assist[bot]

gemini-code-assist[bot]

Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>

@hasumikin hasumikin changed the title Fix mruby-task: wrapping by critical section and the Fix mruby-task: wrapping by critical section and setting initial task receiver to top_self

Feb 13, 2026

@matz

@hasumikin

Read the original on github.com ↗