diff --git a/core/iwasm/libraries/libc-wasi/sandboxed-system-primitives/src/posix.c b/core/iwasm/libraries/libc-wasi/sandboxed-system-primitives/src/posix.c index 3d90811bca..bc9c3c9b4b 100644 --- a/core/iwasm/libraries/libc-wasi/sandboxed-system-primitives/src/posix.c +++ b/core/iwasm/libraries/libc-wasi/sandboxed-system-primitives/src/posix.c @@ -2135,7 +2135,7 @@ wasmtime_ssp_poll_oneoff(wasm_exec_env_t exec_env, struct fd_table *curfds, // nanosleep(). This is incorrect, but good enough for now. os_timespec ts; convert_timestamp(in[0].u.u.clock.timeout, &ts); - nanosleep(&ts, NULL); + os_nanosleep(&ts, NULL); } break; case __WASI_CLOCK_REALTIME: @@ -2163,7 +2163,7 @@ wasmtime_ssp_poll_oneoff(wasm_exec_env_t exec_env, struct fd_table *curfds, // Relative sleeps can be done using nanosleep(). os_timespec ts; convert_timestamp(in[0].u.u.clock.timeout, &ts); - nanosleep(&ts, NULL); + os_nanosleep(&ts, NULL); } break; default: diff --git a/core/shared/platform/common/posix/posix_sleep.c b/core/shared/platform/common/posix/posix_sleep.c index fa0645037a..541c671928 100644 --- a/core/shared/platform/common/posix/posix_sleep.c +++ b/core/shared/platform/common/posix/posix_sleep.c @@ -6,6 +6,7 @@ #include #include "platform_api_extension.h" +#include "libc_errno.h" int os_usleep(uint32 usec) @@ -18,3 +19,13 @@ os_usleep(uint32 usec) ret = nanosleep(&ts, NULL); return ret == 0 ? 0 : -1; } + +__wasi_errno_t +os_nanosleep(const os_timespec *req, os_timespec *rem) +{ + int ret; + + ret = nanosleep(req, rem); + + return convert_errno(ret); +} \ No newline at end of file diff --git a/core/shared/platform/include/platform_api_extension.h b/core/shared/platform/include/platform_api_extension.h index 41ec5742bc..14b72a6bfb 100644 --- a/core/shared/platform/include/platform_api_extension.h +++ b/core/shared/platform/include/platform_api_extension.h @@ -1669,28 +1669,112 @@ __wasi_errno_t os_clock_time_get(__wasi_clockid_t clock_id, __wasi_timestamp_t precision, __wasi_timestamp_t *time); -#ifdef __cplusplus -} -#endif -/* Experimental */ +/**************************************************** + * Section 5 * + * Experimental functions * + ****************************************************/ -/* Used in posix.c around L2259 and expect the return code - * of ioctl() directly. +/** + * NOTES: + * The bellow functions were defined after increasing the support for the + * Zephyr platform to get the full WASI libc running. + * + * If you don't need to support WASI libc, there is no need to implement these + * APIs. + * + * We could also move these definitions to the proper sections + * (File, Time, ...) but still keep them here until we get more feedback or + * observe edges cases. + */ + +/** + * @brief Control device. + * + * The `ioctl` function was one of the POSIX function used without platform + * abastraction API in the `sandboxed-system-primitives` and particularly in the + * `wasmtime_ssp_poll_oneoff` function. + * + * @param handle A platform file handler. + * @param request A platform-dependent request code. + * @param argp Usually an untyped pointer to memory. + * + * @return + * __WASI_ESUCCESS On success + * __WASI_EBADF handle is not a valid file handler. + * __WASI_EFAULT argp references an inaccessible memory area. + * __WASI_EINVAL request or argp is not valid. + * __WASI_ENOTTY handle is not associated with a character special device. + * __WASI_ENOTTY The specified request does not apply to the kind of + * object that the file handler handle references. + * + * NOTE: We seem to only use/support the `FIONREAD` request code: + * + * FIONREAD Get the number of bytes in the input buffer. */ int os_ioctl(os_file_handle handle, int request, ...); -/* Higher level API: - * __wasi_errno_t - * blocking_op_poll(wasm_exec_env_t exec_env, os_poll_file_handle *pfds, - * os_nfds_t nfds, int timeout_ms, int *retp) - * Already format the errno and expect the return code of poll() directly. +/** + * @brief Wait for some event on a file descriptor. + * + * For more context, the higher level API `blocking_op_poll`. + * + * __wasi_errno_t + * blocking_op_poll(wasm_exec_env_t exec_env, os_poll_file_handle *pfds, + * os_nfds_t nfds, int timeout_ms, int *retp) + * + * Format the error code (errno) and expect the return code of (POSIX) `poll()` + * directly hence the `int` return type. + * + * @param pfds A poll file descriptors array + * @param nfs The size of the poll file descriptors array + * @param timeout Specify the number of ms that poll is busy waiting. + * */ int os_poll(os_poll_file_handle *pfds, os_nfds_t nfs, int timeout); +/** + * @brief Compare two platform's file handle/descriptor. + * + * This function should ALWAYS be used when comparaing file handlers because + * for some platforms (Windows, Zephyr, etc...) the `os_file_handle` type is + * a structure and not a primary type like for POSIX. + * + * @param handle1 First file handle or constant. + * @param handle2 Second file handle. + * + * @return + * true The file handlers are similar. + * false The file handlers don't match. + */ bool os_compare_file_handle(os_file_handle handle1, os_file_handle handle2); + +/** + * @brief high-resolution sleep + * + * The `nanosleep` function was the last POSIX function used without platform + * abastraction API in the `sandboxed-system-primitives` and particularly in the + * `wasmtime_ssp_poll_oneoff` function. + * + * @param req time requiered to sleep. + * @param rem remaining time in the case that the function is interrupted. + * + * @return + * __WASI_ESUCCESS On success + * __WASI_EFAULT Problem with copying information. + * __WASI_EINTR The sleep has been interrupted. + * __WASI_EINVAL The req input is badly formed. + */ +__wasi_errno_t +os_nanosleep(const os_timespec *req, os_timespec *rem); + + +#ifdef __cplusplus +} +#endif + #endif /* #ifndef PLATFORM_API_EXTENSION_H */ diff --git a/core/shared/platform/zephyr/platform_internal.h b/core/shared/platform/zephyr/platform_internal.h index d5f0c80d81..7bb14dd337 100644 --- a/core/shared/platform/zephyr/platform_internal.h +++ b/core/shared/platform/zephyr/platform_internal.h @@ -51,6 +51,7 @@ #include #include #include +#include #endif /* end of KERNEL_VERSION_NUMBER < 0x030200 */ #ifdef CONFIG_USERSPACE @@ -284,7 +285,24 @@ typedef unsigned int os_nfds_t; #define FIONREAD ZFD_IOCTL_FIONREAD -typedef struct timespec os_timespec; +/* + * The previous `os_timespec` was a forward declaration: + * + * typedef struct timespec os_timespec; + * + * It was not bad as is, but seemed to not be included from anywhere. + * As of Zephyr v3.7.0 (LTS) the `timespec` struct is only declared with the + * configuration `CONFIG_POSIX_API` enabled which is not a prefered + * configuration for the Zephyr port. + * + * NOTE: If Zephyr later exposes `struct timespec` without requiring + * CONFIG_POSIX_API, this definition should be replaced by an alias. + */ +typedef struct { + int64_t tv_sec; + long tv_nsec; +} os_timespec; + #ifndef CLOCK_REALTIME #define CLOCK_REALTIME 1 diff --git a/core/shared/platform/zephyr/shared_platform.cmake b/core/shared/platform/zephyr/shared_platform.cmake index f424b97204..f0b4a5ecce 100644 --- a/core/shared/platform/zephyr/shared_platform.cmake +++ b/core/shared/platform/zephyr/shared_platform.cmake @@ -19,6 +19,7 @@ if (NOT WAMR_BUILD_LIBC_WASI EQUAL 1) list(REMOVE_ITEM source_all ${PLATFORM_SHARED_DIR}/zephyr_socket.c) list(REMOVE_ITEM source_all ${PLATFORM_SHARED_DIR}/zephyr_file.c) list(REMOVE_ITEM source_all ${PLATFORM_SHARED_DIR}/zephyr_clock.c) + list(REMOVE_ITEM source_all ${PLATFORM_SHARED_DIR}/zephyr_sleep.c) else() include (${CMAKE_CURRENT_LIST_DIR}/../common/libc-util/platform_common_libc_util.cmake) set(source_all ${source_all} ${PLATFORM_COMMON_LIBC_UTIL_SOURCE}) diff --git a/core/shared/platform/zephyr/zephyr_sleep.c b/core/shared/platform/zephyr/zephyr_sleep.c new file mode 100644 index 0000000000..8de9c73227 --- /dev/null +++ b/core/shared/platform/zephyr/zephyr_sleep.c @@ -0,0 +1,120 @@ +/* + * Copyright (C) 2024 Grenoble INP - ESISAR. All rights reserved. + * SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception + */ +#include "platform_api_extension.h" + +/* + * In Zephyr v3.7, there is no simple way to get a `nanosleep` implementation. + * But in the later version the Zephyr community introduced some clock APIs + * and their POSIX compatibility layer. + * + * Relevant Zephyr sources: + * - zephyr/include/zephyr/sys/clock.h + * - Zephyr/lib/os/clock.c + * POSIX layer: + * - zephyr/lib/posix/options/clock.c + * + * Instead of re-implementing the full Clock APIs, this file provides a naive + * `nanosleep` implementation based on the Zephyr thread API (`k_sleep`). + * + * Limitations: + * Maximum sleep duration is limited by UINT32_MAX or UINT64_MAX ticks + * (≈ 4,294,967,295 and 18,446,744,073,709,551,615 respectively). + * + * Example at a "slow" clock rate of 50 kHz: + * - UINT32_MAX: ~85 899s (~23 hours) + * - UINT64_MAX: ~368 934 881 474 191s (~11.7 millions years) + * Clearly, `nanosleep` should not be used for such long durations. + * + * Note: this assumes `CONFIG_POSIX_API=n` in the Zephyr application. + */ + +static k_ticks_t timespec_to_ticks(const os_timespec *ts); +static void ticks_to_timespec(k_ticks_t ticks, os_timespec *ts); + +__wasi_errno_t +os_nanosleep(const os_timespec *req, os_timespec *rem) +{ + k_timeout_t timeout; + k_ticks_t rem_ticks; + + if (req == NULL){ + return __WASI_EINVAL; + } + + if (req->tv_sec < 0 || req->tv_nsec < 0 || req->tv_nsec >= NSEC_PER_SEC) { + return __WASI_EINVAL; + } + + if (req->tv_sec == 0 && req->tv_nsec == 0) { + if (rem != NULL) { + rem->tv_sec = 0; + rem->tv_nsec = 0; + } + return __WASI_ESUCCESS; + } + + timeout.ticks = timespec_to_ticks(req); + + /* + * The function `int32_t k_sleep(k_timeout_t timeout)` return either: + * * 0 requested time elaspsed. + * * >0 remaining time in ms (due to k_wakeup). + */ + int32_t rc = k_sleep(timeout); + if (rem != NULL) { + if (rc > 0) { + +#ifdef CONFIG_TIMEOUT_64BIT + rem_ticks = (k_ticks_t)((uint64_t)rc * CONFIG_SYS_CLOCK_TICKS_PER_SEC / MSEC_PER_SEC); +#else /* CONFIG_TIMEOUT_32BIT */ + uint64_t temp_ticks = (uint64_t)rc * CONFIG_SYS_CLOCK_TICKS_PER_SEC / MSEC_PER_SEC; + rem_ticks = (k_ticks_t)(temp_ticks > UINT32_MAX ? UINT32_MAX : temp_ticks); +#endif + ticks_to_timespec(rem_ticks, rem); + } else { + rem->tv_sec = 0; + rem->tv_nsec = 0; + } + } + + return __WASI_ESUCCESS; +} + + +static k_ticks_t timespec_to_ticks(const os_timespec *ts) +{ + const uint64_t ticks_per_sec = CONFIG_SYS_CLOCK_TICKS_PER_SEC; + uint64_t total_ns, ticks; + + total_ns = (uint64_t)ts->tv_sec * NSEC_PER_SEC + (uint64_t)ts->tv_nsec; + ticks = total_ns * ticks_per_sec / NSEC_PER_SEC; + +#ifdef CONFIG_TIMEOUT_64BIT + if (ticks > INT64_MAX) { + return INT64_MAX; + } +#else /* CONFIG_TIMEOUT_32BIT */ + if (ticks > UINT32_MAX) { + return UINT32_MAX; + } +#endif + + return (k_ticks_t)ticks; +} + +static void ticks_to_timespec(k_ticks_t ticks, os_timespec *ts) +{ + const uint64_t ticks_per_sec = CONFIG_SYS_CLOCK_TICKS_PER_SEC; + uint64_t total_ns; + + if (ts == NULL) { + return; + } + + total_ns = ((uint64_t)ticks * NSEC_PER_SEC) / ticks_per_sec; + + ts->tv_sec = (long)(total_ns / NSEC_PER_SEC); + ts->tv_nsec = (long)(total_ns % NSEC_PER_SEC); +} \ No newline at end of file