mruby-task: move platform-specific sleep to hal

task.c used clock_gettime() directly, breaking portability. added
mrb_task_hal_sleep_us() to hal interface.

Co-authored-by: Claude <noreply@anthropic.com>
This commit is contained in:
Yukihiro "Matz" Matsumoto
2025-10-15 08:00:33 +09:00
parent bc10fbd49e
commit 87c8889726
4 changed files with 90 additions and 66 deletions
+57
View File
@@ -13,9 +13,15 @@
#include "task_hal.h"
#include <signal.h>
#include <sys/time.h>
#include <time.h>
#include <unistd.h>
#include <stdint.h>
/* Time conversion constants */
#define NSEC_PER_MSEC 1000000ULL
#define NSEC_PER_SEC 1000000000ULL
#define USEC_PER_MSEC 1000ULL
/* Multi-VM support */
static mrb_state *vm_list[MRB_TASK_MAX_VMS];
static volatile sig_atomic_t vm_count = 0;
@@ -120,6 +126,57 @@ mrb_task_hal_idle_cpu(mrb_state *mrb)
usleep(MRB_TICK_UNIT * 1000);
}
void
mrb_task_hal_sleep_us(mrb_state *mrb, mrb_int usec)
{
struct timespec start, now, sleep_time;
int ret;
(void)mrb;
/* Validate input to prevent overflow */
if (usec < 0) {
return;
}
ret = clock_gettime(CLOCK_MONOTONIC, &start);
if (ret != 0) {
/* Fallback to simple usleep if clock_gettime fails */
usleep(usec);
return;
}
uint64_t target_ns = (uint64_t)usec * USEC_PER_MSEC;
/* Loop until enough real time has elapsed */
while (1) {
ret = clock_gettime(CLOCK_MONOTONIC, &now);
if (ret != 0) {
break; /* Clock failure - exit loop */
}
uint64_t elapsed_ns = (uint64_t)(now.tv_sec - start.tv_sec) * NSEC_PER_SEC +
(uint64_t)(now.tv_nsec - start.tv_nsec);
if (elapsed_ns >= target_ns) {
break;
}
/* Sleep for remaining time, but at least 1ms to allow timer interrupts */
uint64_t remaining_ns = target_ns - elapsed_ns;
if (remaining_ns > NSEC_PER_MSEC) {
sleep_time.tv_sec = remaining_ns / NSEC_PER_SEC;
sleep_time.tv_nsec = remaining_ns % NSEC_PER_SEC;
}
else {
sleep_time.tv_sec = 0;
sleep_time.tv_nsec = NSEC_PER_MSEC;
}
nanosleep(&sleep_time, NULL); /* Interrupted by signals - that's OK */
}
}
void
mrb_task_hal_final(mrb_state *mrb)
{
+11
View File
@@ -122,6 +122,17 @@ mrb_task_hal_idle_cpu(mrb_state *mrb)
Sleep(MRB_TICK_UNIT);
}
void
mrb_task_hal_sleep_us(mrb_state *mrb, mrb_int usec)
{
(void)mrb;
/* Windows Sleep() takes milliseconds, convert from microseconds */
if (usec >= 0) {
Sleep(usec / 1000);
}
}
void
mrb_task_hal_final(mrb_state *mrb)
{
+20
View File
@@ -121,6 +121,26 @@ void mrb_task_disable_irq(void);
*/
void mrb_task_hal_idle_cpu(mrb_state *mrb);
/**
* Sleep for specified microseconds in wall-clock time
*
* Called by sleep functions when in root context (not in a task).
* Should sleep for the specified number of microseconds in real wall-clock
* time, allowing timer interrupts to occur during the sleep.
*
* Requirements:
* - Sleep for approximately usec microseconds in wall-clock time
* - Must allow timer interrupts/callbacks during sleep
* - Should handle interruptions gracefully and complete full sleep duration
* - On POSIX: use clock_gettime + nanosleep loop for accuracy
* - On Windows: use Sleep() with millisecond conversion
* - On embedded: platform-specific delay with interrupt support
*
* @param mrb The mruby state (for context, may be unused)
* @param usec Number of microseconds to sleep
*/
void mrb_task_hal_sleep_us(mrb_state *mrb, mrb_int usec);
/*
* Core scheduler function (implemented in task.c, called by HAL)
*/
+2 -66
View File
@@ -45,11 +45,6 @@
} \
} while (0)
/* Time conversion constants */
#define NSEC_PER_MSEC 1000000ULL
#define NSEC_PER_SEC 1000000000ULL
#define USEC_PER_MSEC 1000ULL
/* Convert microseconds to tick count */
#define USEC_TO_TICKS(usec) (((usec) / 1000) / MRB_TICK_UNIT)
@@ -482,14 +477,6 @@ mrb_task_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)
{
@@ -497,59 +484,8 @@ sleep_us_impl(mrb_state *mrb, mrb_int usec)
/* Check if we're in a task context */
if (mrb->c == mrb->root_c) {
/* Not in task context - sleep in real wall-clock time */
#ifdef __unix__
struct timespec start, now, sleep_time;
int ret;
/* Validate input to prevent overflow */
if (usec < 0) {
return;
}
ret = clock_gettime(CLOCK_MONOTONIC, &start);
if (ret != 0) {
/* Fallback to simple usleep if clock_gettime fails */
usleep(usec);
switching_ = FALSE;
return;
}
uint64_t target_ns = (uint64_t)usec * USEC_PER_MSEC;
/* Loop until enough real time has elapsed */
while (1) {
ret = clock_gettime(CLOCK_MONOTONIC, &now);
if (ret != 0) {
break; /* Clock failure - exit loop */
}
uint64_t elapsed_ns = (uint64_t)(now.tv_sec - start.tv_sec) * NSEC_PER_SEC +
(uint64_t)(now.tv_nsec - start.tv_nsec);
if (elapsed_ns >= target_ns) {
break;
}
/* Sleep for remaining time, but at least 1ms to allow timer interrupts */
uint64_t remaining_ns = target_ns - elapsed_ns;
if (remaining_ns > NSEC_PER_MSEC) {
sleep_time.tv_sec = remaining_ns / NSEC_PER_SEC;
sleep_time.tv_nsec = remaining_ns % NSEC_PER_SEC;
}
else {
sleep_time.tv_sec = 0;
sleep_time.tv_nsec = NSEC_PER_MSEC;
}
nanosleep(&sleep_time, NULL); /* Interrupted by signals - that's OK */
}
#elif defined(_WIN32)
/* Windows: just use Sleep, it handles interruptions */
if (usec >= 0) {
Sleep(usec / 1000);
}
#endif
/* Not in task context - sleep in real wall-clock time using HAL */
mrb_task_hal_sleep_us(mrb, usec);
/* Clear switching flag - we're in root context, not switching to a task */
switching_ = FALSE;
return;