From 745e577b0e0a92d11a66bf514e8ad9031590d741 Mon Sep 17 00:00:00 2001 From: "Yukihiro \"Matz\" Matsumoto" Date: Thu, 9 Oct 2025 16:22:25 +0900 Subject: [PATCH] mruby-task: treat root context as main task Implement main task wrapper following Fiber's pattern, where root context is represented by a special task object. This matches PicoRuby behavior where Task.current always returns a task object, even from root context. The main task is lazy-allocated on first Task.current call from root, stored in mrb->task.main_task, and has name "main", status RUNNING, priority 0. It wraps the root context without allocating a separate execution context. Co-authored-by: Claude --- include/mruby.h | 1 + mrbgems/mruby-task/src/task.c | 40 +++++++++++++++++++++++++++++++---- 2 files changed, 37 insertions(+), 4 deletions(-) diff --git a/include/mruby.h b/include/mruby.h index 3155cea45..0531a08b0 100644 --- a/include/mruby.h +++ b/include/mruby.h @@ -255,6 +255,7 @@ typedef struct mrb_task_state { volatile uint32_t tick; /* Current tick count */ volatile uint32_t wakeup_tick; /* Next wakeup tick */ volatile mrb_bool switching; /* Context switch pending flag */ + struct mrb_task *main_task; /* Main task wrapper for root context */ } mrb_task_state; #endif diff --git a/mrbgems/mruby-task/src/task.c b/mrbgems/mruby-task/src/task.c index 6f70aaf08..8f095e12e 100644 --- a/mrbgems/mruby-task/src/task.c +++ b/mrbgems/mruby-task/src/task.c @@ -51,10 +51,13 @@ mrb_task_free(mrb_state *mrb, void *ptr) { mrb_task *t = (mrb_task*)ptr; if (t) { - /* Unregister from GC protection */ - mrb_gc_unregister(mrb, t->self); + /* Unregister from GC protection (unless it's the main task during shutdown) */ + if (t != mrb->task.main_task) { + mrb_gc_unregister(mrb, t->self); + } /* Free context resources only if not already terminated by fiber_terminate */ + /* Main task never has allocated context (stbase/cibase are NULL) */ if (t->c.status != MRB_FIBER_TERMINATED) { if (t->c.stbase) { mrb_free(mrb, t->c.stbase); @@ -951,9 +954,29 @@ mrb_task_s_new(mrb_state *mrb, mrb_value self) static mrb_value mrb_task_s_current(mrb_state *mrb, mrb_value self) { - /* Check if we're in root context (not in a task) */ + /* Check if we're in root context */ if (mrb->c == mrb->root_c) { - return mrb_nil_value(); + /* Return main task wrapper (lazy-allocate if needed) */ + if (!mrb->task.main_task) { + struct RClass *task_class = mrb_class_ptr(self); + struct RData *data = mrb_data_object_alloc(mrb, task_class, NULL, &mrb_task_type); + mrb_task *t = (mrb_task*)mrb_calloc(mrb, 1, sizeof(mrb_task)); + + /* Initialize as main task - special status that's never scheduled */ + t->priority = 0; + t->status = MRB_TASKSTATUS_RUNNING; /* Always running */ + t->name = mrb_str_new_cstr(mrb, "main"); + t->self = mrb_obj_value(data); + data->data = t; + data->type = &mrb_task_type; + + /* Register for GC protection */ + mrb_gc_register(mrb, t->self); + + /* Note: t->c is not used - root context is in mrb->root_c */ + mrb->task.main_task = t; + } + return mrb->task.main_task->self; } /* Use pointer arithmetic to get task from context - O(1) */ @@ -1438,6 +1461,9 @@ mrb_mruby_task_gem_init(mrb_state *mrb) /* Initialize HAL (timer and interrupts) */ mrb_task_hal_init(mrb); + /* Initialize main task to NULL */ + mrb->task.main_task = NULL; + task_class = mrb_define_class(mrb, "Task", mrb->object_class); MRB_SET_INSTANCE_TT(task_class, MRB_TT_DATA); @@ -1473,6 +1499,12 @@ mrb_mruby_task_gem_init(mrb_state *mrb) void mrb_mruby_task_gem_final(mrb_state *mrb) { + /* Clear main task pointer - GC will handle freeing the object */ + if (mrb->task.main_task) { + mrb_gc_unregister(mrb, mrb->task.main_task->self); + mrb->task.main_task = NULL; + } + mrb_task_hal_final(mrb); }