From 87c88897261c0b0296c6b59010093520afafbfb0 Mon Sep 17 00:00:00 2001 From: "Yukihiro \"Matz\" Matsumoto" Date: Wed, 15 Oct 2025 08:00:33 +0900 Subject: [PATCH] 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 --- mrbgems/hal-posix-task/src/task_hal.c | 57 ++++++++++++++++++++++ mrbgems/hal-win-task/src/task_hal.c | 11 +++++ mrbgems/mruby-task/include/task_hal.h | 20 ++++++++ mrbgems/mruby-task/src/task.c | 68 +-------------------------- 4 files changed, 90 insertions(+), 66 deletions(-) diff --git a/mrbgems/hal-posix-task/src/task_hal.c b/mrbgems/hal-posix-task/src/task_hal.c index d5f7bbe5b..a9d20251f 100644 --- a/mrbgems/hal-posix-task/src/task_hal.c +++ b/mrbgems/hal-posix-task/src/task_hal.c @@ -13,9 +13,15 @@ #include "task_hal.h" #include #include +#include #include #include +/* 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) { diff --git a/mrbgems/hal-win-task/src/task_hal.c b/mrbgems/hal-win-task/src/task_hal.c index ec2b07d2a..27464eef9 100644 --- a/mrbgems/hal-win-task/src/task_hal.c +++ b/mrbgems/hal-win-task/src/task_hal.c @@ -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) { diff --git a/mrbgems/mruby-task/include/task_hal.h b/mrbgems/mruby-task/include/task_hal.h index eba63af8b..37c3a608f 100644 --- a/mrbgems/mruby-task/include/task_hal.h +++ b/mrbgems/mruby-task/include/task_hal.h @@ -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) */ diff --git a/mrbgems/mruby-task/src/task.c b/mrbgems/mruby-task/src/task.c index bacb6f2c7..c777ce692 100644 --- a/mrbgems/mruby-task/src/task.c +++ b/mrbgems/mruby-task/src/task.c @@ -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 -#include -#elif defined(_WIN32) -#include -#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;