mruby-task: fix context switching and sleep implementation

This commit fixes several critical issues in the task scheduler:

1. Context switching now properly saves and restores ci/cci pointers
   and sets prev links, following the fiber implementation pattern.
   This prevents crashes when tasks complete.

2. Sleep implementation now falls back to blocking sleep (usleep/Sleep)
   when not in task context, fixing standalone sleep calls.

3. Removed unused functions q_find_task and task_free to eliminate
   compiler warnings.

4. Added platform-specific headers for sleep functions on Unix/Windows.

5. Enabled HAL initialization which was previously commented out.

6. Added SA_RESTART flag to SIGALRM handler to prevent timer from
   interrupting IO syscalls, fixing mrbtest IO.popen failures.

Co-authored-by: Claude <noreply@anthropic.com>
This commit is contained in:
Yukihiro "Matz" Matsumoto
2025-10-05 23:36:39 +09:00
parent 47dfb771cf
commit f58051c48e
+47 -39
View File
@@ -18,6 +18,12 @@
#include <mruby/variable.h>
#include <string.h>
#include <stdint.h>
#include <stdlib.h>
#ifdef _WIN32
#include <windows.h>
#else
#include <unistd.h>
#endif
#include "../include/task.h"
/*
@@ -127,25 +133,6 @@ q_delete_task(mrb_state *mrb, mrb_task *t)
}
}
/* Find task in all queues */
static mrb_task*
q_find_task(mrb_state *mrb, mrb_task *t)
{
mrb_task *curr;
int i;
for (i = 0; i < MRB_NUM_TASK_QUEUE; i++) {
curr = mrb->task.queues[i];
while (curr != NULL) {
if (curr == t) {
return curr;
}
curr = curr->next;
}
}
return NULL;
}
/*
* Task lifecycle
*/
@@ -159,19 +146,6 @@ task_alloc(mrb_state *mrb)
return t;
}
/* Free task and context */
static void
task_free(mrb_state *mrb, mrb_task *t)
{
if (t->c.stbase) {
mrb_free(mrb, t->c.stbase);
}
if (t->c.cibase) {
mrb_free(mrb, t->c.cibase);
}
mrb_free(mrb, t);
}
/* Initialize task context (stack and callinfo) - similar to Fiber */
static void
task_init_context(mrb_state *mrb, mrb_task *t, const struct RProc *proc)
@@ -278,6 +252,8 @@ mrb_tasks_run(mrb_state *mrb)
{
mrb_task *t;
struct mrb_context *prev_c;
mrb_callinfo *prev_ci;
uint8_t prev_cci;
while (1) {
t = q_ready_;
@@ -299,6 +275,9 @@ mrb_tasks_run(mrb_state *mrb)
/* Switch to task context */
prev_c = mrb->c;
prev_ci = prev_c->ci; /* Save ci pointer */
prev_cci = prev_c->ci->cci; /* Save cci before context switch */
t->c.prev = mrb->c; /* Link task context to current context */
mrb->c = &t->c;
/* If task hasn't started yet, pop dummy callinfo */
@@ -318,10 +297,14 @@ mrb_tasks_run(mrb_state *mrb)
/* Restore context */
mrb->c = prev_c;
t->c.prev = NULL; /* Clear prev pointer to avoid dangling reference */
prev_c->ci = prev_ci; /* Restore ci pointer */
prev_ci->cci = prev_cci; /* Restore cci as it may have changed */
/* Check if task finished (returned without switching flag set) */
if (!switching_) {
/* Check if task finished (context status is TERMINATED) */
if (t->c.status == MRB_FIBER_TERMINATED) {
/* Task completed naturally - mark as dormant */
switching_ = FALSE; /* Clear switching flag */
/* Move to dormant queue */
mrb_task_disable_irq();
q_delete_task(mrb, t);
@@ -373,7 +356,15 @@ sleep_ms_impl(mrb_state *mrb, mrb_int ms)
{
mrb_task *t = q_ready_; /* Current running task */
if (!t) return; /* No task to sleep */
if (!t) {
/* Not in task context - use blocking sleep */
#ifdef __unix__
usleep((useconds_t)(ms * 1000));
#elif defined(_WIN32)
Sleep((DWORD)ms);
#endif
return;
}
mrb_task_disable_irq();
@@ -472,6 +463,15 @@ mrb_task_hal_init(mrb_state *mrb)
{
struct sigaction sa;
struct itimerval timer;
int i;
/* Initialize task state */
for (i = 0; i < 4; i++) {
mrb->task.queues[i] = NULL;
}
mrb->task.tick = 0;
mrb->task.wakeup_tick = UINT32_MAX;
mrb->task.switching = FALSE;
global_mrb = mrb;
@@ -481,7 +481,7 @@ mrb_task_hal_init(mrb_state *mrb)
/* Set up signal handler */
sa.sa_handler = sigalrm_handler;
sa.sa_flags = 0;
sa.sa_flags = SA_RESTART; /* Restart interrupted syscalls */
sigemptyset(&sa.sa_mask);
sigaction(SIGALRM, &sa, NULL);
@@ -519,7 +519,16 @@ mrb_task_hal_idle_cpu(mrb_state *mrb)
void
mrb_task_hal_init(mrb_state *mrb)
{
(void)mrb;
int i;
/* Initialize task state */
for (i = 0; i < 4; i++) {
mrb->task.queues[i] = NULL;
}
mrb->task.tick = 0;
mrb->task.wakeup_tick = UINT32_MAX;
mrb->task.switching = FALSE;
/* TODO: Platform-specific timer initialization */
}
@@ -960,8 +969,7 @@ mrb_mruby_task_gem_init(mrb_state *mrb)
struct RClass *task_class;
/* Initialize HAL (timer and interrupts) */
/* TODO: Enable after testing basic functionality */
/* mrb_task_hal_init(mrb); */
mrb_task_hal_init(mrb);
task_class = mrb_define_class(mrb, "Task", mrb->object_class);
MRB_SET_INSTANCE_TT(task_class, MRB_TT_DATA);