From c3ef5a333381e83e869c5f20d54ee318a5338790 Mon Sep 17 00:00:00 2001 From: Chris Murphy Date: Wed, 22 Apr 2020 13:17:11 -0400 Subject: [PATCH] master: Issue 390: Clarify documentation of qb_loop_timer_expire_time_get and provide new function to return previously documented behavior Includes unit test addition by chrissie-c --- include/qb/qbloop.h | 15 +++++++++++-- lib/loop_timerlist.c | 50 ++++++++++++++++++++++++++++++++++++++++++++ tests/check_loop.c | 19 +++++++++++++++++ 3 files changed, 82 insertions(+), 2 deletions(-) diff --git a/include/qb/qbloop.h b/include/qb/qbloop.h index db0c480d4..c9b1993d1 100644 --- a/include/qb/qbloop.h +++ b/include/qb/qbloop.h @@ -187,16 +187,27 @@ int32_t qb_loop_timer_del(qb_loop_t *l, qb_loop_timer_handle th); int32_t qb_loop_timer_is_running(qb_loop_t *l, qb_loop_timer_handle th); /** - * Get the time remaining before it expires. + * Get the expiration time of the timer, as set when the timer was created * * @note if the timer has already expired it will return 0 * * @param l pointer to the loop instance * @param th timer handle. - * @return nano seconds left + * @return nano seconds at which the timer will expire */ uint64_t qb_loop_timer_expire_time_get(struct qb_loop *l, qb_loop_timer_handle th); +/** + * Get the time remaining before the timer expires + * + * @note if the timer has already expired it will return 0 + * + * @param l pointer to the loop instance + * @param th timer handle. + * @return nano seconds remaining until the timer expires + */ +uint64_t qb_loop_timer_expire_time_remaining(struct qb_loop *l, qb_loop_timer_handle th); + /** * Set a callback to receive events on file descriptors * getting low. diff --git a/lib/loop_timerlist.c b/lib/loop_timerlist.c index 4102ea3d4..e5e3ae831 100644 --- a/lib/loop_timerlist.c +++ b/lib/loop_timerlist.c @@ -286,6 +286,56 @@ qb_loop_timer_expire_time_get(struct qb_loop * lp, qb_loop_timer_handle th) return timerlist_expire_time(&s->timerlist, t->timerlist_handle); } +uint64_t +qb_loop_timer_expire_time_remaining(struct qb_loop * lp, qb_loop_timer_handle th) +{ + + uint64_t current_ns; + /* NOTE: while it does not appear that absolute timers are used anywhere, + * we may as well respect this pattern in case that changes. + * Unfortunately, that means we do need to repeat timer fetch code from qb_loop_timer_expire_time_get + * rather than just a simple call to qb_loop_timer_expire_time_get and qb_util_nano_current_get. + */ + + struct qb_timer_source *s; + struct qb_loop_timer *t; + int32_t res; + struct qb_loop *l = lp; + + if (l == NULL) { + l = qb_loop_default_get(); + } + s = (struct qb_timer_source *)l->timer_source; + + res = _timer_from_handle_(s, th, &t); + if (res != 0) { + return 0; + } + + struct timerlist_timer *timer = (struct timerlist_timer *)t->timerlist_handle; + + + if (timer->is_absolute_timer) { + current_ns = qb_util_nano_from_epoch_get(); + } + else { + current_ns = qb_util_nano_current_get(); + } + uint64_t timer_ns = timerlist_expire_time(&s->timerlist, t->timerlist_handle); + /* since time estimation is racy by nature, I'll try to check the state late, + * and try to understand that no matter what, the timer might have expired in the mean time + */ + if (t->state != QB_POLL_ENTRY_ACTIVE) { + return 0; + } + if (timer_ns < current_ns) { + return 0; // respect the "expired" contract + } + return timer_ns - current_ns; + + +} + int32_t qb_loop_timer_is_running(qb_loop_t *l, qb_loop_timer_handle th) { diff --git a/tests/check_loop.c b/tests/check_loop.c index ac9bbbfeb..81cc2ba97 100644 --- a/tests/check_loop.c +++ b/tests/check_loop.c @@ -348,6 +348,22 @@ static Suite *loop_job_suite(void) * Timers */ static qb_loop_timer_handle test_th; +static qb_loop_timer_handle test_th2; + +static void check_time_left(void *data) +{ + qb_loop_t *l = (qb_loop_t *)data; + + /* NOTE: We are checking the 'stop_loop' timer here, not our own */ + uint64_t abs_time = qb_loop_timer_expire_time_get(l, test_th); + uint64_t rel_time = qb_loop_timer_expire_time_remaining(l, test_th); + + ck_assert(abs_time > 0ULL); + ck_assert(rel_time > 0ULL); + ck_assert(abs_time > rel_time); + ck_assert(rel_time <= 60*QB_TIME_NS_IN_MSEC); +} + START_TEST(test_loop_timer_input) { @@ -409,6 +425,9 @@ START_TEST(test_loop_timer_basic) res = qb_loop_timer_add(l, QB_LOOP_LOW, 7*QB_TIME_NS_IN_MSEC, l, reset_one_shot_tmo, &reset_th); ck_assert_int_eq(res, 0); + res = qb_loop_timer_add(l, QB_LOOP_HIGH, 20*QB_TIME_NS_IN_MSEC, l, check_time_left, &test_th2); + ck_assert_int_eq(res, 0); + res = qb_loop_timer_add(l, QB_LOOP_LOW, 60*QB_TIME_NS_IN_MSEC, l, job_stop, &test_th); ck_assert_int_eq(res, 0);