Skip to content

Commit 745e577

Browse files
matzclaude
andcommitted
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 <noreply@anthropic.com>
1 parent 6461af9 commit 745e577

2 files changed

Lines changed: 37 additions & 4 deletions

File tree

include/mruby.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -255,6 +255,7 @@ typedef struct mrb_task_state {
255255
volatile uint32_t tick; /* Current tick count */
256256
volatile uint32_t wakeup_tick; /* Next wakeup tick */
257257
volatile mrb_bool switching; /* Context switch pending flag */
258+
struct mrb_task *main_task; /* Main task wrapper for root context */
258259
} mrb_task_state;
259260
#endif
260261

mrbgems/mruby-task/src/task.c

Lines changed: 36 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -51,10 +51,13 @@ mrb_task_free(mrb_state *mrb, void *ptr)
5151
{
5252
mrb_task *t = (mrb_task*)ptr;
5353
if (t) {
54-
/* Unregister from GC protection */
55-
mrb_gc_unregister(mrb, t->self);
54+
/* Unregister from GC protection (unless it's the main task during shutdown) */
55+
if (t != mrb->task.main_task) {
56+
mrb_gc_unregister(mrb, t->self);
57+
}
5658

5759
/* Free context resources only if not already terminated by fiber_terminate */
60+
/* Main task never has allocated context (stbase/cibase are NULL) */
5861
if (t->c.status != MRB_FIBER_TERMINATED) {
5962
if (t->c.stbase) {
6063
mrb_free(mrb, t->c.stbase);
@@ -951,9 +954,29 @@ mrb_task_s_new(mrb_state *mrb, mrb_value self)
951954
static mrb_value
952955
mrb_task_s_current(mrb_state *mrb, mrb_value self)
953956
{
954-
/* Check if we're in root context (not in a task) */
957+
/* Check if we're in root context */
955958
if (mrb->c == mrb->root_c) {
956-
return mrb_nil_value();
959+
/* Return main task wrapper (lazy-allocate if needed) */
960+
if (!mrb->task.main_task) {
961+
struct RClass *task_class = mrb_class_ptr(self);
962+
struct RData *data = mrb_data_object_alloc(mrb, task_class, NULL, &mrb_task_type);
963+
mrb_task *t = (mrb_task*)mrb_calloc(mrb, 1, sizeof(mrb_task));
964+
965+
/* Initialize as main task - special status that's never scheduled */
966+
t->priority = 0;
967+
t->status = MRB_TASKSTATUS_RUNNING; /* Always running */
968+
t->name = mrb_str_new_cstr(mrb, "main");
969+
t->self = mrb_obj_value(data);
970+
data->data = t;
971+
data->type = &mrb_task_type;
972+
973+
/* Register for GC protection */
974+
mrb_gc_register(mrb, t->self);
975+
976+
/* Note: t->c is not used - root context is in mrb->root_c */
977+
mrb->task.main_task = t;
978+
}
979+
return mrb->task.main_task->self;
957980
}
958981

959982
/* Use pointer arithmetic to get task from context - O(1) */
@@ -1438,6 +1461,9 @@ mrb_mruby_task_gem_init(mrb_state *mrb)
14381461
/* Initialize HAL (timer and interrupts) */
14391462
mrb_task_hal_init(mrb);
14401463

1464+
/* Initialize main task to NULL */
1465+
mrb->task.main_task = NULL;
1466+
14411467
task_class = mrb_define_class(mrb, "Task", mrb->object_class);
14421468
MRB_SET_INSTANCE_TT(task_class, MRB_TT_DATA);
14431469

@@ -1473,6 +1499,12 @@ mrb_mruby_task_gem_init(mrb_state *mrb)
14731499
void
14741500
mrb_mruby_task_gem_final(mrb_state *mrb)
14751501
{
1502+
/* Clear main task pointer - GC will handle freeing the object */
1503+
if (mrb->task.main_task) {
1504+
mrb_gc_unregister(mrb, mrb->task.main_task->self);
1505+
mrb->task.main_task = NULL;
1506+
}
1507+
14761508
mrb_task_hal_final(mrb);
14771509
}
14781510

0 commit comments

Comments
 (0)