diff --git a/include/mruby.h b/include/mruby.h index db89b6236..7bbfb9634 100644 --- a/include/mruby.h +++ b/include/mruby.h @@ -274,6 +274,7 @@ typedef struct mrb_task_state { volatile mrb_bool switching; /* Context switch pending flag */ struct mrb_task *main_task; /* Main task wrapper for root context */ uint8_t scheduler_lock; /* Lock counter for synchronous execution */ + mrb_bool loop_running; /* Active mrb_task_run loop flag */ } mrb_task_state; #endif diff --git a/mrbgems/mruby-task/src/task.c b/mrbgems/mruby-task/src/task.c index 70f9e6233..695f6f81c 100644 --- a/mrbgems/mruby-task/src/task.c +++ b/mrbgems/mruby-task/src/task.c @@ -14,6 +14,7 @@ #include #include #include +#include #include #include #include @@ -443,44 +444,62 @@ mrb_tick(mrb_state *mrb) MRB_API mrb_value mrb_task_run(mrb_state *mrb) { + struct mrb_jmpbuf *prev_jmp; + struct mrb_jmpbuf c_jmp; mrb_task *t; - while (1) { - t = q_ready_; - - /* No task ready - check if all tasks are done */ - if (!t) { - mrb_task_disable_irq(); - mrb_bool exiting = !q_ready_ && !q_waiting_ && !q_suspended_; - mrb_task_enable_irq(); - if (exiting) { - /* 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 */ - if (task_cleanup_if_stopped(mrb, t)) { - continue; - } - - /* Execute task using core logic */ - execute_task(mrb, t); - - /* Move to end of ready queue if still running (round-robin) */ - if (t->status == MRB_TASK_STATUS_READY) { - task_change_state(mrb, t, MRB_TASK_STATUS_READY); - } - - /* Run incremental GC if active */ - if (mrb->gc.state != MRB_GC_STATE_ROOT) { - mrb_incremental_gc(mrb); - } + if (mrb->task.loop_running) { + return mrb_nil_value(); } + mrb->task.loop_running = TRUE; + prev_jmp = mrb->jmp; + MRB_TRY(&c_jmp) { + mrb->jmp = &c_jmp; + + while (1) { + t = q_ready_; + + /* No task ready - check if all tasks are done */ + if (!t) { + mrb_task_disable_irq(); + mrb_bool exiting = !q_ready_ && !q_waiting_ && !q_suspended_; + mrb_task_enable_irq(); + if (exiting) { + /* 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 */ + if (task_cleanup_if_stopped(mrb, t)) { + continue; + } + + /* Execute task using core logic */ + execute_task(mrb, t); + + /* Move to end of ready queue if still running (round-robin) */ + if (t->status == MRB_TASK_STATUS_READY) { + task_change_state(mrb, t, MRB_TASK_STATUS_READY); + } + + /* Run incremental GC if active */ + if (mrb->gc.state != MRB_GC_STATE_ROOT) { + mrb_incremental_gc(mrb); + } + } + mrb->jmp = prev_jmp; + } MRB_CATCH(&c_jmp) { + mrb->task.loop_running = FALSE; + mrb->jmp = prev_jmp; + MRB_THROW(prev_jmp); + } MRB_END_EXC(&c_jmp); + + mrb->task.loop_running = FALSE; return mrb_nil_value(); } @@ -1505,6 +1524,7 @@ mrb_mruby_task_gem_init(mrb_state *mrb) /* Initialize main task to NULL and scheduler_lock to 0 */ mrb->task.main_task = NULL; mrb->task.scheduler_lock = 0; + mrb->task.loop_running = FALSE; task_class = mrb_define_class_id(mrb, MRB_SYM(Task), mrb->object_class); MRB_SET_INSTANCE_TT(task_class, MRB_TT_DATA); diff --git a/mrbgems/mruby-task/test/task.rb b/mrbgems/mruby-task/test/task.rb index e93c525af..048bb8136 100644 --- a/mrbgems/mruby-task/test/task.rb +++ b/mrbgems/mruby-task/test/task.rb @@ -183,3 +183,10 @@ assert("Task.new with block doesn't execute immediately") do # Block should not execute until scheduler runs assert_false executed end + +assert("Task.run inside Task.run is a noop") do + assert_nothing_raised do + Task.new { Task.run } + Task.run + end +end