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);
+}