Files
HASUMI Hitoshi 0a21eef938 Fix mruby-task for PicoRuby Integration
With this PR, I can remove the original task.c in picoruby/picoruby and future development will be much easier.

## Add

### General

- C API functions exported with MRB_API for external integration:
  - mrb_execute_proc_synchronously() for synchronous proc execution
  - Task control APIs (mrb_create_task, mrb_suspend_task, mrb_resume_task, mrb_terminate_task, mrb_stop_task, mrb_task_value, mrb_task_status)
  - Task context management APIs for picoruby-sandbox (mrb_task_init_context, mrb_task_reset_context, mrb_task_proc_set)
  - Task.tick class method to get current tick count
- Comprehensive C API documentation with WASM integration examples

### For PicoRuby.wasm

- WASM/Emscripten support: Disable SIGALRM timer when __EMSCRIPTEN__ is defined, as JavaScript handles tick calls via setInterval
- Scheduler lock mechanism to prevent asynchronous task operations during synchronous execution (scheduler_lock counter in mrb_task_state)
- mrb_task_run_once() for single-step execution (event loop integration)

## Fix

### task.c
- Replace MRB_FIBER_TERMINATED with MRB_TASK_STOPPED just for clarity
- Allow suspending DORMANT and WAITING tasks in mrb_task_suspend (See comment in the source)
- Task context initialization by removing dummy callinfo push/pop

*NOTE*

With the dummy callinfo code that I deleted, IRB in PicoRuby ended SEGV.
If that code is mandatory, we need to discuss how to solve my problem.

### vm.c
- Handle MRB_TASK_CREATED status in VM's NORMAL_RETURN phase to properly stop tasks

----

These changes are necessary to make PicoRuby work.
Nevertheless, even with this patch, MicroRuby for Raspberry Pi Pico 2 is still unstable.
I would like to merge this PR anyway to make development easier by involving the PicoRuby community.
2026-01-11 17:07:04 +09:00

144 lines
4.6 KiB
C

/*
** task.h - Task scheduler
**
** See Copyright Notice in mruby.h
*/
#ifndef MRUBY_TASK_H
#define MRUBY_TASK_H
#include <mruby.h>
/*
* Task status values (bit-mapped)
*/
enum {
MRB_TASK_STATUS_DORMANT = 0x00, /* Not started or finished */
MRB_TASK_STATUS_READY = 0x02, /* Ready to run */
MRB_TASK_STATUS_RUNNING = 0x03, /* Currently executing */
MRB_TASK_STATUS_WAITING = 0x04, /* Waiting for condition */
MRB_TASK_STATUS_SUSPENDED = 0x08, /* Manually suspended */
};
/*
* Task wait reason
*/
enum {
MRB_TASK_REASON_NONE = 0x00, /* No specific reason */
MRB_TASK_REASON_SLEEP = 0x01, /* Sleeping for time */
MRB_TASK_REASON_MUTEX = 0x02, /* Waiting for mutex (reserved) */
MRB_TASK_REASON_JOIN = 0x04, /* Waiting for another task */
};
/*
* Task structure - represents a single task in the scheduler
*
* Memory-optimized layout:
* - Removed priority_preemption (always equals priority): 1 byte
* - Removed started flag (inferred from c.status): 1 byte
* - Unified wakeup_tick/join/mutex into single union: 4 bytes
* - Removed redundant proc field (stored in c.ci->proc): 8 bytes
* - Unified timeslice/result into state union: ~4 bytes
* Total savings: ~18 bytes per task (14% reduction)
*/
typedef struct mrb_task {
struct mrb_task *next; /* Linked list pointer */
uint8_t priority; /* Priority (0-255, 0=highest) */
uint8_t status; /* Current status (TASKSTATUS enum) */
uint8_t reason; /* Wait reason (TASKREASON enum) */
mrb_value name; /* Optional task name */
/* Wait-specific data - mutually exclusive based on reason field */
union {
uint32_t wakeup_tick; /* Tick count to wake up (REASON_SLEEP) */
const struct mrb_task *join; /* Task being waited on (REASON_JOIN) */
void *mutex; /* Mutex pointer (REASON_MUTEX, reserved) */
} wait;
mrb_value self; /* Ruby Task object reference */
/* State-specific data - mutually exclusive based on status */
union {
volatile uint8_t timeslice; /* Remaining ticks (RUNNING only) */
mrb_value result; /* Task return value (DORMANT only) */
} state;
struct mrb_context c; /* Execution context (stack, callinfo, etc) */
} mrb_task;
/*
* Task queue configuration
* (mrb_task_state is defined in mruby.h)
*/
#define MRB_NUM_TASK_QUEUE 4
/* 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 4 /* 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_hal_final(mrb_state *mrb);
void mrb_task_enable_irq(void);
void mrb_task_disable_irq(void);
void mrb_task_hal_idle_cpu(mrb_state *mrb);
/*
* GC integration
*/
void mrb_task_mark_all(mrb_state *mrb);
/*
* Core task scheduler API
*/
MRB_API void mrb_tick(mrb_state *mrb);
MRB_API mrb_value mrb_task_run(mrb_state *mrb);
MRB_API mrb_value mrb_task_run_once(mrb_state *mrb);
/*
* Task creation API
*/
MRB_API mrb_value mrb_create_task(mrb_state *mrb, struct RProc *proc, mrb_value name, mrb_value priority, mrb_value top_self);
/*
* Synchronous execution API (for picoruby-wasm)
*/
MRB_API mrb_value mrb_execute_proc_synchronously(mrb_state *mrb, mrb_value proc, mrb_int argc, const mrb_value *argv);
/*
* Task control API
* Note: mrb_task_run is the main scheduler loop (for picoruby-sandbox and picoruby-wasm)
*/
MRB_API void mrb_suspend_task(mrb_state *mrb, mrb_value task);
MRB_API void mrb_resume_task(mrb_state *mrb, mrb_value task);
MRB_API void mrb_terminate_task(mrb_state *mrb, mrb_value task);
MRB_API mrb_bool mrb_stop_task(mrb_state *mrb, mrb_value task);
MRB_API mrb_value mrb_task_value(mrb_state *mrb, mrb_value task);
MRB_API mrb_value mrb_task_status(mrb_state *mrb, mrb_value self);
/*
* Task context management API (for picoruby-sandbox)
*/
MRB_API void mrb_task_init_context(mrb_state *mrb, mrb_value task, struct RProc *proc);
MRB_API void mrb_task_reset_context(mrb_state *mrb, mrb_value task);
MRB_API void mrb_task_proc_set(mrb_state *mrb, mrb_value task, struct RProc *proc);
#endif /* MRUBY_TASK_H */