27 #include "rcl/error_handling.h"
30 #include "rcutils/logging_macros.h"
31 #include "rcutils/macros.h"
32 #include "rcutils/stdatomic_helper.h"
33 #include "tracetools/tracetools.h"
34 #include "./timer_impl.h"
44 void _rcl_timer_time_jump(
57 RCUTILS_LOG_ERROR_NAMED(ROS_PACKAGE_NAME,
"Failed to get current time in jump callback");
66 const int64_t next_call_time = rcutils_atomic_load_int64_t(&impl->next_call_time);
67 rcutils_atomic_store(&impl->time_credit, next_call_time - now);
72 RCUTILS_LOG_ERROR_NAMED(ROS_PACKAGE_NAME,
"Failed to get current time in jump callback");
75 const int64_t last_call_time = rcutils_atomic_load_int64_t(&impl->last_call_time);
76 const int64_t next_call_time = rcutils_atomic_load_int64_t(&impl->next_call_time);
77 const int64_t period = rcutils_atomic_load_int64_t(&impl->period);
86 int64_t time_credit = rcutils_atomic_exchange_int64_t(&impl->time_credit, 0);
89 rcutils_atomic_store(&impl->next_call_time, now - time_credit + period);
90 rcutils_atomic_store(&impl->last_call_time, now - time_credit);
92 }
else if (next_call_time <= now) {
95 RCUTILS_LOG_ERROR_NAMED(
96 ROS_PACKAGE_NAME,
"Failed to get trigger guard condition in jump callback");
98 }
else if (now < last_call_time) {
101 rcutils_atomic_store(&impl->next_call_time, now + period);
102 rcutils_atomic_store(&impl->last_call_time, now);
122 RCL_SET_ERROR_MSG(
"timer period must be non-negative");
125 RCUTILS_LOG_DEBUG_NAMED(
126 ROS_PACKAGE_NAME,
"Initializing timer with period: %" PRIu64
"ns", period);
128 RCL_SET_ERROR_MSG(
"timer already initialized, or memory was uninitialized");
134 RCL_EXPECT_ERROR_IS_SET(now_ret);
139 impl.context = context;
147 atomic_init(&impl.callback, (uintptr_t)callback);
148 atomic_init(&impl.callback_data, (uintptr_t)NULL);
149 atomic_init(&impl.period, period);
150 atomic_init(&impl.time_credit, 0);
151 atomic_init(&impl.last_call_time, now);
152 atomic_init(&impl.next_call_time, now + period);
153 atomic_init(&impl.canceled, !autostart);
154 impl.allocator = allocator;
157 impl.reset_callback_data.on_reset_callback = NULL;
158 impl.reset_callback_data.user_data = NULL;
159 impl.reset_callback_data.reset_counter = 0;
160 impl.in_use_by_waitset =
false;
163 if (NULL == timer->
impl) {
166 RCUTILS_LOG_ERROR_NAMED(ROS_PACKAGE_NAME,
"Failed to fini guard condition after bad alloc");
171 RCUTILS_LOG_ERROR_NAMED(ROS_PACKAGE_NAME,
"Failed to remove callback after bad alloc");
175 RCL_SET_ERROR_MSG(
"allocating memory failed");
189 RCUTILS_LOG_ERROR_NAMED(
190 ROS_PACKAGE_NAME,
"Failed to fini guard condition after failing to add jump callback");
193 allocator.deallocate(timer->
impl, allocator.state);
200 TRACETOOLS_TRACEPOINT(rcl_timer_init, (
const void *)timer, period);
207 if (!timer || !timer->
impl) {
220 RCUTILS_LOG_ERROR_NAMED(ROS_PACKAGE_NAME,
"Failed to remove timer jump callback");
225 RCL_SET_ERROR_MSG(
"Failure to fini guard condition");
227 allocator.deallocate(timer->
impl, allocator.state);
238 *clock = timer->
impl->clock;
252 RCUTILS_LOG_DEBUG_NAMED(ROS_PACKAGE_NAME,
"Calling timer");
256 if (rcutils_atomic_load_bool(&timer->
impl->canceled)) {
257 RCL_SET_ERROR_MSG(
"timer is canceled");
263 RCL_EXPECT_ERROR_IS_SET(now_ret);
267 RCL_SET_ERROR_MSG(
"clock now returned negative time point value");
271 rcutils_atomic_exchange_int64_t(&timer->
impl->last_call_time, now);
275 int64_t next_call_time = rcutils_atomic_load_int64_t(&timer->
impl->next_call_time);
276 call_info->expected_call_time = next_call_time;
277 call_info->actual_call_time = now;
278 int64_t period = rcutils_atomic_load_int64_t(&timer->
impl->period);
282 next_call_time += period;
284 if (next_call_time <= now) {
287 next_call_time = now;
290 int64_t now_ahead = now - next_call_time;
292 int64_t periods_ahead = 1 + now_ahead / period;
293 next_call_time += periods_ahead * period;
296 rcutils_atomic_store(&timer->
impl->next_call_time, next_call_time);
298 if (typed_callback != NULL) {
299 int64_t since_last_call = now - previous_ns;
301 typed_callback(timer, since_last_call, callback_data);
312 int64_t time_until_next_call;
318 RCL_EXPECT_ERROR_IS_SET(ret);
321 *is_ready = (time_until_next_call <= 0);
332 if (rcutils_atomic_load_bool(&timer->
impl->canceled)) {
337 rcutils_atomic_load_int64_t(&timer->
impl->next_call_time);
347 if (rcutils_atomic_load_bool(&timer->
impl->canceled)) {
353 RCL_EXPECT_ERROR_IS_SET(ret);
356 *time_until_next_call =
357 rcutils_atomic_load_int64_t(&timer->
impl->next_call_time) - now;
372 RCL_EXPECT_ERROR_IS_SET(ret);
375 *time_since_last_call =
376 now - rcutils_atomic_load_int64_t(&timer->
impl->last_call_time);
386 *period = rcutils_atomic_load_int64_t(&timer->
impl->period);
398 *old_period = rcutils_atomic_exchange_int64_t(&timer->
impl->period, new_period);
399 RCUTILS_LOG_DEBUG_NAMED(
400 ROS_PACKAGE_NAME,
"Updated timer period from '%" PRIu64
"ns' to '%" PRIu64
"ns'",
401 *old_period, new_period);
408 RCL_CHECK_ARGUMENT_FOR_NULL(timer, NULL);
409 RCL_CHECK_FOR_NULL_WITH_MSG(timer->
impl,
"timer is invalid",
return NULL);
416 RCL_CHECK_ARGUMENT_FOR_NULL(timer, (uintptr_t)NULL);
417 RCL_CHECK_FOR_NULL_WITH_MSG(timer->
impl,
"timer is invalid", return (uintptr_t)NULL);
418 return (uintptr_t)rcutils_atomic_load_uintptr_t(&timer->
impl->callback_data);
424 RCUTILS_LOG_DEBUG_NAMED(ROS_PACKAGE_NAME,
"Updating timer callback");
425 RCL_CHECK_ARGUMENT_FOR_NULL(timer, NULL);
426 RCL_CHECK_FOR_NULL_WITH_MSG(timer->
impl,
"timer is invalid",
return NULL);
428 &timer->
impl->callback, (uintptr_t)new_callback);
437 return rcutils_atomic_exchange_uintptr_t(&timer->
impl->callback_data, data);
448 rcutils_atomic_store(&timer->
impl->canceled,
true);
449 RCUTILS_LOG_DEBUG_NAMED(ROS_PACKAGE_NAME,
"Timer canceled");
459 *is_canceled = rcutils_atomic_load_bool(&timer->
impl->canceled);
473 RCL_EXPECT_ERROR_IS_SET(now_ret);
476 int64_t period = rcutils_atomic_load_int64_t(&timer->
impl->period);
477 rcutils_atomic_store(&timer->
impl->next_call_time, now + period);
478 rcutils_atomic_store(&timer->
impl->canceled,
false);
483 if (cb_data->on_reset_callback) {
484 cb_data->on_reset_callback(cb_data->user_data, 1);
486 cb_data->reset_counter++;
490 RCUTILS_LOG_ERROR_NAMED(ROS_PACKAGE_NAME,
"Failed to trigger timer guard condition");
492 RCUTILS_LOG_DEBUG_NAMED(ROS_PACKAGE_NAME,
"Timer successfully reset");
499 RCL_CHECK_ARGUMENT_FOR_NULL(timer, NULL);
500 RCL_CHECK_FOR_NULL_WITH_MSG(timer->
impl,
"timer is invalid",
return NULL);
501 return &timer->
impl->allocator;
507 if (NULL == timer || NULL == timer->
impl || NULL == timer->
impl->guard_condition.
impl) {
510 return &timer->
impl->guard_condition;
516 rcl_event_callback_t on_reset_callback,
517 const void * user_data)
523 if (on_reset_callback) {
524 cb_data->on_reset_callback = on_reset_callback;
525 cb_data->user_data = user_data;
526 if (cb_data->reset_counter) {
527 cb_data->on_reset_callback(user_data, cb_data->reset_counter);
528 cb_data->reset_counter = 0;
531 cb_data->on_reset_callback = NULL;
532 cb_data->user_data = NULL;
#define RCL_CHECK_ALLOCATOR_WITH_MSG(allocator, msg, fail_statement)
Check that the given allocator is initialized, or fail with a message.
rcutils_allocator_t rcl_allocator_t
Encapsulation of an allocator.
RCL_PUBLIC RCL_WARN_UNUSED rcl_guard_condition_t rcl_get_zero_initialized_guard_condition(void)
Return a rcl_guard_condition_t struct with members set to NULL.
RCL_PUBLIC RCL_WARN_UNUSED rcl_ret_t rcl_trigger_guard_condition(rcl_guard_condition_t *guard_condition)
Trigger a rcl guard condition.
RCL_PUBLIC RCL_WARN_UNUSED rcl_ret_t rcl_guard_condition_init(rcl_guard_condition_t *guard_condition, rcl_context_t *context, const rcl_guard_condition_options_t options)
Initialize a rcl guard_condition.
RCL_PUBLIC RCL_WARN_UNUSED rcl_ret_t rcl_guard_condition_fini(rcl_guard_condition_t *guard_condition)
Finalize a rcl_guard_condition_t.
RCL_PUBLIC RCL_WARN_UNUSED rcl_guard_condition_options_t rcl_guard_condition_get_default_options(void)
Return the default options in a rcl_guard_condition_options_t struct.
Encapsulation of a time source.
rcl_clock_type_t type
Clock type.
Encapsulates the non-global state of an init/shutdown cycle.
rcl_duration_value_t nanoseconds
Duration in nanoseconds and its source.
Options available for a rcl guard condition.
Handle for a rcl guard condition.
rcl_guard_condition_impl_t * impl
Pointer to the guard condition implementation.
Describe the prerequisites for calling a time jump callback.
rcl_duration_t min_forward
bool on_clock_change
True to call callback when the clock type changes.
rcl_duration_t min_backward
Struct to describe a jump in time.
rcl_clock_change_t clock_change
Indicate whether or not the source of time changed.
Structure which encapsulates timer information when called.
Structure which encapsulates the on reset callback data.
Structure which encapsulates a ROS Timer.
rcl_timer_impl_t * impl
Private implementation pointer.
RCL_PUBLIC RCL_WARN_UNUSED rcl_ret_t rcl_clock_get_now(rcl_clock_t *clock, rcl_time_point_value_t *time_point_value)
Fill the time point value with the current value of the associated clock.
@ RCL_ROS_TIME_DEACTIVATED
The source switched to SYSTEM_TIME from ROS_TIME.
@ RCL_ROS_TIME_ACTIVATED
The source switched to ROS_TIME from SYSTEM_TIME.
rcutils_time_point_value_t rcl_time_point_value_t
A single point in time, measured in nanoseconds since the Unix epoch.
RCL_PUBLIC RCL_WARN_UNUSED rcl_ret_t rcl_clock_add_jump_callback(rcl_clock_t *clock, rcl_jump_threshold_t threshold, rcl_jump_callback_t callback, void *user_data)
Add a callback to be called when a time jump exceeds a threshold.
RCL_PUBLIC RCL_WARN_UNUSED rcl_ret_t rcl_clock_remove_jump_callback(rcl_clock_t *clock, rcl_jump_callback_t callback, void *user_data)
Remove a previously added time jump callback.
@ RCL_ROS_TIME
Use ROS time.
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 when the next call to rcl_timer_call() shall occur.
RCL_PUBLIC RCL_WARN_UNUSED rcl_ret_t rcl_timer_get_time_since_last_call(const rcl_timer_t *timer, int64_t *time_since_last_call)
Retrieve the time since the previous call to rcl_timer_call() occurred.
RCL_PUBLIC RCL_WARN_UNUSED rcl_ret_t rcl_timer_get_time_until_next_call(const rcl_timer_t *timer, int64_t *time_until_next_call)
Calculate and retrieve the time until the next call in nanoseconds.
RCL_PUBLIC RCL_WARN_UNUSED rcl_ret_t rcl_timer_call(rcl_timer_t *timer)
Call the timer's callback and set the last call time.
void(* rcl_timer_callback_t)(rcl_timer_t *, int64_t, uintptr_t)
User callback signature for timers.
RCL_PUBLIC RCL_WARN_UNUSED rcl_ret_t rcl_timer_reset(rcl_timer_t *timer)
Reset a timer.
RCL_PUBLIC RCL_WARN_UNUSED rcl_ret_t rcl_timer_get_period(const rcl_timer_t *timer, int64_t *period)
Retrieve the period of the timer.
RCL_PUBLIC RCL_WARN_UNUSED rcl_ret_t rcl_timer_set_on_reset_callback(const rcl_timer_t *timer, rcl_event_callback_t on_reset_callback, const void *user_data)
Set the on reset callback function for the timer.
RCL_PUBLIC RCL_WARN_UNUSED rcl_ret_t rcl_timer_exchange_period(const rcl_timer_t *timer, int64_t new_period, int64_t *old_period)
Exchange the period of the timer and return the previous period.
RCL_PUBLIC RCL_WARN_UNUSED rcl_timer_callback_t rcl_timer_get_callback(const rcl_timer_t *timer)
Return the current timer callback.
RCL_PUBLIC RCL_WARN_UNUSED rcl_timer_callback_t rcl_timer_exchange_callback(rcl_timer_t *timer, const rcl_timer_callback_t new_callback)
Exchange the current timer callback and return the current callback.
RCL_PUBLIC RCL_WARN_UNUSED rcl_ret_t rcl_timer_init2(rcl_timer_t *timer, rcl_clock_t *clock, rcl_context_t *context, int64_t period, const rcl_timer_callback_t callback, rcl_allocator_t allocator, bool autostart)
Initialize a timer.
RCL_PUBLIC RCL_WARN_UNUSED rcl_ret_t rcl_timer_cancel(rcl_timer_t *timer)
Cancel a timer.
RCL_PUBLIC RCL_WARN_UNUSED uintptr_t rcl_timer_get_callback_data(const rcl_timer_t *timer)
Return the current timer callback data.
RCL_PUBLIC RCL_WARN_UNUSED rcl_guard_condition_t * rcl_timer_get_guard_condition(const rcl_timer_t *timer)
Retrieve a guard condition used by the timer to wake the waitset when using ROSTime.
RCL_PUBLIC RCL_WARN_UNUSED rcl_ret_t rcl_timer_clock(const rcl_timer_t *timer, rcl_clock_t **clock)
Retrieve the clock of the timer.
RCL_PUBLIC RCL_WARN_UNUSED uintptr_t rcl_timer_exchange_callback_data(rcl_timer_t *timer, uintptr_t data)
Set the type erased data that the timer callback will be called with.
RCL_PUBLIC RCL_WARN_UNUSED rcl_ret_t rcl_timer_call_with_info(rcl_timer_t *timer, rcl_timer_call_info_t *call_info)
Same as rcl_timer_call() except that it also retrieves the actual and expected call time.
RCL_PUBLIC RCL_WARN_UNUSED rcl_timer_t rcl_get_zero_initialized_timer(void)
Return a zero initialized timer.
RCL_PUBLIC RCL_WARN_UNUSED rcl_ret_t rcl_timer_fini(rcl_timer_t *timer)
Finalize a timer.
RCL_PUBLIC RCL_WARN_UNUSED rcl_ret_t rcl_timer_is_canceled(const rcl_timer_t *timer, bool *is_canceled)
Retrieve the canceled state of a timer.
RCL_PUBLIC RCL_WARN_UNUSED rcl_ret_t rcl_timer_is_ready(const rcl_timer_t *timer, bool *is_ready)
Calculates whether or not the timer should be called.
RCL_PUBLIC RCL_WARN_UNUSED const rcl_allocator_t * rcl_timer_get_allocator(const rcl_timer_t *timer)
Return the allocator for the timer.
#define RCL_RET_TIMER_INVALID
Invalid rcl_timer_t given return code.
#define RCL_RET_ALREADY_INIT
rcl_init() already called return code.
#define RCL_RET_OK
Success return code.
#define RCL_RET_BAD_ALLOC
Failed to allocate memory return code.
#define RCL_RET_INVALID_ARGUMENT
Invalid argument return code.
#define RCL_RET_ERROR
Unspecified error return code.
#define RCL_RET_TIMER_CANCELED
Given timer was canceled return code.
rmw_ret_t rcl_ret_t
The type that holds an rcl return code.