From dca0dcfc6a1e2974bacc4cf9ef8dead8c73b74d5 Mon Sep 17 00:00:00 2001 From: Sebastian Reimers Date: Mon, 13 Mar 2023 12:04:39 +0100 Subject: [PATCH 1/4] tmr: add tmr_timespec_get() --- include/re_tmr.h | 1 + src/tmr/tmr.c | 39 +++++++++++++++++++++++++++++++++++++-- 2 files changed, 38 insertions(+), 2 deletions(-) diff --git a/include/re_tmr.h b/include/re_tmr.h index f325c0fed..013d101df 100644 --- a/include/re_tmr.h +++ b/include/re_tmr.h @@ -32,6 +32,7 @@ void tmr_poll(struct tmrl *tmrl); uint64_t tmr_jiffies_usec(void); uint64_t tmr_jiffies(void); uint64_t tmr_jiffies_rt_usec(void); +int tmr_timespec_get(struct timespec *tp, uint64_t offset); uint64_t tmr_next_timeout(struct tmrl *tmrl); void tmr_debug(void); int tmr_status(struct re_printf *pf, void *unused); diff --git a/src/tmr/tmr.c b/src/tmr/tmr.c index d82fa905c..524cb2272 100644 --- a/src/tmr/tmr.c +++ b/src/tmr/tmr.c @@ -7,9 +7,7 @@ #ifdef HAVE_SYS_TIME_H #include #endif -#ifndef WIN32 #include -#endif #include #include #include @@ -253,6 +251,43 @@ uint64_t tmr_jiffies_rt_usec(void) } +/** + * Modifies the timespec object to current calendar time (TIME_UTC) + * + * @param tp Pointer to timespec object + * @param offset Offset in [ms] + * + * @return 0 if success, otherwise errorcode + */ +int tmr_timespec_get(struct timespec *tp, uint64_t offset) +{ + int err; + + if (!tp) + return EINVAL; + +#if defined(WIN32) && !defined(__MINGW32__) + err = (timespec_get(tp, TIME_UTC) == TIME_UTC) ? 0 : EINVAL; +#else + err = (clock_gettime(CLOCK_REALTIME, tp) == 0) ? 0 : errno; +#endif + + if (err) + return err; + + if (offset) { + tp->tv_sec += (offset / 1000); + tp->tv_nsec += ((offset * 1000000) % 1000000000LL); + while (tp->tv_nsec > 1000000000LL) { + tp->tv_sec += 1; + tp->tv_nsec -= 1000000000LL; + } + } + + return 0; +} + + /** * Get number of milliseconds until the next timer expires * From 011f10888e8eba707f6c243583ea75fde3befa9a Mon Sep 17 00:00:00 2001 From: Sebastian Reimers Date: Mon, 13 Mar 2023 11:05:29 +0100 Subject: [PATCH 2/4] thread/posix: add cnd_timedwait() --- include/re_thread.h | 13 +++++++++++++ src/thread/posix.c | 13 +++++++++++++ 2 files changed, 26 insertions(+) diff --git a/include/re_thread.h b/include/re_thread.h index 1b07f8192..82cb439b1 100644 --- a/include/re_thread.h +++ b/include/re_thread.h @@ -181,6 +181,19 @@ int cnd_broadcast(cnd_t *cnd); int cnd_wait(cnd_t *cnd, mtx_t *mtx); +/** + * Blocks on a condition variable with timeout (TIME_UTC based) + * + * @param cnd Pointer to condition variable + * @param mtx Lock mutex pointer + * @param abstime Pointer to timeout time + * + * @return thrd_success on success, thrd_timedout if the timeout time + * has been reached before the mutex is locked, otherwise thrd_error + */ +int cnd_timedwait(cnd_t *cnd, mtx_t *mtx, const struct timespec *abstime); + + /** * Destroys the condition variable pointed to by cnd. * If there are thrds waiting on cnd, the behavior is undefined. diff --git a/src/thread/posix.c b/src/thread/posix.c index d699e4e6d..7cc3822ff 100644 --- a/src/thread/posix.c +++ b/src/thread/posix.c @@ -129,6 +129,19 @@ int cnd_wait(cnd_t *cnd, mtx_t *mtx) } +int cnd_timedwait(cnd_t *cnd, mtx_t *mtx, const struct timespec *abstime) +{ + if (!cnd || !mtx || !abstime) + return thrd_error; + + int ret = pthread_cond_timedwait(cnd, mtx, abstime); + if (ret == ETIMEDOUT) + return thrd_timedout; + + return (ret == 0) ? thrd_success : thrd_error; +} + + void cnd_destroy(cnd_t *cnd) { if (!cnd) From 67d7340b92dd2061d2f353d8efa7131283c11a57 Mon Sep 17 00:00:00 2001 From: Sebastian Reimers Date: Mon, 13 Mar 2023 11:20:19 +0100 Subject: [PATCH 3/4] thread/win32: add cnd_timedwait() --- src/thread/win32.c | 30 ++++++++++++++++++++++++++++++ 1 file changed, 30 insertions(+) diff --git a/src/thread/win32.c b/src/thread/win32.c index 1cd16d402..f177e6534 100644 --- a/src/thread/win32.c +++ b/src/thread/win32.c @@ -4,9 +4,12 @@ * Copyright (C) 2022 Sebastian Reimers */ +#include #include #include #include +#include +#include #include @@ -227,6 +230,33 @@ int cnd_wait(cnd_t *cnd, mtx_t *mtx) } +static uint32_t abs2ms(const struct timespec *ts) +{ + struct timespec now; + if (!ts) + return 0; + + uint64_t abs_ms = (ts->tv_sec * 1000U) + (ts->tv_nsec / 1000000L); + + tmr_timespec_get(&now, 0); + uint64_t now_ms = (now.tv_sec * 1000U) + (now.tv_nsec / 1000000L); + + return (abs_ms > now_ms) ? (uint32_t)(abs_ms - now_ms) : 0; +} + + +int cnd_timedwait(cnd_t *cnd, mtx_t *mtx, const struct timespec *abstime) +{ + if (!cnd || !mtx || !abstime) + return thrd_error; + + if (SleepConditionVariableCS(cnd, mtx, abs2ms(abstime))) + return thrd_success; + + return (GetLastError() == ERROR_TIMEOUT) ? thrd_timedout : thrd_error; +} + + void cnd_destroy(cnd_t *cnd) { if (!cnd) From 2721b48e96e21a3bd5ef2efb561912cc82330de3 Mon Sep 17 00:00:00 2001 From: Sebastian Reimers Date: Mon, 13 Mar 2023 14:17:36 +0100 Subject: [PATCH 4/4] test: add test_thread_cnd_timedwait --- test/test.c | 1 + test/test.h | 1 + test/thread.c | 38 ++++++++++++++++++++++++++++++++++++++ 3 files changed, 40 insertions(+) diff --git a/test/test.c b/test/test.c index b57b97212..63826fd51 100644 --- a/test/test.c +++ b/test/test.c @@ -260,6 +260,7 @@ static const struct test tests_integration[] = { TEST(test_tmr_jiffies), TEST(test_tmr_jiffies_usec), TEST(test_turn_thread), + TEST(test_thread_cnd_timedwait), }; diff --git a/test/test.h b/test/test.h index 4bc6ca64f..058a5aa9c 100644 --- a/test/test.h +++ b/test/test.h @@ -324,6 +324,7 @@ int test_sys_getenv(void); int test_tcp(void); int test_telev(void); int test_thread(void); +int test_thread_cnd_timedwait(void); int test_tmr_jiffies(void); int test_tmr_jiffies_usec(void); int test_try_into(void); diff --git a/test/thread.c b/test/thread.c index c7bc09603..7f94e599f 100644 --- a/test/thread.c +++ b/test/thread.c @@ -4,6 +4,7 @@ * Copyright (C) 2022 Sebastian Reimers */ +#include #include #include "test.h" @@ -49,3 +50,40 @@ int test_thread(void) out: return err; } + + +int test_thread_cnd_timedwait(void) +{ + cnd_t cnd; + mtx_t mtx; + struct timespec tp; + int err = 0; + + cnd_init(&cnd); + mtx_init(&mtx, mtx_plain); + + err = tmr_timespec_get(&tp, 100); + TEST_ERR(err); + + mtx_lock(&mtx); + + uint64_t start = tmr_jiffies(); + int ret = cnd_timedwait(&cnd, &mtx, &tp); + TEST_EQUALS(thrd_timedout, ret); + uint64_t end = tmr_jiffies(); + + /* This tests can fail if a spurious wake-up occurs */ + if (end - start < 100) { + DEBUG_WARNING("cnd_timedwait: early wake-up!\n"); + goto out; + } + + if (end - start > 500) { + err = ETIMEDOUT; + TEST_ERR(err); + } + +out: + mtx_unlock(&mtx); + return err; +}