Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -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:
Expand Down Expand Up @@ -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:
Expand Down
11 changes: 11 additions & 0 deletions core/shared/platform/common/posix/posix_sleep.c
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
#include <time.h>

#include "platform_api_extension.h"
#include "libc_errno.h"
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It is not a standard libc header and leads to a compilation error:

fatal error: libc_errno.h: No such file or directory

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes I know, but it didn't anticipate this error.
In fact I tested building iwasm for Linux (file sample) and no other plateforms (except Zephyr obviously).

From what I see, the issue comes from this CMake logic:

if ((NOT WAMR_BUILD_LIBC_WASI EQUAL 1) AND (NOT WAMR_BUILD_DEBUG_INTERP EQUAL 1))
list(REMOVE_ITEM source_all
${PLATFORM_COMMON_POSIX_DIR}/posix_socket.c
)
else()
include (${CMAKE_CURRENT_LIST_DIR}/../libc-util/platform_common_libc_util.cmake)
set(source_all ${source_all} ${PLATFORM_COMMON_LIBC_UTIL_SOURCE})
endif()

Now that posix_sleep.c directly include libc_errno.h, we can build with WAMR_BUILD_LIBC_WASI=0 but we still need this helper.

How do we manage this case ? We can:

  • Use a case in os_nanosleep on ret for POSIX.
  • Change the CMake logic (but I can't cleary indentify the impact)


int
os_usleep(uint32 usec)
Expand All @@ -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);
}
106 changes: 95 additions & 11 deletions core/shared/platform/include/platform_api_extension.h
Original file line number Diff line number Diff line change
Expand Up @@ -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 */
20 changes: 19 additions & 1 deletion core/shared/platform/zephyr/platform_internal.h
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,7 @@
#include <zephyr/net/net_core.h>
#include <zephyr/net/net_context.h>
#include <zephyr/net/socket.h>
#include <zephyr/fs/fs.h>
#endif /* end of KERNEL_VERSION_NUMBER < 0x030200 */

#ifdef CONFIG_USERSPACE
Expand Down Expand Up @@ -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
Expand Down
1 change: 1 addition & 0 deletions core/shared/platform/zephyr/shared_platform.cmake
Original file line number Diff line number Diff line change
Expand Up @@ -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})
Expand Down
120 changes: 120 additions & 0 deletions core/shared/platform/zephyr/zephyr_sleep.c
Original file line number Diff line number Diff line change
@@ -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);
}
Loading