mirror of
https://github.com/mruby/mruby
synced 2026-06-08 16:11:16 +00:00
mruby-task: introduce HAL (hardware abstraction layer) for platform support
separates platform-specific timer and interrupt code into hal-posix-task and hal-win-task gems. mruby-task now uses HAL interface defined in task_hal.h, making it easier to port to new platforms. hal-posix-task: uses sigalrm/setitimer for timer, sigprocmask for irq protection, and SA_RESTART flag to prevent EINTR on system calls. hal-win-task: uses multimedia timer API and critical_section for irq protection. both HALs support multiple mrb_state instances with single shared timer. auto-detection loads appropriate HAL based on platform. Co-authored-by: Claude <noreply@anthropic.com>
This commit is contained in:
@@ -0,0 +1,102 @@
|
||||
# hal-posix-task
|
||||
|
||||
POSIX Hardware Abstraction Layer (HAL) implementation for mruby-task.
|
||||
|
||||
## Description
|
||||
|
||||
Provides timer and interrupt support for the mruby-task cooperative scheduler on POSIX-compliant platforms. Uses `SIGALRM` and `setitimer()` for periodic timer ticks, and `sigprocmask()` for interrupt protection.
|
||||
|
||||
## Supported Platforms
|
||||
|
||||
- Linux
|
||||
- macOS
|
||||
- BSD (FreeBSD, OpenBSD, NetBSD)
|
||||
- Other POSIX-compliant Unix systems
|
||||
|
||||
## Requirements
|
||||
|
||||
- POSIX-compliant operating system
|
||||
- Signal support (`SIGALRM`, `sigaction`, `sigprocmask`)
|
||||
- Timer support (`setitimer`, `ITIMER_REAL`)
|
||||
|
||||
## Usage
|
||||
|
||||
### Explicit HAL Selection (Recommended)
|
||||
|
||||
```ruby
|
||||
MRuby::Build.new do |conf|
|
||||
# ... other configuration ...
|
||||
|
||||
# Specify POSIX HAL - automatically brings in mruby-task
|
||||
conf.gem core: 'hal-posix-task'
|
||||
end
|
||||
```
|
||||
|
||||
### Auto-detection (Development)
|
||||
|
||||
```ruby
|
||||
MRuby::Build.new do |conf|
|
||||
# ... other configuration ...
|
||||
|
||||
# Auto-detects and selects hal-posix-task on POSIX platforms
|
||||
conf.gem core: 'mruby-task'
|
||||
end
|
||||
```
|
||||
|
||||
## Implementation Details
|
||||
|
||||
### Timer Mechanism
|
||||
|
||||
- Uses `setitimer(ITIMER_REAL, ...)` to generate periodic `SIGALRM` signals
|
||||
- Timer interval configured by `MRB_TICK_UNIT` (default: 4ms)
|
||||
- Signal handler calls `mrb_tick()` for all registered VM instances
|
||||
|
||||
### Interrupt Protection
|
||||
|
||||
- Critical sections protected using `sigprocmask()` to block `SIGALRM`
|
||||
- Prevents race conditions during task queue modifications
|
||||
- Supports nested critical sections through signal masking
|
||||
|
||||
### Multi-VM Support
|
||||
|
||||
- Supports up to `MRB_TASK_MAX_VMS` concurrent mruby VM instances (default: 8)
|
||||
- Single shared timer ticks all registered VMs
|
||||
- Per-VM task counters optimize timer usage (timer disabled when idle)
|
||||
|
||||
### Timer Optimization
|
||||
|
||||
The implementation dynamically enables/disables the timer based on task state:
|
||||
|
||||
- **Timer enabled** when: Multiple ready tasks OR any waiting tasks exist
|
||||
- **Timer disabled** when: Single task or all tasks dormant/suspended
|
||||
- Reduces CPU usage and power consumption when scheduler is idle
|
||||
|
||||
## Configuration
|
||||
|
||||
Override these macros in your build config if needed:
|
||||
|
||||
```ruby
|
||||
conf.gem core: 'hal-posix-task' do |spec|
|
||||
# Custom tick interval (10ms instead of default 4ms)
|
||||
spec.build.defines << 'MRB_TICK_UNIT=10'
|
||||
|
||||
# Custom timeslice (5 ticks instead of default 3)
|
||||
spec.build.defines << 'MRB_TIMESLICE_TICK_COUNT=5'
|
||||
|
||||
# More concurrent VMs (16 instead of default 8)
|
||||
spec.build.defines << 'MRB_TASK_MAX_VMS=16'
|
||||
end
|
||||
```
|
||||
|
||||
## Known Limitations
|
||||
|
||||
- `SIGALRM` conflicts with other code using the same signal
|
||||
- Timer resolution limited by platform (typically 1-10ms)
|
||||
- Signal delivery may be delayed under heavy system load
|
||||
- Not suitable for hard real-time requirements
|
||||
|
||||
## See Also
|
||||
|
||||
- `mruby-task` - Core task scheduler
|
||||
- `hal-win-task` - Windows HAL implementation
|
||||
- Task scheduler documentation: `mrbgems/mruby-task/README.md`
|
||||
@@ -0,0 +1,8 @@
|
||||
MRuby::Gem::Specification.new('hal-posix-task') do |spec|
|
||||
spec.license = 'MIT'
|
||||
spec.authors = 'mruby developers'
|
||||
spec.summary = 'POSIX HAL for mruby-task (Linux, macOS, BSD, Unix)'
|
||||
|
||||
# HAL gem depends on feature gem - brings in mruby-task automatically
|
||||
spec.add_dependency 'mruby-task', core: 'mruby-task'
|
||||
end
|
||||
@@ -0,0 +1,174 @@
|
||||
/*
|
||||
** task_hal.c - POSIX HAL implementation for mruby-task
|
||||
**
|
||||
** See Copyright Notice in mruby.h
|
||||
**
|
||||
** POSIX implementation using SIGALRM and setitimer() for timer,
|
||||
** and sigprocmask() for interrupt protection.
|
||||
**
|
||||
** Supported platforms: Linux, macOS, BSD, Unix
|
||||
*/
|
||||
|
||||
#include <mruby.h>
|
||||
#include "task_hal.h"
|
||||
#include <signal.h>
|
||||
#include <sys/time.h>
|
||||
#include <unistd.h>
|
||||
#include <stdint.h>
|
||||
|
||||
/* Multi-VM support */
|
||||
static mrb_state *vm_list[MRB_TASK_MAX_VMS];
|
||||
static volatile sig_atomic_t vm_count = 0;
|
||||
static sigset_t alarm_mask;
|
||||
|
||||
/* SIGALRM signal handler - ticks all registered VMs */
|
||||
static void
|
||||
sigalrm_handler(int sig)
|
||||
{
|
||||
int i;
|
||||
(void)sig;
|
||||
/* Tick all registered VMs */
|
||||
for (i = 0; i < vm_count; i++) {
|
||||
if (vm_list[i]) {
|
||||
mrb_tick(vm_list[i]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* HAL Interface Implementation
|
||||
*/
|
||||
|
||||
void
|
||||
mrb_task_hal_init(mrb_state *mrb)
|
||||
{
|
||||
struct sigaction sa;
|
||||
struct itimerval timer;
|
||||
int i;
|
||||
int vm_index = -1;
|
||||
|
||||
/* Initialize task state */
|
||||
for (i = 0; i < 4; i++) {
|
||||
mrb->task.queues[i] = NULL;
|
||||
}
|
||||
mrb->task.tick = 0;
|
||||
mrb->task.wakeup_tick = UINT32_MAX;
|
||||
mrb->task.switching = FALSE;
|
||||
|
||||
/* Block SIGALRM during registration to avoid race */
|
||||
sigemptyset(&alarm_mask);
|
||||
sigaddset(&alarm_mask, SIGALRM);
|
||||
sigprocmask(SIG_BLOCK, &alarm_mask, NULL);
|
||||
|
||||
/* Check if this VM is already registered */
|
||||
for (i = 0; i < vm_count; i++) {
|
||||
if (vm_list[i] == mrb) {
|
||||
vm_index = i;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
/* Register new VM if not already present */
|
||||
if (vm_index < 0) {
|
||||
if (vm_count >= MRB_TASK_MAX_VMS) {
|
||||
sigprocmask(SIG_UNBLOCK, &alarm_mask, NULL);
|
||||
mrb_raisef(mrb, E_RUNTIME_ERROR,
|
||||
"too many mrb_states with task scheduler (max: %d)",
|
||||
MRB_TASK_MAX_VMS);
|
||||
}
|
||||
vm_list[vm_count] = mrb;
|
||||
vm_count++;
|
||||
}
|
||||
|
||||
/* Set up signal handler and timer only for first VM */
|
||||
if (vm_count == 1) {
|
||||
/* Set up signal handler - SA_RESTART to avoid breaking IO operations */
|
||||
sa.sa_handler = sigalrm_handler;
|
||||
sa.sa_flags = SA_RESTART;
|
||||
sigemptyset(&sa.sa_mask);
|
||||
sigaction(SIGALRM, &sa, NULL);
|
||||
|
||||
/* Start timer */
|
||||
timer.it_value.tv_sec = 0;
|
||||
timer.it_value.tv_usec = MRB_TICK_UNIT * 1000;
|
||||
timer.it_interval.tv_sec = 0;
|
||||
timer.it_interval.tv_usec = MRB_TICK_UNIT * 1000;
|
||||
setitimer(ITIMER_REAL, &timer, NULL);
|
||||
}
|
||||
|
||||
/* Unblock SIGALRM */
|
||||
sigprocmask(SIG_UNBLOCK, &alarm_mask, NULL);
|
||||
}
|
||||
|
||||
void
|
||||
mrb_task_enable_irq(void)
|
||||
{
|
||||
sigprocmask(SIG_UNBLOCK, &alarm_mask, NULL);
|
||||
}
|
||||
|
||||
void
|
||||
mrb_task_disable_irq(void)
|
||||
{
|
||||
sigprocmask(SIG_BLOCK, &alarm_mask, NULL);
|
||||
}
|
||||
|
||||
void
|
||||
mrb_task_hal_idle_cpu(mrb_state *mrb)
|
||||
{
|
||||
(void)mrb;
|
||||
/* On POSIX, just pause briefly */
|
||||
usleep(MRB_TICK_UNIT * 1000);
|
||||
}
|
||||
|
||||
void
|
||||
mrb_task_hal_final(mrb_state *mrb)
|
||||
{
|
||||
struct itimerval timer;
|
||||
int i, j;
|
||||
|
||||
/* Block SIGALRM during unregistration */
|
||||
sigprocmask(SIG_BLOCK, &alarm_mask, NULL);
|
||||
|
||||
/* Find and remove this VM from the list */
|
||||
for (i = 0; i < vm_count; i++) {
|
||||
if (vm_list[i] == mrb) {
|
||||
/* Shift remaining VMs down */
|
||||
for (j = i; j < vm_count - 1; j++) {
|
||||
vm_list[j] = vm_list[j + 1];
|
||||
}
|
||||
vm_list[vm_count - 1] = NULL;
|
||||
vm_count--;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
/* Stop timer if last VM */
|
||||
if (vm_count == 0) {
|
||||
timer.it_value.tv_sec = 0;
|
||||
timer.it_value.tv_usec = 0;
|
||||
timer.it_interval.tv_sec = 0;
|
||||
timer.it_interval.tv_usec = 0;
|
||||
setitimer(ITIMER_REAL, &timer, NULL);
|
||||
}
|
||||
|
||||
/* Unblock SIGALRM */
|
||||
sigprocmask(SIG_UNBLOCK, &alarm_mask, NULL);
|
||||
}
|
||||
|
||||
/*
|
||||
* Gem initialization (empty - HAL functions called by mruby-task)
|
||||
*/
|
||||
|
||||
void
|
||||
mrb_hal_posix_task_gem_init(mrb_state *mrb)
|
||||
{
|
||||
(void)mrb;
|
||||
/* HAL interface functions are called by mruby-task gem */
|
||||
}
|
||||
|
||||
void
|
||||
mrb_hal_posix_task_gem_final(mrb_state *mrb)
|
||||
{
|
||||
(void)mrb;
|
||||
/* Cleanup handled by mrb_task_hal_final called from mruby-task */
|
||||
}
|
||||
@@ -0,0 +1,109 @@
|
||||
# hal-win-task
|
||||
|
||||
Windows Hardware Abstraction Layer (HAL) implementation for mruby-task.
|
||||
|
||||
## Description
|
||||
|
||||
Provides timer and interrupt support for the mruby-task cooperative scheduler on Windows platforms. Uses multimedia timer (`timeSetEvent`/`timeKillEvent`) for periodic timer ticks, and `CRITICAL_SECTION` for interrupt protection.
|
||||
|
||||
## Supported Platforms
|
||||
|
||||
- Windows 7 and later
|
||||
- Windows Server 2008 R2 and later
|
||||
- All versions with multimedia timer support
|
||||
|
||||
## Requirements
|
||||
|
||||
- Windows operating system
|
||||
- Multimedia timer API (`winmm.lib`)
|
||||
- Visual C++, MinGW, or compatible compiler
|
||||
|
||||
## Usage
|
||||
|
||||
### Explicit HAL Selection (Recommended)
|
||||
|
||||
```ruby
|
||||
MRuby::Build.new do |conf|
|
||||
# ... other configuration ...
|
||||
|
||||
# Specify Windows HAL - automatically brings in mruby-task
|
||||
conf.gem core: 'hal-win-task'
|
||||
end
|
||||
```
|
||||
|
||||
### Auto-detection (Development)
|
||||
|
||||
```ruby
|
||||
MRuby::Build.new do |conf|
|
||||
# ... other configuration ...
|
||||
|
||||
# Auto-detects and selects hal-win-task on Windows platforms
|
||||
conf.gem core: 'mruby-task'
|
||||
end
|
||||
```
|
||||
|
||||
## Implementation Details
|
||||
|
||||
### Timer Mechanism
|
||||
|
||||
- Uses Windows multimedia timer (`timeSetEvent`) for periodic callbacks
|
||||
- Timer interval configured by `MRB_TICK_UNIT` (default: 4ms)
|
||||
- `TIME_KILL_SYNCHRONOUS` flag ensures clean timer shutdown
|
||||
- Requests 1ms timer resolution via `timeBeginPeriod(1)`
|
||||
|
||||
### Interrupt Protection
|
||||
|
||||
- Critical sections protected using `CRITICAL_SECTION` objects
|
||||
- Prevents race conditions during task queue modifications
|
||||
- `EnterCriticalSection`/`LeaveCriticalSection` used for mutual exclusion
|
||||
- Supports nested critical sections (automatic lock counting)
|
||||
|
||||
### Multi-VM Support
|
||||
|
||||
- Supports up to `MRB_TASK_MAX_VMS` concurrent mruby VM instances (default: 8)
|
||||
- Single shared timer ticks all registered VMs
|
||||
- Per-VM task counters optimize timer usage (timer disabled when idle)
|
||||
- Interlocked operations (`InterlockedIncrement`/`InterlockedDecrement`) for thread safety
|
||||
|
||||
### Timer Optimization
|
||||
|
||||
The implementation dynamically enables/disables the timer based on task state:
|
||||
|
||||
- **Timer enabled** when: Multiple ready tasks OR any waiting tasks exist
|
||||
- **Timer disabled** when: Single task or all tasks dormant/suspended
|
||||
- Reduces CPU usage and power consumption when scheduler is idle
|
||||
|
||||
## Configuration
|
||||
|
||||
Override these macros in your build config if needed:
|
||||
|
||||
```ruby
|
||||
conf.gem core: 'hal-win-task' do |spec|
|
||||
# Custom tick interval (10ms instead of default 4ms)
|
||||
spec.build.defines << 'MRB_TICK_UNIT=10'
|
||||
|
||||
# Custom timeslice (5 ticks instead of default 3)
|
||||
spec.build.defines << 'MRB_TIMESLICE_TICK_COUNT=5'
|
||||
|
||||
# More concurrent VMs (16 instead of default 8)
|
||||
spec.build.defines << 'MRB_TASK_MAX_VMS=16'
|
||||
end
|
||||
```
|
||||
|
||||
## Known Limitations
|
||||
|
||||
- Timer resolution typically limited to 1-2ms even with `timeBeginPeriod(1)`
|
||||
- Multimedia timers consume system resources (kernel timer objects)
|
||||
- Timer callbacks execute in separate thread context (handled internally)
|
||||
- Not suitable for hard real-time requirements
|
||||
- May interfere with other multimedia applications requesting different timer resolutions
|
||||
|
||||
## See Also
|
||||
|
||||
- `mruby-task` - Core task scheduler
|
||||
- `hal-posix-task` - POSIX/Unix HAL implementation
|
||||
- Task scheduler documentation: `mrbgems/mruby-task/README.md`
|
||||
|
||||
## Build Notes
|
||||
|
||||
The `winmm` library is automatically linked by the gem specification. No additional linker configuration is needed.
|
||||
@@ -0,0 +1,11 @@
|
||||
MRuby::Gem::Specification.new('hal-win-task') do |spec|
|
||||
spec.license = 'MIT'
|
||||
spec.authors = 'mruby developers'
|
||||
spec.summary = 'Windows HAL for mruby-task'
|
||||
|
||||
# HAL gem depends on feature gem - brings in mruby-task automatically
|
||||
spec.add_dependency 'mruby-task', core: 'mruby-task'
|
||||
|
||||
# Windows multimedia timer library
|
||||
spec.linker.libraries << 'winmm'
|
||||
end
|
||||
@@ -0,0 +1,176 @@
|
||||
/*
|
||||
** task_hal.c - Windows HAL implementation for mruby-task
|
||||
**
|
||||
** See Copyright Notice in mruby.h
|
||||
**
|
||||
** Windows implementation using multimedia timer (timeSetEvent/timeKillEvent)
|
||||
** for periodic timer, and CRITICAL_SECTION for interrupt protection.
|
||||
**
|
||||
** Supported platforms: Windows (all versions with multimedia timer support)
|
||||
*/
|
||||
|
||||
#include <mruby.h>
|
||||
#include "task_hal.h"
|
||||
#include <windows.h>
|
||||
#include <timeapi.h>
|
||||
#include <stdint.h>
|
||||
|
||||
/* Multi-VM support */
|
||||
static mrb_state *vm_list[MRB_TASK_MAX_VMS];
|
||||
static volatile LONG vm_count = 0;
|
||||
static CRITICAL_SECTION irq_lock;
|
||||
static MMRESULT timer_id = 0;
|
||||
|
||||
/* Multimedia timer callback - called periodically by Windows */
|
||||
static void CALLBACK
|
||||
timer_callback(UINT uID, UINT uMsg, DWORD_PTR dwUser, DWORD_PTR dw1, DWORD_PTR dw2)
|
||||
{
|
||||
int i;
|
||||
(void)uID; (void)uMsg; (void)dwUser; (void)dw1; (void)dw2;
|
||||
|
||||
/* Tick all registered VMs */
|
||||
EnterCriticalSection(&irq_lock);
|
||||
for (i = 0; i < vm_count; i++) {
|
||||
if (vm_list[i]) {
|
||||
mrb_tick(vm_list[i]);
|
||||
}
|
||||
}
|
||||
LeaveCriticalSection(&irq_lock);
|
||||
}
|
||||
|
||||
/*
|
||||
* HAL Interface Implementation
|
||||
*/
|
||||
|
||||
void
|
||||
mrb_task_hal_init(mrb_state *mrb)
|
||||
{
|
||||
int i;
|
||||
LONG idx;
|
||||
|
||||
/* Initialize task state */
|
||||
for (i = 0; i < 4; i++) {
|
||||
mrb->task.queues[i] = NULL;
|
||||
}
|
||||
mrb->task.tick = 0;
|
||||
mrb->task.wakeup_tick = UINT32_MAX;
|
||||
mrb->task.switching = FALSE;
|
||||
|
||||
/* Initialize critical section on first VM */
|
||||
if (vm_count == 0) {
|
||||
InitializeCriticalSection(&irq_lock);
|
||||
}
|
||||
|
||||
EnterCriticalSection(&irq_lock);
|
||||
|
||||
/* Check if this VM is already registered */
|
||||
idx = -1;
|
||||
for (i = 0; i < vm_count; i++) {
|
||||
if (vm_list[i] == mrb) {
|
||||
idx = i;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
/* Register new VM if not already present */
|
||||
if (idx < 0) {
|
||||
if (vm_count >= MRB_TASK_MAX_VMS) {
|
||||
LeaveCriticalSection(&irq_lock);
|
||||
mrb_raisef(mrb, E_RUNTIME_ERROR,
|
||||
"too many mrb_states with task scheduler (max: %d)",
|
||||
MRB_TASK_MAX_VMS);
|
||||
}
|
||||
vm_list[vm_count] = mrb;
|
||||
InterlockedIncrement(&vm_count);
|
||||
}
|
||||
|
||||
/* Start timer for first VM */
|
||||
if (vm_count == 1) {
|
||||
/* Request 1ms timer resolution */
|
||||
timeBeginPeriod(1);
|
||||
|
||||
/* Create periodic timer with MRB_TICK_UNIT interval */
|
||||
timer_id = timeSetEvent(
|
||||
MRB_TICK_UNIT, /* interval in milliseconds */
|
||||
1, /* resolution in milliseconds */
|
||||
timer_callback, /* callback function */
|
||||
0, /* user data */
|
||||
TIME_PERIODIC | TIME_KILL_SYNCHRONOUS
|
||||
);
|
||||
}
|
||||
|
||||
LeaveCriticalSection(&irq_lock);
|
||||
}
|
||||
|
||||
void
|
||||
mrb_task_enable_irq(void)
|
||||
{
|
||||
LeaveCriticalSection(&irq_lock);
|
||||
}
|
||||
|
||||
void
|
||||
mrb_task_disable_irq(void)
|
||||
{
|
||||
EnterCriticalSection(&irq_lock);
|
||||
}
|
||||
|
||||
void
|
||||
mrb_task_hal_idle_cpu(mrb_state *mrb)
|
||||
{
|
||||
(void)mrb;
|
||||
/* On Windows, just sleep briefly */
|
||||
Sleep(MRB_TICK_UNIT);
|
||||
}
|
||||
|
||||
void
|
||||
mrb_task_hal_final(mrb_state *mrb)
|
||||
{
|
||||
int i, j;
|
||||
|
||||
EnterCriticalSection(&irq_lock);
|
||||
|
||||
/* Find and remove this VM from the list */
|
||||
for (i = 0; i < vm_count; i++) {
|
||||
if (vm_list[i] == mrb) {
|
||||
/* Shift remaining VMs down */
|
||||
for (j = i; j < vm_count - 1; j++) {
|
||||
vm_list[j] = vm_list[j + 1];
|
||||
}
|
||||
vm_list[vm_count - 1] = NULL;
|
||||
InterlockedDecrement(&vm_count);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
/* Stop timer if last VM */
|
||||
if (vm_count == 0) {
|
||||
if (timer_id != 0) {
|
||||
timeKillEvent(timer_id);
|
||||
timeEndPeriod(1);
|
||||
timer_id = 0;
|
||||
}
|
||||
LeaveCriticalSection(&irq_lock);
|
||||
DeleteCriticalSection(&irq_lock);
|
||||
}
|
||||
else {
|
||||
LeaveCriticalSection(&irq_lock);
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Gem initialization (empty - HAL functions called by mruby-task)
|
||||
*/
|
||||
|
||||
void
|
||||
mrb_hal_win_task_gem_init(mrb_state *mrb)
|
||||
{
|
||||
(void)mrb;
|
||||
/* HAL interface functions are called by mruby-task gem */
|
||||
}
|
||||
|
||||
void
|
||||
mrb_hal_win_task_gem_final(mrb_state *mrb)
|
||||
{
|
||||
(void)mrb;
|
||||
/* Cleanup handled by mrb_task_hal_final called from mruby-task */
|
||||
}
|
||||
+159
-21
@@ -251,42 +251,180 @@ These grow automatically as needed, similar to Fiber.
|
||||
|
||||
### HAL (Hardware Abstraction Layer)
|
||||
|
||||
The task scheduler requires platform-specific timer and interrupt support via four HAL functions:
|
||||
The task scheduler uses a Hardware Abstraction Layer (HAL) to support different platforms. Platform-specific timer and interrupt handling is provided by separate HAL gems.
|
||||
|
||||
```c
|
||||
void mrb_task_hal_init(mrb_state *mrb); // Initialize timer
|
||||
void mrb_task_enable_irq(void); // Enable timer interrupts
|
||||
void mrb_task_disable_irq(void); // Disable timer interrupts
|
||||
void mrb_task_hal_idle_cpu(mrb_state *mrb); // Idle when no tasks ready
|
||||
#### Built-in HAL Gems
|
||||
|
||||
**hal-posix-task** - For POSIX systems (Linux, macOS, BSD, Unix)
|
||||
|
||||
- Uses `SIGALRM` and `setitimer()` for timer
|
||||
- Uses `sigprocmask()` for interrupt protection
|
||||
- Uses `SA_RESTART` to prevent `EINTR` on system calls
|
||||
- Supports multiple VMs per process
|
||||
|
||||
**hal-win-task** - For Windows
|
||||
|
||||
- Uses multimedia timer API (`timeSetEvent`/`timeKillEvent`)
|
||||
- Uses `CRITICAL_SECTION` for interrupt protection
|
||||
- Supports multiple VMs per process
|
||||
|
||||
#### HAL Selection
|
||||
|
||||
The task scheduler will automatically select an appropriate HAL gem based on your platform. For explicit control, you can specify the HAL gem in your build configuration:
|
||||
|
||||
```ruby
|
||||
MRuby::Build.new do |conf|
|
||||
# Option 1: Explicit HAL selection (recommended)
|
||||
conf.gem core: 'hal-posix-task' # For Linux/macOS/BSD
|
||||
# or
|
||||
conf.gem core: 'hal-win-task' # For Windows
|
||||
|
||||
# mruby-task automatically loads if HAL is loaded
|
||||
# But you can also specify it explicitly:
|
||||
conf.gem core: 'mruby-task'
|
||||
end
|
||||
```
|
||||
|
||||
### POSIX Platform
|
||||
**Auto-detection behavior:**
|
||||
|
||||
On POSIX systems (Linux, macOS, BSD), the implementation uses:
|
||||
- If you include `mruby-task` but no HAL gem, it will automatically load the appropriate HAL
|
||||
- On Linux/macOS/BSD: loads `hal-posix-task`
|
||||
- On Windows: loads `hal-win-task`
|
||||
- On unknown platforms: fails with helpful error message
|
||||
|
||||
- `SIGALRM` signal for timer interrupts
|
||||
- `setitimer()` for periodic tick generation
|
||||
- `sigprocmask()` for interrupt enable/disable
|
||||
- `SA_RESTART` flag to prevent `EINTR` on system calls
|
||||
**Multi-VM support:**
|
||||
|
||||
### Other Platforms
|
||||
- Both HAL implementations support multiple `mrb_state` instances
|
||||
- A single system timer ticks all registered VMs
|
||||
- Maximum VMs: configurable via `MRB_TASK_MAX_VMS` (default: 8)
|
||||
|
||||
For embedded or non-POSIX platforms, you must implement the HAL functions. See
|
||||
the POSIX implementation in `src/task.c` as a reference.
|
||||
#### Custom HAL Implementation
|
||||
|
||||
Example for a hypothetical embedded platform:
|
||||
For embedded systems or unsupported platforms, you can create a custom HAL gem. The HAL must provide five functions defined in `mruby-task/include/task_hal.h`:
|
||||
|
||||
```c
|
||||
void mrb_task_hal_init(mrb_state *mrb) {
|
||||
// Setup hardware timer to call mrb_tick() every MRB_TICK_UNIT ms
|
||||
hardware_timer_init(MRB_TICK_UNIT, timer_irq_handler);
|
||||
/**
|
||||
* Initialize timer and register VM
|
||||
* Called during gem initialization
|
||||
* Must set up periodic timer to call mrb_tick(mrb) every MRB_TICK_UNIT ms
|
||||
*/
|
||||
void mrb_task_hal_init(mrb_state *mrb);
|
||||
|
||||
/**
|
||||
* Cleanup timer and unregister VM
|
||||
* Called during gem finalization
|
||||
*/
|
||||
void mrb_task_hal_final(mrb_state *mrb);
|
||||
|
||||
/**
|
||||
* Enable timer interrupts (exit critical section)
|
||||
* Must be reentrant for nested calls
|
||||
*/
|
||||
void mrb_task_enable_irq(void);
|
||||
|
||||
/**
|
||||
* Disable timer interrupts (enter critical section)
|
||||
* Must be reentrant for nested calls
|
||||
*/
|
||||
void mrb_task_disable_irq(void);
|
||||
|
||||
/**
|
||||
* Put CPU in low-power/idle mode
|
||||
* Called when no tasks are ready but some are waiting
|
||||
* Should sleep ~MRB_TICK_UNIT milliseconds
|
||||
*/
|
||||
void mrb_task_hal_idle_cpu(mrb_state *mrb);
|
||||
```
|
||||
|
||||
**Example custom HAL gem structure:**
|
||||
|
||||
```
|
||||
mrbgems/hal-myplatform-task/
|
||||
├── mrbgem.rake # Gem specification
|
||||
├── include/
|
||||
│ └── task_hal.h # Symlink to mruby-task/include/task_hal.h
|
||||
└── src/
|
||||
└── task_hal.c # Platform implementation
|
||||
```
|
||||
|
||||
**mrbgem.rake:**
|
||||
|
||||
```ruby
|
||||
MRuby::Gem::Specification.new('hal-myplatform-task') do |spec|
|
||||
spec.license = 'MIT'
|
||||
spec.authors = 'Your Name'
|
||||
spec.summary = 'My Platform HAL for mruby-task'
|
||||
|
||||
# HAL gem depends on feature gem (important for build order)
|
||||
spec.add_dependency 'mruby-task', core: 'mruby-task'
|
||||
|
||||
# Add any platform-specific libraries or flags
|
||||
# spec.linker.libraries << 'myplatform_timer'
|
||||
end
|
||||
```
|
||||
|
||||
**task_hal.c example for embedded system:**
|
||||
|
||||
```c
|
||||
#include <mruby.h>
|
||||
#include "task_hal.h"
|
||||
#include "myplatform_hardware.h"
|
||||
|
||||
static mrb_state *registered_vm = NULL;
|
||||
|
||||
void mrb_task_hal_init(mrb_state *mrb)
|
||||
{
|
||||
registered_vm = mrb;
|
||||
|
||||
// Setup hardware timer to fire every MRB_TICK_UNIT milliseconds
|
||||
hardware_timer_init(MRB_TICK_UNIT, timer_isr);
|
||||
hardware_timer_start();
|
||||
}
|
||||
|
||||
void timer_irq_handler(void) {
|
||||
mrb_tick(global_mrb); // Must be called from timer interrupt
|
||||
void mrb_task_hal_final(mrb_state *mrb)
|
||||
{
|
||||
hardware_timer_stop();
|
||||
registered_vm = NULL;
|
||||
}
|
||||
|
||||
void mrb_task_enable_irq(void)
|
||||
{
|
||||
hardware_enable_interrupts();
|
||||
}
|
||||
|
||||
void mrb_task_disable_irq(void)
|
||||
{
|
||||
hardware_disable_interrupts();
|
||||
}
|
||||
|
||||
void mrb_task_hal_idle_cpu(mrb_state *mrb)
|
||||
{
|
||||
(void)mrb;
|
||||
hardware_sleep_mode(); // Enter low-power mode until interrupt
|
||||
}
|
||||
|
||||
// Timer ISR - must call mrb_tick() for scheduler
|
||||
void timer_isr(void)
|
||||
{
|
||||
if (registered_vm) {
|
||||
mrb_tick(registered_vm);
|
||||
}
|
||||
}
|
||||
|
||||
// Gem initialization (required but can be empty)
|
||||
void mrb_hal_myplatform_task_gem_init(mrb_state *mrb)
|
||||
{
|
||||
(void)mrb;
|
||||
}
|
||||
|
||||
void mrb_hal_myplatform_task_gem_final(mrb_state *mrb)
|
||||
{
|
||||
(void)mrb;
|
||||
}
|
||||
```
|
||||
|
||||
See `hal-posix-task` and `hal-win-task` source code for complete reference implementations.
|
||||
|
||||
## Examples
|
||||
|
||||
### Basic Multitasking
|
||||
|
||||
@@ -0,0 +1,165 @@
|
||||
/*
|
||||
** task_hal.h - Task scheduler Hardware Abstraction Layer (HAL)
|
||||
**
|
||||
** See Copyright Notice in mruby.h
|
||||
**
|
||||
** This header defines the HAL interface that platform-specific implementations
|
||||
** must provide. The HAL separates platform-specific timer and interrupt handling
|
||||
** from the core task scheduler logic.
|
||||
*/
|
||||
|
||||
#ifndef MRUBY_TASK_HAL_H
|
||||
#define MRUBY_TASK_HAL_H
|
||||
|
||||
#include <mruby.h>
|
||||
|
||||
/*
|
||||
* Configuration - can be overridden in platform-specific build configs
|
||||
*/
|
||||
|
||||
/* Tick period in milliseconds - how often the timer fires */
|
||||
#ifndef MRB_TICK_UNIT
|
||||
#define MRB_TICK_UNIT 4
|
||||
#endif
|
||||
|
||||
/* Number of timer ticks per task timeslice */
|
||||
#ifndef MRB_TIMESLICE_TICK_COUNT
|
||||
#define MRB_TIMESLICE_TICK_COUNT 3
|
||||
#endif
|
||||
|
||||
/* Maximum number of concurrent mrb_state instances with task scheduler */
|
||||
#ifndef MRB_TASK_MAX_VMS
|
||||
#define MRB_TASK_MAX_VMS 8
|
||||
#endif
|
||||
|
||||
/*
|
||||
* HAL Interface Functions
|
||||
*
|
||||
* Platform-specific implementations (hal-posix-task, hal-win-task, etc.)
|
||||
* must provide these functions.
|
||||
*/
|
||||
|
||||
/**
|
||||
* Initialize hardware timer and interrupt system
|
||||
*
|
||||
* Called once during mruby-task gem initialization. Should set up a periodic
|
||||
* timer that calls mrb_tick(mrb) every MRB_TICK_UNIT milliseconds.
|
||||
*
|
||||
* Requirements:
|
||||
* - Initialize platform-specific timer hardware/APIs
|
||||
* - Set up timer to fire every MRB_TICK_UNIT milliseconds
|
||||
* - Register mrb_state for multi-VM support if needed
|
||||
* - Initialize interrupt protection mechanisms (mutexes, signal masks, etc.)
|
||||
* - Timer should call mrb_tick() on each tick for all registered VMs
|
||||
*
|
||||
* @param mrb The mruby state to associate with the timer
|
||||
*/
|
||||
void mrb_task_hal_init(mrb_state *mrb);
|
||||
|
||||
/**
|
||||
* Cleanup timer and interrupt resources
|
||||
*
|
||||
* Called during mruby-task gem finalization. Should clean up all resources
|
||||
* allocated by mrb_task_hal_init().
|
||||
*
|
||||
* Requirements:
|
||||
* - Stop and cleanup platform timer
|
||||
* - Unregister mrb_state from multi-VM support
|
||||
* - Free any allocated HAL resources
|
||||
* - If last VM, cleanup global HAL state
|
||||
*
|
||||
* @param mrb The mruby state to disassociate from the timer
|
||||
*/
|
||||
void mrb_task_hal_final(mrb_state *mrb);
|
||||
|
||||
/**
|
||||
* Enable timer interrupts (exit critical section)
|
||||
*
|
||||
* Called by the task scheduler when it's safe to allow timer interrupts.
|
||||
* Should enable timer interrupts/callbacks that were disabled by
|
||||
* mrb_task_disable_irq().
|
||||
*
|
||||
* Requirements:
|
||||
* - Must be reentrant (can be called multiple times)
|
||||
* - Should use nesting counter or equivalent for nested critical sections
|
||||
* - On POSIX: unmask signals
|
||||
* - On Windows: leave critical section
|
||||
* - On embedded: enable timer interrupts
|
||||
*/
|
||||
void mrb_task_enable_irq(void);
|
||||
|
||||
/**
|
||||
* Disable timer interrupts (enter critical section)
|
||||
*
|
||||
* Called by the task scheduler before modifying shared task state.
|
||||
* Should disable timer interrupts/callbacks to prevent concurrent access.
|
||||
*
|
||||
* Requirements:
|
||||
* - Must be reentrant (can be called multiple times)
|
||||
* - Should use nesting counter or equivalent for nested critical sections
|
||||
* - On POSIX: block signals
|
||||
* - On Windows: enter critical section
|
||||
* - On embedded: disable timer interrupts
|
||||
*/
|
||||
void mrb_task_disable_irq(void);
|
||||
|
||||
/**
|
||||
* Put CPU in low-power/idle mode
|
||||
*
|
||||
* Called by the scheduler when no tasks are ready to run but some tasks
|
||||
* are waiting or suspended. Should briefly idle the CPU or sleep for
|
||||
* approximately MRB_TICK_UNIT milliseconds to allow timer to fire.
|
||||
*
|
||||
* Requirements:
|
||||
* - Should return when timer fires or after ~MRB_TICK_UNIT milliseconds
|
||||
* - Must allow timer interrupts to occur during idle
|
||||
* - On POSIX: usleep() or nanosleep()
|
||||
* - On Windows: Sleep()
|
||||
* - On embedded: platform-specific sleep/wait-for-interrupt instruction
|
||||
*
|
||||
* @param mrb The mruby state (for context, may be unused)
|
||||
*/
|
||||
void mrb_task_hal_idle_cpu(mrb_state *mrb);
|
||||
|
||||
/*
|
||||
* Core scheduler functions (implemented in task.c, called by HAL)
|
||||
*
|
||||
* These are provided by the core scheduler for HAL implementations to call.
|
||||
*/
|
||||
|
||||
/**
|
||||
* Tick handler - advances scheduler time and wakes sleeping tasks
|
||||
*
|
||||
* HAL timer callback must call this function every MRB_TICK_UNIT milliseconds
|
||||
* for each registered mrb_state. This function:
|
||||
* - Increments the global tick counter
|
||||
* - Decrements running task's timeslice
|
||||
* - Wakes tasks whose sleep time has expired
|
||||
* - Triggers context switches when needed
|
||||
*
|
||||
* @param mrb The mruby state to tick
|
||||
*/
|
||||
void mrb_tick(mrb_state *mrb);
|
||||
|
||||
/**
|
||||
* Main scheduler loop - runs all tasks until completion
|
||||
*
|
||||
* Executes tasks in priority order until all tasks are dormant.
|
||||
* Handles task switching, preemption, and idle states.
|
||||
*
|
||||
* @param mrb The mruby state containing the task scheduler
|
||||
* @return mrb_nil_value() when all tasks complete
|
||||
*/
|
||||
mrb_value mrb_tasks_run(mrb_state *mrb);
|
||||
|
||||
/**
|
||||
* Mark all tasks for garbage collection
|
||||
*
|
||||
* Called by the GC during marking phase to protect task objects
|
||||
* and their associated data from collection.
|
||||
*
|
||||
* @param mrb The mruby state
|
||||
*/
|
||||
void mrb_task_mark_all(mrb_state *mrb);
|
||||
|
||||
#endif /* MRUBY_TASK_HAL_H */
|
||||
@@ -6,8 +6,30 @@ MRuby::Gem::Specification.new('mruby-task') do |spec|
|
||||
# Enable task scheduler globally (required for vm.c integration)
|
||||
spec.build.defines << 'MRB_USE_TASK_SCHEDULER'
|
||||
|
||||
# Windows: link with multimedia timer library
|
||||
if spec.for_windows?
|
||||
spec.linker.libraries << 'winmm'
|
||||
# Check if HAL gem is loaded
|
||||
# HAL gems must be explicitly specified in build config (recommended) or via auto-selection below
|
||||
spec.build.gems.one? { |g| g.name =~ /^hal-.*-task$/ } or begin
|
||||
# No HAL found - determine appropriate error message or auto-load
|
||||
suggested_hal = if spec.for_windows?
|
||||
'hal-win-task'
|
||||
elsif RUBY_PLATFORM =~ /linux|darwin|bsd/
|
||||
'hal-posix-task'
|
||||
else
|
||||
nil
|
||||
end
|
||||
|
||||
if suggested_hal
|
||||
# Auto-load HAL gem for convenience (for development)
|
||||
# This works because HAL gems declare dependency on mruby-task
|
||||
warn "mruby-task: No HAL specified, loading #{suggested_hal} (explicit selection recommended)"
|
||||
spec.build.gem core: suggested_hal
|
||||
else
|
||||
# Unknown platform - fail with helpful message
|
||||
fail "mruby-task: No HAL available for platform '#{RUBY_PLATFORM}'.\n" \
|
||||
"Please specify HAL gem explicitly in your build config:\n" \
|
||||
" conf.gem core: 'hal-posix-task' # For Linux/macOS/BSD\n" \
|
||||
" conf.gem core: 'hal-win-task' # For Windows\n" \
|
||||
"Or create custom HAL - see mrbgems/mruby-task/README.md"
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
@@ -19,13 +19,8 @@
|
||||
#include <string.h>
|
||||
#include <stdint.h>
|
||||
#include <stdlib.h>
|
||||
#ifdef _WIN32
|
||||
#include <windows.h>
|
||||
#else
|
||||
#include <time.h>
|
||||
#include <unistd.h>
|
||||
#endif
|
||||
#include "../include/task.h"
|
||||
#include "task_hal.h"
|
||||
|
||||
/*
|
||||
* Queue helper macros
|
||||
@@ -283,10 +278,6 @@ task_init_context(mrb_state *mrb, mrb_task *t, const struct RProc *proc)
|
||||
* Scheduler core
|
||||
*/
|
||||
|
||||
/* Forward declarations for timer control (defined in HAL section) */
|
||||
static void update_timer_state(void);
|
||||
static void task_count_update(mrb_state *mrb, uint8_t old_status, uint8_t new_status);
|
||||
|
||||
/* Wake up tasks waiting on join for a completed task */
|
||||
static void
|
||||
wake_up_join_waiters(mrb_state *mrb, mrb_task *completed_task)
|
||||
@@ -301,7 +292,6 @@ wake_up_join_waiters(mrb_state *mrb, mrb_task *completed_task)
|
||||
curr->reason = MRB_TASK_REASON_NONE;
|
||||
curr->join = NULL;
|
||||
q_insert_task(mrb, curr);
|
||||
task_count_update(mrb, MRB_TASK_STATUS_WAITING, MRB_TASK_STATUS_READY);
|
||||
mrb_task_enable_irq();
|
||||
}
|
||||
curr = next;
|
||||
@@ -312,12 +302,10 @@ wake_up_join_waiters(mrb_state *mrb, mrb_task *completed_task)
|
||||
static void
|
||||
task_change_state(mrb_state *mrb, mrb_task *t, uint8_t new_status)
|
||||
{
|
||||
uint8_t old_status = t->status;
|
||||
mrb_task_disable_irq();
|
||||
q_delete_task(mrb, t);
|
||||
t->status = new_status;
|
||||
q_insert_task(mrb, t);
|
||||
task_count_update(mrb, old_status, new_status);
|
||||
mrb_task_enable_irq();
|
||||
}
|
||||
|
||||
@@ -381,7 +369,6 @@ execute_task(mrb_state *mrb, mrb_task *t)
|
||||
q_delete_task(mrb, t);
|
||||
t->status = MRB_TASK_STATUS_DORMANT;
|
||||
q_insert_task(mrb, t);
|
||||
task_count_update(mrb, MRB_TASK_STATUS_RUNNING, MRB_TASK_STATUS_DORMANT);
|
||||
mrb_task_enable_irq();
|
||||
|
||||
/* Wake up tasks waiting on join */
|
||||
@@ -495,6 +482,14 @@ mrb_tasks_run(mrb_state *mrb)
|
||||
* Sleep operations
|
||||
*/
|
||||
|
||||
/* Platform-specific includes for root-context sleeping */
|
||||
#if defined(__unix__) || defined(__APPLE__) || defined(__MACH__)
|
||||
#include <time.h>
|
||||
#include <unistd.h>
|
||||
#elif defined(_WIN32)
|
||||
#include <windows.h>
|
||||
#endif
|
||||
|
||||
static void
|
||||
sleep_us_impl(mrb_state *mrb, mrb_int usec)
|
||||
{
|
||||
@@ -580,7 +575,6 @@ sleep_us_impl(mrb_state *mrb, mrb_int usec)
|
||||
}
|
||||
|
||||
q_insert_task(mrb, t);
|
||||
task_count_update(mrb, MRB_TASK_STATUS_READY, MRB_TASK_STATUS_WAITING);
|
||||
|
||||
mrb_task_enable_irq();
|
||||
|
||||
@@ -610,7 +604,6 @@ mrb_f_sleep(mrb_state *mrb, mrb_value self)
|
||||
q_delete_task(mrb, t);
|
||||
t->status = MRB_TASK_STATUS_SUSPENDED;
|
||||
q_insert_task(mrb, t);
|
||||
task_count_update(mrb, MRB_TASK_STATUS_READY, MRB_TASK_STATUS_SUSPENDED);
|
||||
mrb_task_enable_irq();
|
||||
switching_ = TRUE;
|
||||
}
|
||||
@@ -659,580 +652,6 @@ mrb_f_usleep(mrb_state *mrb, mrb_value self)
|
||||
return mrb_fixnum_value(usec);
|
||||
}
|
||||
|
||||
/*
|
||||
* HAL POSIX implementation (Linux, BSD, macOS)
|
||||
*/
|
||||
|
||||
#if defined(__unix__) || defined(__APPLE__) || defined(__MACH__)
|
||||
#include <signal.h>
|
||||
#include <sys/time.h>
|
||||
#include <unistd.h>
|
||||
|
||||
/* Maximum number of concurrent mrb_states with task scheduler */
|
||||
#ifndef MRB_TASK_MAX_VMS
|
||||
#define MRB_TASK_MAX_VMS 8
|
||||
#endif
|
||||
|
||||
static mrb_state *vm_list[MRB_TASK_MAX_VMS];
|
||||
static volatile sig_atomic_t vm_count = 0;
|
||||
static volatile sig_atomic_t timer_enabled = 0;
|
||||
static sigset_t alarm_mask;
|
||||
|
||||
/* Task counters for each VM */
|
||||
static uint16_t vm_ready_counts[MRB_TASK_MAX_VMS];
|
||||
static uint16_t vm_waiting_counts[MRB_TASK_MAX_VMS];
|
||||
|
||||
/* Find VM index in vm_list */
|
||||
static int
|
||||
find_vm_index(mrb_state *mrb)
|
||||
{
|
||||
int i;
|
||||
for (i = 0; i < vm_count; i++) {
|
||||
if (vm_list[i] == mrb) {
|
||||
return i;
|
||||
}
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
/* Helper functions to maintain task counters and update timer */
|
||||
static void
|
||||
task_count_update(mrb_state *mrb, uint8_t old_status, uint8_t new_status)
|
||||
{
|
||||
int vm_idx = find_vm_index(mrb);
|
||||
if (vm_idx < 0) return;
|
||||
|
||||
/* Decrement old queue counter */
|
||||
if (old_status == MRB_TASK_STATUS_READY || old_status == MRB_TASK_STATUS_RUNNING) {
|
||||
if (vm_ready_counts[vm_idx] > 0) {
|
||||
vm_ready_counts[vm_idx]--;
|
||||
}
|
||||
}
|
||||
else if (old_status == MRB_TASK_STATUS_WAITING) {
|
||||
if (vm_waiting_counts[vm_idx] > 0) {
|
||||
vm_waiting_counts[vm_idx]--;
|
||||
}
|
||||
}
|
||||
|
||||
/* Increment new queue counter */
|
||||
if (new_status == MRB_TASK_STATUS_READY || new_status == MRB_TASK_STATUS_RUNNING) {
|
||||
vm_ready_counts[vm_idx]++;
|
||||
}
|
||||
else if (new_status == MRB_TASK_STATUS_WAITING) {
|
||||
vm_waiting_counts[vm_idx]++;
|
||||
}
|
||||
|
||||
update_timer_state();
|
||||
}
|
||||
|
||||
/* Check if timer should be enabled based on task counts */
|
||||
static int
|
||||
should_timer_be_enabled(void)
|
||||
{
|
||||
int i;
|
||||
/* Timer needed if any VM has multiple ready tasks OR any waiting tasks */
|
||||
for (i = 0; i < vm_count; i++) {
|
||||
if (vm_ready_counts[i] > 1 || vm_waiting_counts[i] > 0) {
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Platform-specific timer control */
|
||||
static void
|
||||
hal_set_timer_enabled(int enable)
|
||||
{
|
||||
struct itimerval timer;
|
||||
|
||||
if (enable) {
|
||||
/* Enable timer */
|
||||
timer.it_value.tv_sec = 0;
|
||||
timer.it_value.tv_usec = MRB_TICK_UNIT * 1000;
|
||||
timer.it_interval.tv_sec = 0;
|
||||
timer.it_interval.tv_usec = MRB_TICK_UNIT * 1000;
|
||||
}
|
||||
else {
|
||||
/* Disable timer */
|
||||
timer.it_value.tv_sec = 0;
|
||||
timer.it_value.tv_usec = 0;
|
||||
timer.it_interval.tv_sec = 0;
|
||||
timer.it_interval.tv_usec = 0;
|
||||
}
|
||||
|
||||
setitimer(ITIMER_REAL, &timer, NULL);
|
||||
}
|
||||
|
||||
/* Update timer state based on task counts */
|
||||
static void
|
||||
update_timer_state(void)
|
||||
{
|
||||
int needs_timer = should_timer_be_enabled();
|
||||
|
||||
/* Update timer only if state changed */
|
||||
if (needs_timer && !timer_enabled) {
|
||||
hal_set_timer_enabled(1);
|
||||
timer_enabled = 1;
|
||||
}
|
||||
else if (!needs_timer && timer_enabled) {
|
||||
hal_set_timer_enabled(0);
|
||||
timer_enabled = 0;
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
sigalrm_handler(int sig)
|
||||
{
|
||||
int i;
|
||||
(void)sig;
|
||||
/* Tick all registered VMs */
|
||||
for (i = 0; i < vm_count; i++) {
|
||||
if (vm_list[i]) {
|
||||
mrb_tick(vm_list[i]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
mrb_task_hal_init(mrb_state *mrb)
|
||||
{
|
||||
struct sigaction sa;
|
||||
int i;
|
||||
int vm_index = -1;
|
||||
|
||||
/* Initialize task state */
|
||||
for (i = 0; i < 4; i++) {
|
||||
mrb->task.queues[i] = NULL;
|
||||
}
|
||||
mrb->task.tick = 0;
|
||||
mrb->task.wakeup_tick = UINT32_MAX;
|
||||
mrb->task.switching = FALSE;
|
||||
|
||||
/* Block SIGALRM during registration to avoid race */
|
||||
sigemptyset(&alarm_mask);
|
||||
sigaddset(&alarm_mask, SIGALRM);
|
||||
sigprocmask(SIG_BLOCK, &alarm_mask, NULL);
|
||||
|
||||
/* Check if this VM is already registered */
|
||||
for (i = 0; i < vm_count; i++) {
|
||||
if (vm_list[i] == mrb) {
|
||||
vm_index = i;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
/* Register new VM if not already present */
|
||||
if (vm_index < 0) {
|
||||
if (vm_count >= MRB_TASK_MAX_VMS) {
|
||||
sigprocmask(SIG_UNBLOCK, &alarm_mask, NULL);
|
||||
mrb_raisef(mrb, E_RUNTIME_ERROR,
|
||||
"too many mrb_states with task scheduler (max: %d)",
|
||||
MRB_TASK_MAX_VMS);
|
||||
}
|
||||
vm_list[vm_count] = mrb;
|
||||
vm_ready_counts[vm_count] = 0;
|
||||
vm_waiting_counts[vm_count] = 0;
|
||||
vm_count++;
|
||||
}
|
||||
|
||||
/* Set up signal handler only for first VM */
|
||||
if (vm_count == 1) {
|
||||
/* Set up signal handler - no SA_RESTART so nanosleep returns on EINTR */
|
||||
sa.sa_handler = sigalrm_handler;
|
||||
sa.sa_flags = 0;
|
||||
sigemptyset(&sa.sa_mask);
|
||||
sigaction(SIGALRM, &sa, NULL);
|
||||
}
|
||||
|
||||
/* Update timer state based on current task queues */
|
||||
update_timer_state();
|
||||
|
||||
/* Unblock SIGALRM */
|
||||
sigprocmask(SIG_UNBLOCK, &alarm_mask, NULL);
|
||||
}
|
||||
|
||||
void
|
||||
mrb_task_enable_irq(void)
|
||||
{
|
||||
sigprocmask(SIG_UNBLOCK, &alarm_mask, NULL);
|
||||
}
|
||||
|
||||
void
|
||||
mrb_task_disable_irq(void)
|
||||
{
|
||||
sigprocmask(SIG_BLOCK, &alarm_mask, NULL);
|
||||
}
|
||||
|
||||
void
|
||||
mrb_task_hal_idle_cpu(mrb_state *mrb)
|
||||
{
|
||||
(void)mrb;
|
||||
/* On POSIX, just pause briefly */
|
||||
usleep(MRB_TICK_UNIT * 1000);
|
||||
}
|
||||
|
||||
void
|
||||
mrb_task_hal_final(mrb_state *mrb)
|
||||
{
|
||||
int i, j;
|
||||
|
||||
/* Block SIGALRM during unregistration */
|
||||
sigprocmask(SIG_BLOCK, &alarm_mask, NULL);
|
||||
|
||||
/* Find and remove this VM from the list */
|
||||
for (i = 0; i < vm_count; i++) {
|
||||
if (vm_list[i] == mrb) {
|
||||
/* Shift remaining VMs and counters down */
|
||||
for (j = i; j < vm_count - 1; j++) {
|
||||
vm_list[j] = vm_list[j + 1];
|
||||
vm_ready_counts[j] = vm_ready_counts[j + 1];
|
||||
vm_waiting_counts[j] = vm_waiting_counts[j + 1];
|
||||
}
|
||||
vm_list[vm_count - 1] = NULL;
|
||||
vm_ready_counts[vm_count - 1] = 0;
|
||||
vm_waiting_counts[vm_count - 1] = 0;
|
||||
vm_count--;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
/* Update timer state based on remaining VMs */
|
||||
update_timer_state();
|
||||
|
||||
/* Unblock SIGALRM */
|
||||
sigprocmask(SIG_UNBLOCK, &alarm_mask, NULL);
|
||||
}
|
||||
|
||||
#elif defined(_WIN32)
|
||||
/*
|
||||
* HAL Windows implementation
|
||||
*/
|
||||
|
||||
#include <windows.h>
|
||||
#include <timeapi.h>
|
||||
|
||||
/* Maximum number of concurrent mrb_states with task scheduler */
|
||||
#ifndef MRB_TASK_MAX_VMS
|
||||
#define MRB_TASK_MAX_VMS 8
|
||||
#endif
|
||||
|
||||
static mrb_state *vm_list[MRB_TASK_MAX_VMS];
|
||||
static volatile LONG vm_count = 0;
|
||||
static volatile LONG timer_enabled = 0;
|
||||
static CRITICAL_SECTION irq_lock;
|
||||
static MMRESULT timer_id = 0;
|
||||
|
||||
/* Task counters for each VM */
|
||||
static uint16_t vm_ready_counts[MRB_TASK_MAX_VMS];
|
||||
static uint16_t vm_waiting_counts[MRB_TASK_MAX_VMS];
|
||||
|
||||
/* Find VM index in vm_list */
|
||||
static int
|
||||
find_vm_index(mrb_state *mrb)
|
||||
{
|
||||
int i;
|
||||
for (i = 0; i < vm_count; i++) {
|
||||
if (vm_list[i] == mrb) {
|
||||
return i;
|
||||
}
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
/* Helper functions to maintain task counters and update timer */
|
||||
static void
|
||||
task_count_update(mrb_state *mrb, uint8_t old_status, uint8_t new_status)
|
||||
{
|
||||
int vm_idx = find_vm_index(mrb);
|
||||
if (vm_idx < 0) return;
|
||||
|
||||
/* Decrement old queue counter */
|
||||
if (old_status == MRB_TASK_STATUS_READY || old_status == MRB_TASK_STATUS_RUNNING) {
|
||||
if (vm_ready_counts[vm_idx] > 0) {
|
||||
vm_ready_counts[vm_idx]--;
|
||||
}
|
||||
}
|
||||
else if (old_status == MRB_TASK_STATUS_WAITING) {
|
||||
if (vm_waiting_counts[vm_idx] > 0) {
|
||||
vm_waiting_counts[vm_idx]--;
|
||||
}
|
||||
}
|
||||
|
||||
/* Increment new queue counter */
|
||||
if (new_status == MRB_TASK_STATUS_READY || new_status == MRB_TASK_STATUS_RUNNING) {
|
||||
vm_ready_counts[vm_idx]++;
|
||||
}
|
||||
else if (new_status == MRB_TASK_STATUS_WAITING) {
|
||||
vm_waiting_counts[vm_idx]++;
|
||||
}
|
||||
|
||||
update_timer_state();
|
||||
}
|
||||
|
||||
/* Check if timer should be enabled based on task counts */
|
||||
static int
|
||||
should_timer_be_enabled(void)
|
||||
{
|
||||
int i;
|
||||
/* Timer needed if any VM has multiple ready tasks OR any waiting tasks */
|
||||
for (i = 0; i < vm_count; i++) {
|
||||
if (vm_ready_counts[i] > 1 || vm_waiting_counts[i] > 0) {
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Multimedia timer callback - called periodically by Windows */
|
||||
static void CALLBACK
|
||||
timer_callback(UINT uID, UINT uMsg, DWORD_PTR dwUser, DWORD_PTR dw1, DWORD_PTR dw2)
|
||||
{
|
||||
int i;
|
||||
(void)uID; (void)uMsg; (void)dwUser; (void)dw1; (void)dw2;
|
||||
|
||||
/* Tick all registered VMs */
|
||||
EnterCriticalSection(&irq_lock);
|
||||
for (i = 0; i < vm_count; i++) {
|
||||
if (vm_list[i]) {
|
||||
mrb_tick(vm_list[i]);
|
||||
}
|
||||
}
|
||||
LeaveCriticalSection(&irq_lock);
|
||||
}
|
||||
|
||||
/* Platform-specific timer control */
|
||||
static void
|
||||
hal_set_timer_enabled(int enable)
|
||||
{
|
||||
if (enable) {
|
||||
/* Enable timer - create multimedia timer if not already running */
|
||||
if (timer_id == 0) {
|
||||
/* Request 1ms timer resolution */
|
||||
timeBeginPeriod(1);
|
||||
|
||||
/* Create periodic timer with MRB_TICK_UNIT interval */
|
||||
timer_id = timeSetEvent(
|
||||
MRB_TICK_UNIT, /* interval in milliseconds */
|
||||
1, /* resolution in milliseconds */
|
||||
timer_callback, /* callback function */
|
||||
0, /* user data */
|
||||
TIME_PERIODIC | TIME_KILL_SYNCHRONOUS
|
||||
);
|
||||
}
|
||||
}
|
||||
else {
|
||||
/* Disable timer */
|
||||
if (timer_id != 0) {
|
||||
timeKillEvent(timer_id);
|
||||
timeEndPeriod(1);
|
||||
timer_id = 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* Update timer state based on task counts */
|
||||
static void
|
||||
update_timer_state(void)
|
||||
{
|
||||
int needs_timer = should_timer_be_enabled();
|
||||
|
||||
/* Update timer only if state changed */
|
||||
if (needs_timer && !timer_enabled) {
|
||||
hal_set_timer_enabled(1);
|
||||
InterlockedExchange(&timer_enabled, 1);
|
||||
}
|
||||
else if (!needs_timer && timer_enabled) {
|
||||
hal_set_timer_enabled(0);
|
||||
InterlockedExchange(&timer_enabled, 0);
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
mrb_task_hal_init(mrb_state *mrb)
|
||||
{
|
||||
int i;
|
||||
LONG idx;
|
||||
|
||||
/* Initialize task state */
|
||||
for (i = 0; i < 4; i++) {
|
||||
mrb->task.queues[i] = NULL;
|
||||
}
|
||||
mrb->task.tick = 0;
|
||||
mrb->task.wakeup_tick = UINT32_MAX;
|
||||
mrb->task.switching = FALSE;
|
||||
|
||||
/* Initialize critical section on first VM */
|
||||
if (vm_count == 0) {
|
||||
InitializeCriticalSection(&irq_lock);
|
||||
}
|
||||
|
||||
EnterCriticalSection(&irq_lock);
|
||||
|
||||
/* Check if this VM is already registered */
|
||||
idx = -1;
|
||||
for (i = 0; i < vm_count; i++) {
|
||||
if (vm_list[i] == mrb) {
|
||||
idx = i;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
/* Register new VM if not already present */
|
||||
if (idx < 0) {
|
||||
if (vm_count >= MRB_TASK_MAX_VMS) {
|
||||
LeaveCriticalSection(&irq_lock);
|
||||
mrb_raisef(mrb, E_RUNTIME_ERROR,
|
||||
"too many mrb_states with task scheduler (max: %d)",
|
||||
MRB_TASK_MAX_VMS);
|
||||
}
|
||||
vm_list[vm_count] = mrb;
|
||||
vm_ready_counts[vm_count] = 0;
|
||||
vm_waiting_counts[vm_count] = 0;
|
||||
InterlockedIncrement(&vm_count);
|
||||
}
|
||||
|
||||
/* Update timer state based on current task queues */
|
||||
update_timer_state();
|
||||
|
||||
LeaveCriticalSection(&irq_lock);
|
||||
}
|
||||
|
||||
void
|
||||
mrb_task_enable_irq(void)
|
||||
{
|
||||
LeaveCriticalSection(&irq_lock);
|
||||
}
|
||||
|
||||
void
|
||||
mrb_task_disable_irq(void)
|
||||
{
|
||||
EnterCriticalSection(&irq_lock);
|
||||
}
|
||||
|
||||
void
|
||||
mrb_task_hal_idle_cpu(mrb_state *mrb)
|
||||
{
|
||||
(void)mrb;
|
||||
/* On Windows, just sleep briefly */
|
||||
Sleep(MRB_TICK_UNIT);
|
||||
}
|
||||
|
||||
void
|
||||
mrb_task_hal_final(mrb_state *mrb)
|
||||
{
|
||||
int i, j;
|
||||
|
||||
EnterCriticalSection(&irq_lock);
|
||||
|
||||
/* Find and remove this VM from the list */
|
||||
for (i = 0; i < vm_count; i++) {
|
||||
if (vm_list[i] == mrb) {
|
||||
/* Shift remaining VMs and counters down */
|
||||
for (j = i; j < vm_count - 1; j++) {
|
||||
vm_list[j] = vm_list[j + 1];
|
||||
vm_ready_counts[j] = vm_ready_counts[j + 1];
|
||||
vm_waiting_counts[j] = vm_waiting_counts[j + 1];
|
||||
}
|
||||
vm_list[vm_count - 1] = NULL;
|
||||
vm_ready_counts[vm_count - 1] = 0;
|
||||
vm_waiting_counts[vm_count - 1] = 0;
|
||||
InterlockedDecrement(&vm_count);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
/* Update timer state based on remaining VMs */
|
||||
update_timer_state();
|
||||
|
||||
/* Cleanup critical section if last VM */
|
||||
if (vm_count == 0) {
|
||||
LeaveCriticalSection(&irq_lock);
|
||||
DeleteCriticalSection(&irq_lock);
|
||||
}
|
||||
else {
|
||||
LeaveCriticalSection(&irq_lock);
|
||||
}
|
||||
}
|
||||
|
||||
#else
|
||||
/* Stub implementation for unsupported platforms */
|
||||
/* WARNING: Task scheduler requires platform-specific timer implementation */
|
||||
/* This stub allows compilation but task scheduling will NOT work correctly */
|
||||
|
||||
/* Stub for task_count_update */
|
||||
static void
|
||||
task_count_update(mrb_state *mrb, uint8_t old_status, uint8_t new_status)
|
||||
{
|
||||
(void)mrb;
|
||||
(void)old_status;
|
||||
(void)new_status;
|
||||
/* Platform-specific task counting for timer control not implemented */
|
||||
}
|
||||
|
||||
/* Platform-specific timer control stub */
|
||||
static void
|
||||
hal_set_timer_enabled(int enable)
|
||||
{
|
||||
(void)enable;
|
||||
/* TODO: Platform-specific timer control */
|
||||
}
|
||||
|
||||
/* Stub for update_timer_state */
|
||||
static void
|
||||
update_timer_state(void)
|
||||
{
|
||||
/* TODO: Implement timer state management */
|
||||
}
|
||||
|
||||
static int
|
||||
should_timer_be_enabled(void)
|
||||
{
|
||||
return 0; /* Stub: always return false */
|
||||
}
|
||||
|
||||
void
|
||||
mrb_task_hal_init(mrb_state *mrb)
|
||||
{
|
||||
int i;
|
||||
|
||||
/* Initialize task state */
|
||||
for (i = 0; i < 4; i++) {
|
||||
mrb->task.queues[i] = NULL;
|
||||
}
|
||||
mrb->task.tick = 0;
|
||||
mrb->task.wakeup_tick = UINT32_MAX;
|
||||
mrb->task.switching = FALSE;
|
||||
|
||||
/* TODO: Platform-specific timer initialization */
|
||||
}
|
||||
|
||||
void
|
||||
mrb_task_enable_irq(void)
|
||||
{
|
||||
/* TODO: Platform-specific interrupt enable */
|
||||
}
|
||||
|
||||
void
|
||||
mrb_task_disable_irq(void)
|
||||
{
|
||||
/* TODO: Platform-specific interrupt disable */
|
||||
}
|
||||
|
||||
void
|
||||
mrb_task_hal_idle_cpu(mrb_state *mrb)
|
||||
{
|
||||
(void)mrb;
|
||||
/* TODO: Platform-specific idle/sleep */
|
||||
}
|
||||
|
||||
void
|
||||
mrb_task_hal_final(mrb_state *mrb)
|
||||
{
|
||||
(void)mrb;
|
||||
/* TODO: Platform-specific cleanup */
|
||||
}
|
||||
#endif
|
||||
|
||||
/*
|
||||
* Task class methods
|
||||
*/
|
||||
@@ -1303,7 +722,6 @@ mrb_task_s_new(mrb_state *mrb, mrb_value self)
|
||||
/* Insert into ready queue */
|
||||
mrb_task_disable_irq();
|
||||
q_insert_task(mrb, t);
|
||||
task_count_update(mrb, MRB_TASK_STATUS_DORMANT, MRB_TASK_STATUS_READY);
|
||||
mrb_task_enable_irq();
|
||||
|
||||
/* Trigger context switch if this task has higher priority than current */
|
||||
@@ -1685,12 +1103,10 @@ mrb_task_terminate(mrb_state *mrb, mrb_value self)
|
||||
mrb_task_disable_irq();
|
||||
|
||||
/* Move to dormant queue */
|
||||
uint8_t old_status = t->status;
|
||||
q_delete_task(mrb, t);
|
||||
t->status = MRB_TASK_STATUS_DORMANT;
|
||||
t->c.status = MRB_TASK_STOPPED;
|
||||
q_insert_task(mrb, t);
|
||||
task_count_update(mrb, old_status, MRB_TASK_STATUS_DORMANT);
|
||||
|
||||
mrb_task_enable_irq();
|
||||
|
||||
@@ -1735,7 +1151,6 @@ mrb_task_join(mrb_state *mrb, mrb_value self)
|
||||
current->reason = MRB_TASK_REASON_JOIN;
|
||||
current->join = t;
|
||||
q_insert_task(mrb, current);
|
||||
task_count_update(mrb, MRB_TASK_STATUS_READY, MRB_TASK_STATUS_WAITING);
|
||||
mrb_task_enable_irq();
|
||||
|
||||
/* Trigger context switch */
|
||||
|
||||
Reference in New Issue
Block a user