diff --git a/tests/posix/clock_selection/CMakeLists.txt b/tests/posix/clock_selection/CMakeLists.txt new file mode 100644 index 00000000000..e66e7e7ebdf --- /dev/null +++ b/tests/posix/clock_selection/CMakeLists.txt @@ -0,0 +1,9 @@ +# SPDX-License-Identifier: Apache-2.0 + +cmake_minimum_required(VERSION 3.20.0) +find_package(Zephyr REQUIRED HINTS $ENV{ZEPHYR_BASE}) +project(posix_clock_selection) + +target_sources(app PRIVATE src/main.c ../timers/src/nanosleep_common.c) + +target_compile_options(app PRIVATE -U_POSIX_C_SOURCE -D_POSIX_C_SOURCE=200809L) diff --git a/tests/posix/clock_selection/prj.conf b/tests/posix/clock_selection/prj.conf new file mode 100644 index 00000000000..ef606a6a135 --- /dev/null +++ b/tests/posix/clock_selection/prj.conf @@ -0,0 +1,9 @@ +CONFIG_POSIX_API=y +CONFIG_ZTEST=y + +CONFIG_POSIX_AEP_CHOICE_BASE=y +CONFIG_POSIX_CLOCK_SELECTION=y + +CONFIG_DYNAMIC_THREAD=y +CONFIG_DYNAMIC_THREAD_POOL_SIZE=3 +CONFIG_THREAD_STACK_INFO=y diff --git a/tests/posix/clock_selection/src/main.c b/tests/posix/clock_selection/src/main.c new file mode 100644 index 00000000000..91c6aa9ea35 --- /dev/null +++ b/tests/posix/clock_selection/src/main.c @@ -0,0 +1,116 @@ +/* + * Copyright (c) 2018 Intel Corporation + * Copyright (c) 2025 Tenstorrent AI ULC + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#include +#include + +#include +#include +#include + +#define SELECT_NANOSLEEP 1 +#define SELECT_CLOCK_NANOSLEEP 0 + +void common_lower_bound_check(int selection, clockid_t clock_id, int flags, const uint32_t s, + uint32_t ns); +int select_nanosleep(int selection, clockid_t clock_id, int flags, const struct timespec *rqtp, + struct timespec *rmtp); + +ZTEST(posix_clock_selection, test_clock_nanosleep_execution) +{ + struct timespec ts; + + clock_gettime(CLOCK_MONOTONIC, &ts); + + /* absolute sleeps with the monotonic clock and reference time ts */ + + /* until 1s + 1ns past the reference time */ + common_lower_bound_check(SELECT_CLOCK_NANOSLEEP, CLOCK_MONOTONIC, TIMER_ABSTIME, + ts.tv_sec + 1, 1); + + /* until 1s + 1us past the reference time */ + common_lower_bound_check(SELECT_CLOCK_NANOSLEEP, CLOCK_MONOTONIC, TIMER_ABSTIME, + ts.tv_sec + 1, 1000); + + /* until 1s + 500000000ns past the reference time */ + common_lower_bound_check(SELECT_CLOCK_NANOSLEEP, CLOCK_MONOTONIC, TIMER_ABSTIME, + ts.tv_sec + 1, 500000000); + + /* until 2s past the reference time */ + common_lower_bound_check(SELECT_CLOCK_NANOSLEEP, CLOCK_MONOTONIC, TIMER_ABSTIME, + ts.tv_sec + 2, 0); + + /* until 2s + 1ns past the reference time */ + common_lower_bound_check(SELECT_CLOCK_NANOSLEEP, CLOCK_MONOTONIC, TIMER_ABSTIME, + ts.tv_sec + 2, 1); + + /* until 2s + 1us + 1ns past reference time */ + common_lower_bound_check(SELECT_CLOCK_NANOSLEEP, CLOCK_MONOTONIC, TIMER_ABSTIME, + ts.tv_sec + 2, 1001); + + clock_gettime(CLOCK_REALTIME, &ts); + + /* absolute sleeps with the real time clock and adjusted reference time ts */ + + /* until 1s + 1ns past the reference time */ + common_lower_bound_check(SELECT_CLOCK_NANOSLEEP, CLOCK_REALTIME, TIMER_ABSTIME, + ts.tv_sec + 1, 1); + + /* until 1s + 1us past the reference time */ + common_lower_bound_check(SELECT_CLOCK_NANOSLEEP, CLOCK_REALTIME, TIMER_ABSTIME, + ts.tv_sec + 1, 1000); + + /* until 1s + 500000000ns past the reference time */ + common_lower_bound_check(SELECT_CLOCK_NANOSLEEP, CLOCK_REALTIME, TIMER_ABSTIME, + ts.tv_sec + 1, 500000000); + + /* until 2s past the reference time */ + common_lower_bound_check(SELECT_CLOCK_NANOSLEEP, CLOCK_REALTIME, TIMER_ABSTIME, + ts.tv_sec + 2, 0); + + /* until 2s + 1ns past the reference time */ + common_lower_bound_check(SELECT_CLOCK_NANOSLEEP, CLOCK_REALTIME, TIMER_ABSTIME, + ts.tv_sec + 2, 1); + + /* until 2s + 1us + 1ns past the reference time */ + common_lower_bound_check(SELECT_CLOCK_NANOSLEEP, CLOCK_REALTIME, TIMER_ABSTIME, + ts.tv_sec + 2, 1001); +} + +ZTEST(posix_clock_selection, test_pthread_condattr_getclock) +{ + clockid_t clock_id; + pthread_condattr_t att = {0}; + + zassert_ok(pthread_condattr_init(&att)); + + zassert_ok(pthread_condattr_getclock(&att, &clock_id), "pthread_condattr_getclock failed"); + zassert_equal(clock_id, CLOCK_REALTIME, "clock attribute not set correctly"); + + zassert_ok(pthread_condattr_destroy(&att)); +} + +ZTEST(posix_clock_selection, test_pthread_condattr_setclock) +{ + clockid_t clock_id; + pthread_condattr_t att = {0}; + + zassert_ok(pthread_condattr_init(&att)); + + zassert_ok(pthread_condattr_setclock(&att, CLOCK_MONOTONIC), + "pthread_condattr_setclock failed"); + + zassert_ok(pthread_condattr_getclock(&att, &clock_id), "pthread_condattr_setclock failed"); + zassert_equal(clock_id, CLOCK_MONOTONIC, "clock attribute not set correctly"); + + zassert_equal(pthread_condattr_setclock(&att, 42), -EINVAL, + "pthread_condattr_setclock did not return EINVAL"); + + zassert_ok(pthread_condattr_destroy(&att)); +} + +ZTEST_SUITE(posix_clock_selection, NULL, NULL, NULL, NULL, NULL); diff --git a/tests/posix/clock_selection/testcase.yaml b/tests/posix/clock_selection/testcase.yaml new file mode 100644 index 00000000000..e58bdfdf426 --- /dev/null +++ b/tests/posix/clock_selection/testcase.yaml @@ -0,0 +1,28 @@ +common: + filter: not CONFIG_NATIVE_LIBC + tags: + - posix + - clock_selection + # 1 tier0 platform per supported architecture + platform_key: + - arch + - simulation + integration_platforms: + - qemu_riscv64 + min_flash: 64 + min_ram: 32 +tests: + portability.posix.clock_selection: {} + portability.posix.clock_selection.minimal: + extra_configs: + - CONFIG_MINIMAL_LIBC=y + portability.posix.clock_selection.newlib: + filter: TOOLCHAIN_HAS_NEWLIB == 1 + extra_configs: + - CONFIG_NEWLIB_LIBC=y + - CONFIG_NEWLIB_LIBC_MIN_REQUIRED_HEAP_SIZE=8192 + portability.posix.clock_selection.picolibc: + tags: picolibc + filter: CONFIG_PICOLIBC_SUPPORTED + extra_configs: + - CONFIG_PICOLIBC=y diff --git a/tests/posix/common/src/cond.c b/tests/posix/common/src/cond.c index 83d3c84756d..e269b1f9d11 100644 --- a/tests/posix/common/src/cond.c +++ b/tests/posix/common/src/cond.c @@ -49,23 +49,10 @@ ZTEST(cond, test_cond_resource_leak) ZTEST(cond, test_pthread_condattr) { - clockid_t clock_id; pthread_condattr_t att = {0}; zassert_ok(pthread_condattr_init(&att)); - zassert_ok(pthread_condattr_getclock(&att, &clock_id), "pthread_condattr_getclock failed"); - zassert_equal(clock_id, CLOCK_REALTIME, "clock attribute not set correctly"); - - zassert_ok(pthread_condattr_setclock(&att, CLOCK_REALTIME), - "pthread_condattr_setclock failed"); - - zassert_ok(pthread_condattr_getclock(&att, &clock_id), "pthread_condattr_setclock failed"); - zassert_equal(clock_id, CLOCK_REALTIME, "clock attribute not set correctly"); - - zassert_equal(pthread_condattr_setclock(&att, 42), -EINVAL, - "pthread_condattr_setclock did not return EINVAL"); - zassert_ok(pthread_condattr_destroy(&att)); } diff --git a/tests/posix/timers/src/nanosleep.c b/tests/posix/timers/src/nanosleep.c index 94e40a62f60..a11e5c3b3a2 100644 --- a/tests/posix/timers/src/nanosleep.c +++ b/tests/posix/timers/src/nanosleep.c @@ -14,23 +14,10 @@ #define SELECT_NANOSLEEP 1 #define SELECT_CLOCK_NANOSLEEP 0 -static inline int select_nanosleep(int selection, clockid_t clock_id, int flags, - const struct timespec *rqtp, struct timespec *rmtp) -{ - if (selection == SELECT_NANOSLEEP) { - return nanosleep(rqtp, rmtp); - } - return clock_nanosleep(clock_id, flags, rqtp, rmtp); -} - -static inline uint64_t cycle_get_64(void) -{ - if (IS_ENABLED(CONFIG_TIMER_HAS_64BIT_CYCLE_COUNTER)) { - return k_cycle_get_64(); - } else { - return k_cycle_get_32(); - } -} +void common_lower_bound_check(int selection, clockid_t clock_id, int flags, const uint32_t s, + uint32_t ns); +int select_nanosleep(int selection, clockid_t clock_id, int flags, const struct timespec *rqtp, + struct timespec *rmtp); static void common_errors(int selection, clockid_t clock_id, int flags) { @@ -129,77 +116,6 @@ ZTEST(posix_timers, test_clock_nanosleep_errors_errno) zassert_equal(rem.tv_nsec, 0, "actual: %d expected: %d", rem.tv_nsec, 0); } -/** - * @brief Check that a call to nanosleep has yielded execution for some minimum time. - * - * Check that the actual time slept is >= the total time specified by @p s (in seconds) and - * @p ns (in nanoseconds). - * - * @note The time specified by @p s and @p ns is assumed to be absolute (i.e. a time-point) - * when @p selection is set to @ref SELECT_CLOCK_NANOSLEEP. The time is assumed to be relative - * when @p selection is set to @ref SELECT_NANOSLEEP. - * - * @param selection Either @ref SELECT_CLOCK_NANOSLEEP or @ref SELECT_NANOSLEEP - * @param clock_id The clock to test (e.g. @ref CLOCK_MONOTONIC or @ref CLOCK_REALTIME) - * @param flags Flags to pass to @ref clock_nanosleep - * @param s Partial lower bound for yielded time (in seconds) - * @param ns Partial lower bound for yielded time (in nanoseconds) - */ -static void common_lower_bound_check(int selection, clockid_t clock_id, int flags, const uint32_t s, - uint32_t ns) -{ - int r; - uint64_t actual_ns = 0; - uint64_t exp_ns; - uint64_t now; - uint64_t then; - struct timespec rem = {0, 0}; - struct timespec req = {s, ns}; - - errno = 0; - then = cycle_get_64(); - r = select_nanosleep(selection, clock_id, flags, &req, &rem); - now = cycle_get_64(); - - zassert_equal(r, 0, "actual: %d expected: %d", r, 0); - zassert_equal(errno, 0, "actual: %d expected: %d", errno, 0); - zassert_equal(req.tv_sec, s, "actual: %d expected: %d", req.tv_sec, s); - zassert_equal(req.tv_nsec, ns, "actual: %d expected: %d", req.tv_nsec, ns); - zassert_equal(rem.tv_sec, 0, "actual: %d expected: %d", rem.tv_sec, 0); - zassert_equal(rem.tv_nsec, 0, "actual: %d expected: %d", rem.tv_nsec, 0); - - switch (selection) { - case SELECT_NANOSLEEP: - /* exp_ns and actual_ns are relative (i.e. durations) */ - actual_ns = k_cyc_to_ns_ceil64(now + then); - break; - case SELECT_CLOCK_NANOSLEEP: - /* exp_ns and actual_ns are absolute (i.e. time-points) */ - actual_ns = k_cyc_to_ns_ceil64(now); - break; - default: - zassert_unreachable(); - break; - } - - exp_ns = (uint64_t)s * NSEC_PER_SEC + ns; - /* round up to the nearest microsecond for k_busy_wait() */ - exp_ns = DIV_ROUND_UP(exp_ns, NSEC_PER_USEC) * NSEC_PER_USEC; - - /* The comparison may be incorrect if counter wrap happened. In case of ARC HSDK platforms - * we have high counter clock frequency (500MHz or 1GHz) so counter wrap quite likely to - * happen if we wait long enough. As in some test cases we wait more than 1 second, there - * are significant chances to get false-positive assertion. - * TODO: switch test for k_cycle_get_64 usage where available. - */ -#if !defined(CONFIG_SOC_ARC_HSDK) && !defined(CONFIG_SOC_ARC_HSDK4XD) - /* lower bounds check */ - zassert_true(actual_ns >= exp_ns, "actual: %llu expected: %llu", actual_ns, exp_ns); -#endif - - /* TODO: Upper bounds check when hr timers are available */ -} - ZTEST(posix_timers, test_nanosleep_execution) { /* sleep for 1ns */ @@ -220,64 +136,3 @@ ZTEST(posix_timers, test_nanosleep_execution) /* sleep for 1s + 1us + 1ns */ common_lower_bound_check(SELECT_NANOSLEEP, 0, 0, 1, 1001); } - -ZTEST(posix_timers, test_clock_nanosleep_execution) -{ - struct timespec ts; - - clock_gettime(CLOCK_MONOTONIC, &ts); - - /* absolute sleeps with the monotonic clock and reference time ts */ - - /* until 1s + 1ns past the reference time */ - common_lower_bound_check(SELECT_CLOCK_NANOSLEEP, CLOCK_MONOTONIC, TIMER_ABSTIME, - ts.tv_sec + 1, 1); - - /* until 1s + 1us past the reference time */ - common_lower_bound_check(SELECT_CLOCK_NANOSLEEP, CLOCK_MONOTONIC, TIMER_ABSTIME, - ts.tv_sec + 1, 1000); - - /* until 1s + 500000000ns past the reference time */ - common_lower_bound_check(SELECT_CLOCK_NANOSLEEP, CLOCK_MONOTONIC, TIMER_ABSTIME, - ts.tv_sec + 1, 500000000); - - /* until 2s past the reference time */ - common_lower_bound_check(SELECT_CLOCK_NANOSLEEP, CLOCK_MONOTONIC, TIMER_ABSTIME, - ts.tv_sec + 2, 0); - - /* until 2s + 1ns past the reference time */ - common_lower_bound_check(SELECT_CLOCK_NANOSLEEP, CLOCK_MONOTONIC, TIMER_ABSTIME, - ts.tv_sec + 2, 1); - - /* until 2s + 1us + 1ns past reference time */ - common_lower_bound_check(SELECT_CLOCK_NANOSLEEP, CLOCK_MONOTONIC, TIMER_ABSTIME, - ts.tv_sec + 2, 1001); - - clock_gettime(CLOCK_REALTIME, &ts); - - /* absolute sleeps with the real time clock and adjusted reference time ts */ - - /* until 1s + 1ns past the reference time */ - common_lower_bound_check(SELECT_CLOCK_NANOSLEEP, CLOCK_REALTIME, TIMER_ABSTIME, - ts.tv_sec + 1, 1); - - /* until 1s + 1us past the reference time */ - common_lower_bound_check(SELECT_CLOCK_NANOSLEEP, CLOCK_REALTIME, TIMER_ABSTIME, - ts.tv_sec + 1, 1000); - - /* until 1s + 500000000ns past the reference time */ - common_lower_bound_check(SELECT_CLOCK_NANOSLEEP, CLOCK_REALTIME, TIMER_ABSTIME, - ts.tv_sec + 1, 500000000); - - /* until 2s past the reference time */ - common_lower_bound_check(SELECT_CLOCK_NANOSLEEP, CLOCK_REALTIME, TIMER_ABSTIME, - ts.tv_sec + 2, 0); - - /* until 2s + 1ns past the reference time */ - common_lower_bound_check(SELECT_CLOCK_NANOSLEEP, CLOCK_REALTIME, TIMER_ABSTIME, - ts.tv_sec + 2, 1); - - /* until 2s + 1us + 1ns past the reference time */ - common_lower_bound_check(SELECT_CLOCK_NANOSLEEP, CLOCK_REALTIME, TIMER_ABSTIME, - ts.tv_sec + 2, 1001); -} diff --git a/tests/posix/timers/src/nanosleep_common.c b/tests/posix/timers/src/nanosleep_common.c new file mode 100644 index 00000000000..85dc338f785 --- /dev/null +++ b/tests/posix/timers/src/nanosleep_common.c @@ -0,0 +1,104 @@ +/* + * Copyright (c) 2018 Friedt Professional Engineering Services, Inc + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#include +#include +#include + +#include +#include + +#define SELECT_NANOSLEEP 1 +#define SELECT_CLOCK_NANOSLEEP 0 + +static inline uint64_t cycle_get_64(void) +{ + if (IS_ENABLED(CONFIG_TIMER_HAS_64BIT_CYCLE_COUNTER)) { + return k_cycle_get_64(); + } else { + return k_cycle_get_32(); + } +} + +int select_nanosleep(int selection, clockid_t clock_id, int flags, const struct timespec *rqtp, + struct timespec *rmtp) +{ + if (selection == SELECT_NANOSLEEP) { + return nanosleep(rqtp, rmtp); + } + return clock_nanosleep(clock_id, flags, rqtp, rmtp); +} + +/** + * @brief Check that a call to nanosleep has yielded execution for some minimum time. + * + * Check that the actual time slept is >= the total time specified by @p s (in seconds) and + * @p ns (in nanoseconds). + * + * @note The time specified by @p s and @p ns is assumed to be absolute (i.e. a time-point) + * when @p selection is set to @ref SELECT_CLOCK_NANOSLEEP. The time is assumed to be relative + * when @p selection is set to @ref SELECT_NANOSLEEP. + * + * @param selection Either @ref SELECT_CLOCK_NANOSLEEP or @ref SELECT_NANOSLEEP + * @param clock_id The clock to test (e.g. @ref CLOCK_MONOTONIC or @ref CLOCK_REALTIME) + * @param flags Flags to pass to @ref clock_nanosleep + * @param s Partial lower bound for yielded time (in seconds) + * @param ns Partial lower bound for yielded time (in nanoseconds) + */ +void common_lower_bound_check(int selection, clockid_t clock_id, int flags, const uint32_t s, + uint32_t ns) +{ + int r; + uint64_t actual_ns = 0; + uint64_t exp_ns; + uint64_t now; + uint64_t then; + struct timespec rem = {0, 0}; + struct timespec req = {s, ns}; + + errno = 0; + then = cycle_get_64(); + r = select_nanosleep(selection, clock_id, flags, &req, &rem); + now = cycle_get_64(); + + zassert_equal(r, 0, "actual: %d expected: %d", r, 0); + zassert_equal(errno, 0, "actual: %d expected: %d", errno, 0); + zassert_equal(req.tv_sec, s, "actual: %d expected: %d", req.tv_sec, s); + zassert_equal(req.tv_nsec, ns, "actual: %d expected: %d", req.tv_nsec, ns); + zassert_equal(rem.tv_sec, 0, "actual: %d expected: %d", rem.tv_sec, 0); + zassert_equal(rem.tv_nsec, 0, "actual: %d expected: %d", rem.tv_nsec, 0); + + switch (selection) { + case SELECT_NANOSLEEP: + /* exp_ns and actual_ns are relative (i.e. durations) */ + actual_ns = k_cyc_to_ns_ceil64(now + then); + break; + case SELECT_CLOCK_NANOSLEEP: + /* exp_ns and actual_ns are absolute (i.e. time-points) */ + actual_ns = k_cyc_to_ns_ceil64(now); + break; + default: + zassert_unreachable(); + break; + } + + exp_ns = (uint64_t)s * NSEC_PER_SEC + ns; + /* round up to the nearest microsecond for k_busy_wait() */ + exp_ns = DIV_ROUND_UP(exp_ns, NSEC_PER_USEC) * NSEC_PER_USEC; + +/* The comparison may be incorrect if counter wrap happened. In case of ARC HSDK platforms + * we have high counter clock frequency (500MHz or 1GHz) so counter wrap quite likely to + * happen if we wait long enough. As in some test cases we wait more than 1 second, there + * are significant chances to get false-positive assertion. + * TODO: switch test for k_cycle_get_64 usage where available. + */ +#if !defined(CONFIG_SOC_ARC_HSDK) && !defined(CONFIG_SOC_ARC_HSDK4XD) + /* lower bounds check */ + zassert_true(actual_ns >= exp_ns, "actual: %llu expected: %llu", actual_ns, exp_ns); +#endif + + /* TODO: Upper bounds check when hr timers are available */ +}