Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

thread: add cnd_timedwait() #736

Merged
merged 4 commits into from
Mar 20, 2023
Merged
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
13 changes: 13 additions & 0 deletions include/re_thread.h
Original file line number Diff line number Diff line change
Expand Up @@ -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.
Expand Down
1 change: 1 addition & 0 deletions include/re_tmr.h
Original file line number Diff line number Diff line change
Expand Up @@ -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);
Expand Down
13 changes: 13 additions & 0 deletions src/thread/posix.c
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand Down
30 changes: 30 additions & 0 deletions src/thread/win32.c
Original file line number Diff line number Diff line change
Expand Up @@ -4,9 +4,12 @@
* Copyright (C) 2022 Sebastian Reimers
*/

#include <time.h>
#include <process.h>
#include <re_types.h>
#include <re_mem.h>
#include <re_list.h>
#include <re_tmr.h>
#include <re_thread.h>


Expand Down Expand Up @@ -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)
Expand Down
39 changes: 37 additions & 2 deletions src/tmr/tmr.c
Original file line number Diff line number Diff line change
Expand Up @@ -7,9 +7,7 @@
#ifdef HAVE_SYS_TIME_H
#include <sys/time.h>
#endif
#ifndef WIN32
#include <time.h>
#endif
#include <re_types.h>
#include <re_list.h>
#include <re_fmt.h>
Expand Down Expand Up @@ -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
*
Expand Down
1 change: 1 addition & 0 deletions test/test.c
Original file line number Diff line number Diff line change
Expand Up @@ -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),
};


Expand Down
1 change: 1 addition & 0 deletions test/test.h
Original file line number Diff line number Diff line change
Expand Up @@ -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);
Expand Down
38 changes: 38 additions & 0 deletions test/thread.c
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
* Copyright (C) 2022 Sebastian Reimers
*/

#include <time.h>
#include <re.h>
#include "test.h"

Expand Down Expand Up @@ -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;
}