diff --git a/mrbgems/mruby-task/include/task.h b/mrbgems/mruby-task/include/task.h new file mode 100644 index 000000000..f7a6bd4f7 --- /dev/null +++ b/mrbgems/mruby-task/include/task.h @@ -0,0 +1,111 @@ +/* +** task.h - Task scheduler +** +** See Copyright Notice in mruby.h +*/ + +#ifndef MRUBY_TASK_H +#define MRUBY_TASK_H + +#include + +#ifdef MRB_USE_TASK_SCHEDULER + +/* + * Task status values (bit-mapped) + */ +enum { + MRB_TASKSTATUS_DORMANT = 0x00, /* Not started or finished */ + MRB_TASKSTATUS_READY = 0x02, /* Ready to run */ + MRB_TASKSTATUS_RUNNING = 0x03, /* Currently executing */ + MRB_TASKSTATUS_WAITING = 0x04, /* Waiting for condition */ + MRB_TASKSTATUS_SUSPENDED = 0x08, /* Manually suspended */ +}; + +/* + * Task wait reason + */ +enum { + MRB_TASKREASON_NONE = 0x00, /* No specific reason */ + MRB_TASKREASON_SLEEP = 0x01, /* Sleeping for time */ + MRB_TASKREASON_MUTEX = 0x02, /* Waiting for mutex (reserved) */ + MRB_TASKREASON_JOIN = 0x04, /* Waiting for another task */ +}; + +/* + * Task Control Block (TCB) + */ +typedef struct mrb_tcb { + struct mrb_tcb *next; /* Linked list pointer */ + uint8_t priority; /* Initial priority (0-255, 0=highest) */ + uint8_t priority_preemption; /* Effective priority for preemption */ + volatile uint8_t timeslice; /* Remaining time slice ticks */ + uint8_t status; /* Current status (TASKSTATUS enum) */ + uint8_t reason; /* Wait reason (TASKREASON enum) */ + mrb_value name; /* Optional task name */ + + union { + uint32_t wakeup_tick; /* Tick count to wake up (for sleep) */ + void *mutex; /* Mutex pointer (reserved) */ + }; + const struct mrb_tcb *tcb_join; /* Task being waited on (for join) */ + + mrb_value task; /* Ruby Task object reference */ + mrb_value value; /* Task return value */ + struct mrb_context c; /* Execution context (stack, callinfo, etc) */ +} mrb_tcb; + +/* + * Task scheduler state (part of mrb_state) + */ +#define MRB_NUM_TASK_QUEUE 4 + +typedef struct mrb_task_state { + mrb_tcb *queues[MRB_NUM_TASK_QUEUE]; /* Task queues */ + volatile uint32_t tick; /* Current tick count */ + volatile uint32_t wakeup_tick; /* Next wakeup tick */ + volatile mrb_bool switching; /* Context switch pending flag */ +} mrb_task_state; + +/* Queue indices */ +#define MRB_TASK_QUEUE_DORMANT 0 +#define MRB_TASK_QUEUE_READY 1 +#define MRB_TASK_QUEUE_WAITING 2 +#define MRB_TASK_QUEUE_SUSPENDED 3 + +/* Configuration */ +#ifndef MRB_TICK_UNIT +#define MRB_TICK_UNIT 4 /* Tick period in milliseconds */ +#endif + +#ifndef MRB_TIMESLICE_TICK_COUNT +#define MRB_TIMESLICE_TICK_COUNT 3 /* Number of ticks per timeslice */ +#endif + +#define TASK_STACK_INIT_SIZE 64 /* Initial task stack size */ +#define TASK_CI_INIT_SIZE 8 /* Initial task callinfo size */ + +/* + * HAL (Hardware Abstraction Layer) functions + * Platform-specific implementations must provide these + */ +void mrb_task_hal_init(mrb_state *mrb); +void mrb_task_enable_irq(void); +void mrb_task_disable_irq(void); +void mrb_task_hal_idle_cpu(mrb_state *mrb); + +/* + * Core task scheduler API + */ +void mrb_tick(mrb_state *mrb); +mrb_value mrb_tasks_run(mrb_state *mrb); + +/* + * Task context status values (extends mrb_fiber_state) + */ +#define MRB_TASK_CREATED (MRB_FIBER_TRANSFERRED + 1) +#define MRB_TASK_STOPPED (MRB_FIBER_TRANSFERRED + 2) + +#endif /* MRB_USE_TASK_SCHEDULER */ + +#endif /* MRUBY_TASK_H */ diff --git a/mrbgems/mruby-task/mrbgem.rake b/mrbgems/mruby-task/mrbgem.rake new file mode 100644 index 000000000..c845a296a --- /dev/null +++ b/mrbgems/mruby-task/mrbgem.rake @@ -0,0 +1,8 @@ +MRuby::Gem::Specification.new('mruby-task') do |spec| + spec.license = 'MIT' + spec.authors = 'mruby developers' + spec.summary = 'Cooperative multitasking with preemptive scheduling' + + spec.cc.defines << 'MRB_USE_TASK_SCHEDULER' + spec.add_dependency 'mruby-fiber' # Uses same context infrastructure +end diff --git a/mrbgems/mruby-task/mrblib/task.rb b/mrbgems/mruby-task/mrblib/task.rb new file mode 100644 index 000000000..15e7d3c28 --- /dev/null +++ b/mrbgems/mruby-task/mrblib/task.rb @@ -0,0 +1,115 @@ +class Task + # Class methods + + # Create a new task with optional name and priority + # @param name [String, Symbol, nil] optional task name + # @param priority [Integer, nil] task priority (0-255, 0 is highest) + # @yield block to execute in the task + # @return [Task] the created task + def self.new(name: nil, priority: nil, &block) + end + + # Get the currently running task + # @return [Task] the current task + def self.current + end + + # Get all tasks in the system + # @return [Array] array of all tasks + def self.list + end + + # Yield execution to other tasks (cooperative yielding) + # @return [nil] + def self.pass + end + + # Get task scheduler statistics + # @return [Task::Stat] scheduler statistics object + def self.stat + end + + # Find a task by name + # @param name [String, Symbol] task name to find + # @return [Task, nil] the task if found, nil otherwise + def self.get(name) + end + + # Instance methods + + # Get the task status + # @return [Symbol] one of :DORMANT, :READY, :RUNNING, :WAITING, :SUSPENDED + def status + end + + # Get the task name + # @return [String, Symbol, nil] the task name + def name + end + + # Set the task name + # @param val [String, Symbol] new task name + def name=(val) + end + + # Get the task priority + # @return [Integer] priority value (0-255, 0 is highest) + def priority + end + + # Set the task priority + # @param val [Integer] new priority (0-255, 0 is highest) + def priority=(val) + end + + # Suspend the task + # @return [self] + def suspend + end + + # Resume a suspended task + # @return [self] + def resume + end + + # Terminate the task + # @return [self] + def terminate + end + + # Wait for the task to complete + # @return [self] + def join + end + + # Task scheduler statistics class + class Stat + attr_reader :tick, :wakeup_tick, :tasks + + def initialize(tick, wakeup_tick, tasks) + @tick = tick + @wakeup_tick = wakeup_tick + @tasks = tasks + end + + def to_s + "tick: #{@tick}, wakeup_tick: #{@wakeup_tick}, tasks: #{@tasks.size}" + end + end +end + +# Kernel methods for sleeping + +module Kernel + # Sleep for specified duration + # @param sec [Integer, Float, nil] seconds to sleep (nil = suspend indefinitely) + # @return [Integer, nil] actual sleep time + def sleep(sec = nil) + end + + # Sleep for specified milliseconds + # @param ms [Integer] milliseconds to sleep + # @return [nil] + def sleep_ms(ms) + end +end diff --git a/mrbgems/mruby-task/src/task.c b/mrbgems/mruby-task/src/task.c new file mode 100644 index 000000000..7736fe3db --- /dev/null +++ b/mrbgems/mruby-task/src/task.c @@ -0,0 +1,322 @@ +/* +** task.c - Task scheduler +** +** See Copyright Notice in mruby.h +*/ + +#include + +#ifdef MRB_USE_TASK_SCHEDULER + +#include +#include +#include +#include +#include +#include +#include +#include "../include/task.h" + +/* + * Queue helper macros + */ +#define q_dormant_ (mrb->task.queues[MRB_TASK_QUEUE_DORMANT]) +#define q_ready_ (mrb->task.queues[MRB_TASK_QUEUE_READY]) +#define q_waiting_ (mrb->task.queues[MRB_TASK_QUEUE_WAITING]) +#define q_suspended_ (mrb->task.queues[MRB_TASK_QUEUE_SUSPENDED]) +#define tick_ (mrb->task.tick) +#define wakeup_tick_ (mrb->task.wakeup_tick) +#define switching_ (mrb->task.switching) + +/* + * Task data type for GC + */ +static void +mrb_task_free(mrb_state *mrb, void *ptr) +{ + /* TODO: Free TCB and associated resources */ +} + +static const struct mrb_data_type mrb_task_type = { + "Task", mrb_task_free, +}; + +/* + * Queue operations + */ + +/* Insert task into appropriate queue based on priority */ +static void +q_insert_task(mrb_state *mrb, mrb_tcb *tcb) +{ + /* TODO: Implement priority-based insertion */ +} + +/* Delete task from its current queue */ +static void +q_delete_task(mrb_state *mrb, mrb_tcb *tcb) +{ + /* TODO: Implement queue deletion */ +} + +/* Find task in queues */ +static mrb_tcb* +q_find_task(mrb_state *mrb, mrb_tcb *tcb) +{ + /* TODO: Implement task search */ + return NULL; +} + +/* + * TCB lifecycle + */ + +/* Allocate new TCB */ +static mrb_tcb* +mrb_tcb_new(mrb_state *mrb) +{ + /* TODO: Allocate and initialize TCB */ + return NULL; +} + +/* Free TCB and context */ +static void +mrb_tcb_free(mrb_state *mrb, mrb_tcb *tcb) +{ + /* TODO: Free TCB and associated context */ +} + +/* Initialize task context (stack and callinfo) */ +static void +mrb_task_init_context(mrb_state *mrb, mrb_value task, struct RProc *proc) +{ + /* TODO: Allocate and initialize context similar to Fiber */ +} + +/* + * Scheduler core + */ + +/* Tick handler - called by timer interrupt */ +void +mrb_tick(mrb_state *mrb) +{ + /* TODO: Implement tick handler + * - Increment tick counter + * - Decrease current task timeslice + * - Wake up sleeping tasks + * - Set switching flag if needed + */ +} + +/* Main scheduler loop */ +mrb_value +mrb_tasks_run(mrb_state *mrb) +{ + /* TODO: Implement main scheduler loop + * - Get next ready task + * - Switch context + * - Execute via mrb_vm_exec() + * - Handle completion/switching + * - Run incremental GC + */ + return mrb_nil_value(); +} + +/* + * Sleep operations + */ + +static void +sleep_ms_impl(mrb_state *mrb, mrb_int ms) +{ + /* TODO: Implement sleep + * - Move current task to waiting queue + * - Set wakeup tick + * - Trigger context switch + */ +} + +static mrb_value +mrb_f_sleep(mrb_state *mrb, mrb_value self) +{ + /* TODO: Implement Kernel#sleep */ + return mrb_nil_value(); +} + +static mrb_value +mrb_f_sleep_ms(mrb_state *mrb, mrb_value self) +{ + /* TODO: Implement Kernel#sleep_ms */ + return mrb_nil_value(); +} + +/* + * Task class methods + */ + +static mrb_value +mrb_task_s_new(mrb_state *mrb, mrb_value self) +{ + /* TODO: Implement Task.new */ + return mrb_nil_value(); +} + +static mrb_value +mrb_task_s_current(mrb_state *mrb, mrb_value self) +{ + /* TODO: Implement Task.current */ + return mrb_nil_value(); +} + +static mrb_value +mrb_task_s_list(mrb_state *mrb, mrb_value self) +{ + /* TODO: Implement Task.list */ + return mrb_ary_new(mrb); +} + +static mrb_value +mrb_task_s_pass(mrb_state *mrb, mrb_value self) +{ + /* TODO: Implement Task.pass */ + return mrb_nil_value(); +} + +static mrb_value +mrb_task_s_stat(mrb_state *mrb, mrb_value self) +{ + /* TODO: Implement Task.stat */ + return mrb_nil_value(); +} + +static mrb_value +mrb_task_s_get(mrb_state *mrb, mrb_value self) +{ + /* TODO: Implement Task.get */ + return mrb_nil_value(); +} + +/* + * Task instance methods + */ + +static mrb_value +mrb_task_status(mrb_state *mrb, mrb_value self) +{ + /* TODO: Implement Task#status */ + return mrb_nil_value(); +} + +static mrb_value +mrb_task_name(mrb_state *mrb, mrb_value self) +{ + /* TODO: Implement Task#name */ + return mrb_nil_value(); +} + +static mrb_value +mrb_task_set_name(mrb_state *mrb, mrb_value self) +{ + /* TODO: Implement Task#name= */ + return mrb_nil_value(); +} + +static mrb_value +mrb_task_priority(mrb_state *mrb, mrb_value self) +{ + /* TODO: Implement Task#priority */ + return mrb_nil_value(); +} + +static mrb_value +mrb_task_set_priority(mrb_state *mrb, mrb_value self) +{ + /* TODO: Implement Task#priority= */ + return mrb_nil_value(); +} + +static mrb_value +mrb_task_suspend(mrb_state *mrb, mrb_value self) +{ + /* TODO: Implement Task#suspend */ + return self; +} + +static mrb_value +mrb_task_resume(mrb_state *mrb, mrb_value self) +{ + /* TODO: Implement Task#resume */ + return self; +} + +static mrb_value +mrb_task_terminate(mrb_state *mrb, mrb_value self) +{ + /* TODO: Implement Task#terminate */ + return self; +} + +static mrb_value +mrb_task_join(mrb_state *mrb, mrb_value self) +{ + /* TODO: Implement Task#join */ + return self; +} + +/* + * Initialization + */ + +void +mrb_mruby_task_gem_init(mrb_state *mrb) +{ + struct RClass *task_class; + + task_class = mrb_define_class(mrb, "Task", mrb->object_class); + MRB_SET_INSTANCE_TT(task_class, MRB_TT_DATA); + + /* Class methods */ + mrb_define_class_method(mrb, task_class, "new", mrb_task_s_new, MRB_ARGS_BLOCK()); + mrb_define_class_method(mrb, task_class, "current", mrb_task_s_current, MRB_ARGS_NONE()); + mrb_define_class_method(mrb, task_class, "list", mrb_task_s_list, MRB_ARGS_NONE()); + mrb_define_class_method(mrb, task_class, "pass", mrb_task_s_pass, MRB_ARGS_NONE()); + mrb_define_class_method(mrb, task_class, "stat", mrb_task_s_stat, MRB_ARGS_NONE()); + mrb_define_class_method(mrb, task_class, "get", mrb_task_s_get, MRB_ARGS_REQ(1)); + + /* Instance methods */ + mrb_define_method(mrb, task_class, "status", mrb_task_status, MRB_ARGS_NONE()); + mrb_define_method(mrb, task_class, "name", mrb_task_name, MRB_ARGS_NONE()); + mrb_define_method(mrb, task_class, "name=", mrb_task_set_name, MRB_ARGS_REQ(1)); + mrb_define_method(mrb, task_class, "priority", mrb_task_priority, MRB_ARGS_NONE()); + mrb_define_method(mrb, task_class, "priority=", mrb_task_set_priority, MRB_ARGS_REQ(1)); + mrb_define_method(mrb, task_class, "suspend", mrb_task_suspend, MRB_ARGS_NONE()); + mrb_define_method(mrb, task_class, "resume", mrb_task_resume, MRB_ARGS_NONE()); + mrb_define_method(mrb, task_class, "terminate", mrb_task_terminate, MRB_ARGS_NONE()); + mrb_define_method(mrb, task_class, "join", mrb_task_join, MRB_ARGS_NONE()); + + /* Kernel methods */ + mrb_define_method(mrb, mrb->kernel_module, "sleep", mrb_f_sleep, MRB_ARGS_OPT(1)); + mrb_define_method(mrb, mrb->kernel_module, "sleep_ms", mrb_f_sleep_ms, MRB_ARGS_REQ(1)); +} + +void +mrb_mruby_task_gem_final(mrb_state *mrb) +{ + /* Cleanup if needed */ +} + +#else + +/* Task scheduler not enabled - provide stub */ +void +mrb_mruby_task_gem_init(mrb_state *mrb) +{ +} + +void +mrb_mruby_task_gem_final(mrb_state *mrb) +{ +} + +#endif /* MRB_USE_TASK_SCHEDULER */