From 00f4f97e44817ceea4e2e1dc0e33d06ab605c16e Mon Sep 17 00:00:00 2001 From: Nathan Hui <205866820+n8hui@users.noreply.github.com> Date: Wed, 2 Apr 2025 12:22:44 +1100 Subject: [PATCH 1/2] Added rcl_timer_get_next_call_time (#1146) (cherry-picked from commit 2e9549c0301a0d390eea7f3d2b9251626e03b376) Signed-off-by: Nathan Hui <205866820+n8hui@users.noreply.github.com> --- rcl/include/rcl/timer.h | 24 ++++++++++++++++++++++++ rcl/src/rcl/timer.c | 16 ++++++++++++++++ 2 files changed, 40 insertions(+) diff --git a/rcl/include/rcl/timer.h b/rcl/include/rcl/timer.h index ec08acf0b..f6fb4eabb 100644 --- a/rcl/include/rcl/timer.h +++ b/rcl/include/rcl/timer.h @@ -325,6 +325,30 @@ RCL_WARN_UNUSED rcl_ret_t rcl_timer_get_time_until_next_call(const rcl_timer_t * timer, int64_t * time_until_next_call); +/// Retrieve the time when the next call to rcl_timer_call() shall occur. +/** + *
+ * Attribute | Adherence + * ------------------ | ------------- + * Allocates Memory | No + * Thread-Safe | Yes + * Uses Atomics | Yes + * Lock-Free | Yes [1] + * [1] if `atomic_is_lock_free()` returns true for `atomic_int_least64_t` + * + * \param[in] timer the handle to the timer that is being queried + * \param[out] next_call_time the output variable for the result + * \return #RCL_RET_OK if the timer until next call was successfully calculated, or + * \return #RCL_RET_INVALID_ARGUMENT if any arguments are invalid, or + * \return #RCL_RET_TIMER_INVALID if the timer->impl is invalid, or + * \return #RCL_RET_TIMER_CANCELED if the timer is canceled, or + * \return #RCL_RET_ERROR an unspecified error occur. + */ +RCL_PUBLIC +RCL_WARN_UNUSED +rcl_ret_t +rcl_timer_get_next_call_time(const rcl_timer_t * timer, int64_t * next_call_time); + /// Retrieve the time since the previous call to rcl_timer_call() occurred. /** * This function calculates the time since the last call and copies it into diff --git a/rcl/src/rcl/timer.c b/rcl/src/rcl/timer.c index 77239fb39..a3bbe7d43 100644 --- a/rcl/src/rcl/timer.c +++ b/rcl/src/rcl/timer.c @@ -309,6 +309,22 @@ rcl_timer_is_ready(const rcl_timer_t * timer, bool * is_ready) return RCL_RET_OK; } +rcl_ret_t +rcl_timer_get_next_call_time(const rcl_timer_t * timer, int64_t * next_call_time) +{ + RCL_CHECK_ARGUMENT_FOR_NULL(timer, RCL_RET_INVALID_ARGUMENT); + RCL_CHECK_ARGUMENT_FOR_NULL(timer->impl, RCL_RET_TIMER_INVALID); + RCL_CHECK_ARGUMENT_FOR_NULL(next_call_time, RCL_RET_INVALID_ARGUMENT); + + if (rcutils_atomic_load_bool(&timer->impl->canceled)) { + return RCL_RET_TIMER_CANCELED; + } + + *next_call_time = + rcutils_atomic_load_int64_t(&timer->impl->next_call_time); + return RCL_RET_OK; +} + rcl_ret_t rcl_timer_get_time_until_next_call(const rcl_timer_t * timer, int64_t * time_until_next_call) { From 9ed12fcd214acb361e38d27a68894b273924f1d6 Mon Sep 17 00:00:00 2001 From: Nathan Hui <205866820+n8hui@users.noreply.github.com> Date: Sat, 21 Feb 2026 17:06:10 +1100 Subject: [PATCH 2/2] Add unit test for rcl_timer_get_next_call_time Signed-off-by: Nathan Hui <205866820+n8hui@users.noreply.github.com> --- rcl/test/rcl/test_timer.cpp | 26 ++++++++++++++++++++++++++ 1 file changed, 26 insertions(+) diff --git a/rcl/test/rcl/test_timer.cpp b/rcl/test/rcl/test_timer.cpp index 66ed38c1c..ebdfdad0a 100644 --- a/rcl/test/rcl/test_timer.cpp +++ b/rcl/test/rcl/test_timer.cpp @@ -497,6 +497,10 @@ TEST_F(TestTimerFixture, test_canceled_timer) { ret = rcl_timer_cancel(&timer); ASSERT_EQ(RCL_RET_OK, ret) << rcl_get_error_string().str; + int64_t next_call_time = 0; + ret = rcl_timer_get_next_call_time(&timer, &next_call_time); + EXPECT_EQ(RCL_RET_TIMER_CANCELED, ret) << rcl_get_error_string().str; + int64_t time_until_next_call = 0; ret = rcl_timer_get_time_until_next_call(&timer, &time_until_next_call); EXPECT_EQ(RCL_RET_TIMER_CANCELED, ret) << rcl_get_error_string().str; @@ -904,3 +908,25 @@ TEST_F(TestPreInitTimer, test_time_since_last_call) { ASSERT_EQ(RCL_RET_OK, rcl_timer_get_time_since_last_call(&timer, &time_sice_next_call_end)); EXPECT_GT(time_sice_next_call_end, time_sice_next_call_start); } + +TEST_F(TestPreInitTimer, test_next_call_time) { + int64_t next_call_time_start = 0; + int64_t next_call_time_end = 0; + times_called = 0; + + EXPECT_EQ(RCL_RET_INVALID_ARGUMENT, rcl_timer_get_next_call_time(nullptr, &next_call_time_start)); + rcl_reset_error(); + EXPECT_EQ(RCL_RET_INVALID_ARGUMENT, rcl_timer_get_next_call_time(&timer, nullptr)); + rcl_reset_error(); + + ASSERT_EQ(RCL_RET_OK, rcl_timer_get_next_call_time(&timer, &next_call_time_start)) << + rcl_get_error_string().str; + + // move the next call time one period forward + ASSERT_EQ(RCL_RET_OK, rcl_timer_call(&timer)) << rcl_get_error_string().str; + EXPECT_EQ(times_called, 1); + + ASSERT_EQ(RCL_RET_OK, rcl_timer_get_next_call_time(&timer, &next_call_time_end)) << + rcl_get_error_string().str; + EXPECT_EQ(RCL_S_TO_NS(1), next_call_time_end - next_call_time_start); +}