mirror of
https://github.com/mruby/mruby
synced 2026-06-08 16:11:16 +00:00
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:
@@ -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)
|
||||
{
|
||||
|
||||
@@ -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)
|
||||
{
|
||||
|
||||
@@ -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)
|
||||
*/
|
||||
|
||||
@@ -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;
|
||||
|
||||
Reference in New Issue
Block a user