Make timer callback optional and fix timer logic
Make timer callback optional and fix wait bugs
This commit is contained in:
parent
087315b4c6
commit
3f886e075f
4 changed files with 55 additions and 38 deletions
|
@ -77,7 +77,12 @@ rcl_get_zero_initialized_timer(void);
|
||||||
* The period is a duration (rather an absolute time in the future).
|
* The period is a duration (rather an absolute time in the future).
|
||||||
* If the period is 0 then it will always be ready.
|
* If the period is 0 then it will always be ready.
|
||||||
*
|
*
|
||||||
* The callback must be a function which returns void and takes two arguments,
|
* The callback is an optional argument.
|
||||||
|
* Valid inputs are either a pointer to the function callback, or NULL to
|
||||||
|
* indicate that no callback will be stored in rcl.
|
||||||
|
* If the callback is null, the caller client library is responsible for
|
||||||
|
* firing the timer callback.
|
||||||
|
* Else, it must be a function which returns void and takes two arguments,
|
||||||
* the first being a pointer to the associated timer, and the second a uint64_t
|
* the first being a pointer to the associated timer, and the second a uint64_t
|
||||||
* which is the time since the previous call, or since the timer was created
|
* which is the time since the previous call, or since the timer was created
|
||||||
* if it is the first call to the callback.
|
* if it is the first call to the callback.
|
||||||
|
@ -148,6 +153,10 @@ rcl_timer_fini(rcl_timer_t * timer);
|
||||||
* the timer's period has not yet elapsed.
|
* the timer's period has not yet elapsed.
|
||||||
* It is up to the calling code to make sure the period has elapsed by first
|
* It is up to the calling code to make sure the period has elapsed by first
|
||||||
* calling rcl_timer_is_ready().
|
* calling rcl_timer_is_ready().
|
||||||
|
* If the callback pointer is null (either set in init or exchanged after
|
||||||
|
* initialized), no callback is fired.
|
||||||
|
* However, this function should still be called by the client library to
|
||||||
|
* update the state of the timer.
|
||||||
* The order of operations in this command are as follows:
|
* The order of operations in this command are as follows:
|
||||||
*
|
*
|
||||||
* - Ensure the timer has not been canceled.
|
* - Ensure the timer has not been canceled.
|
||||||
|
@ -326,7 +335,9 @@ rcl_timer_get_callback(const rcl_timer_t * timer);
|
||||||
/* This function can fail, and therefore return NULL, if:
|
/* This function can fail, and therefore return NULL, if:
|
||||||
* - timer is NULL
|
* - timer is NULL
|
||||||
* - timer has not been initialized (the implementation is invalid)
|
* - timer has not been initialized (the implementation is invalid)
|
||||||
* - the new_callback argument is NULL
|
*
|
||||||
|
* This function can set callback to null, in which case the callback is
|
||||||
|
* ignored when rcl_timer_call is called.
|
||||||
*
|
*
|
||||||
* This function is thread-safe.
|
* This function is thread-safe.
|
||||||
* This function is lock-free so long as the C11's stdatomic.h function
|
* This function is lock-free so long as the C11's stdatomic.h function
|
||||||
|
|
|
@ -186,7 +186,7 @@ rcl_wait_set_add_subscription(
|
||||||
/// Remove (sets to NULL) the subscriptions in the wait set.
|
/// Remove (sets to NULL) the subscriptions in the wait set.
|
||||||
/* This function should be used after passing using rcl_wait, but before
|
/* This function should be used after passing using rcl_wait, but before
|
||||||
* adding new subscriptions to the set.
|
* adding new subscriptions to the set.
|
||||||
* Sets all of the entries in the underlying rmw array to null, and sets the
|
* Sets all of the entries in the underlying rmw array to null, and sets the
|
||||||
* count in the rmw array to 0.
|
* count in the rmw array to 0.
|
||||||
*
|
*
|
||||||
* Calling this on an uninitialized (zero initialized) wait set will fail.
|
* Calling this on an uninitialized (zero initialized) wait set will fail.
|
||||||
|
|
|
@ -51,7 +51,6 @@ rcl_timer_init(
|
||||||
rcl_allocator_t allocator)
|
rcl_allocator_t allocator)
|
||||||
{
|
{
|
||||||
RCL_CHECK_ARGUMENT_FOR_NULL(timer, RCL_RET_INVALID_ARGUMENT);
|
RCL_CHECK_ARGUMENT_FOR_NULL(timer, RCL_RET_INVALID_ARGUMENT);
|
||||||
RCL_CHECK_ARGUMENT_FOR_NULL(callback, RCL_RET_INVALID_ARGUMENT);
|
|
||||||
if (timer->impl) {
|
if (timer->impl) {
|
||||||
RCL_SET_ERROR_MSG("timer already initailized, or memory was uninitialized");
|
RCL_SET_ERROR_MSG("timer already initailized, or memory was uninitialized");
|
||||||
return RCL_RET_ALREADY_INIT;
|
return RCL_RET_ALREADY_INIT;
|
||||||
|
@ -106,10 +105,13 @@ rcl_timer_call(rcl_timer_t * timer)
|
||||||
}
|
}
|
||||||
rcl_time_point_value_t previous_ns =
|
rcl_time_point_value_t previous_ns =
|
||||||
rcl_atomic_exchange_uint64_t(&timer->impl->last_call_time, now_steady);
|
rcl_atomic_exchange_uint64_t(&timer->impl->last_call_time, now_steady);
|
||||||
uint64_t since_last_call = now_steady - previous_ns;
|
|
||||||
rcl_timer_callback_t typed_callback =
|
rcl_timer_callback_t typed_callback =
|
||||||
(rcl_timer_callback_t)rcl_atomic_load_uintptr_t(&timer->impl->callback);
|
(rcl_timer_callback_t)rcl_atomic_load_uintptr_t(&timer->impl->callback);
|
||||||
typed_callback(timer, since_last_call);
|
|
||||||
|
if (typed_callback != NULL) {
|
||||||
|
uint64_t since_last_call = now_steady - previous_ns;
|
||||||
|
typed_callback(timer, since_last_call);
|
||||||
|
}
|
||||||
return RCL_RET_OK;
|
return RCL_RET_OK;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -195,7 +197,6 @@ rcl_timer_callback_t
|
||||||
rcl_timer_exchange_callback(rcl_timer_t * timer, const rcl_timer_callback_t new_callback)
|
rcl_timer_exchange_callback(rcl_timer_t * timer, const rcl_timer_callback_t new_callback)
|
||||||
{
|
{
|
||||||
RCL_CHECK_ARGUMENT_FOR_NULL(timer, NULL);
|
RCL_CHECK_ARGUMENT_FOR_NULL(timer, NULL);
|
||||||
RCL_CHECK_ARGUMENT_FOR_NULL(new_callback, NULL);
|
|
||||||
RCL_CHECK_FOR_NULL_WITH_MSG(timer->impl, "timer is invalid", return NULL);
|
RCL_CHECK_FOR_NULL_WITH_MSG(timer->impl, "timer is invalid", return NULL);
|
||||||
return (rcl_timer_callback_t)rcl_atomic_exchange_uintptr_t(
|
return (rcl_timer_callback_t)rcl_atomic_exchange_uintptr_t(
|
||||||
&timer->impl->callback, (uintptr_t)new_callback);
|
&timer->impl->callback, (uintptr_t)new_callback);
|
||||||
|
|
|
@ -512,44 +512,49 @@ rcl_wait(rcl_wait_set_t * wait_set, int64_t timeout)
|
||||||
return RCL_RET_WAIT_SET_EMPTY;
|
return RCL_RET_WAIT_SET_EMPTY;
|
||||||
}
|
}
|
||||||
// Calculate the timeout argument.
|
// Calculate the timeout argument.
|
||||||
rmw_time_t * timeout_argument;
|
// By default, set the timer to block indefinitely if none of the below conditions are met.
|
||||||
|
rmw_time_t * timeout_argument = NULL;
|
||||||
rmw_time_t temporary_timeout_storage;
|
rmw_time_t temporary_timeout_storage;
|
||||||
if (timeout < 0) {
|
|
||||||
// Pass NULL to wait to indicate block indefinitely.
|
|
||||||
timeout_argument = NULL;
|
|
||||||
}
|
|
||||||
if (timeout > 0) {
|
|
||||||
// Determine the nearest timeout (given or a timer).
|
|
||||||
uint64_t min_timeout = timeout;
|
|
||||||
// If min_timeout is > 0, then compare it to the time until each timer.
|
|
||||||
// Take the lowest and use that for the wait timeout.
|
|
||||||
if (min_timeout > 0) {
|
|
||||||
size_t i;
|
|
||||||
for (i = 0; i < wait_set->size_of_timers; ++i) {
|
|
||||||
if (!wait_set->timers[i]) {
|
|
||||||
continue; // Skip NULL timers.
|
|
||||||
}
|
|
||||||
int64_t timer_timeout;
|
|
||||||
rcl_ret_t ret = rcl_timer_get_time_until_next_call(wait_set->timers[i], &timer_timeout);
|
|
||||||
if (ret != RCL_RET_OK) {
|
|
||||||
return ret; // The rcl error state should already be set.
|
|
||||||
}
|
|
||||||
if ((uint64_t)timer_timeout < min_timeout) {
|
|
||||||
min_timeout = timer_timeout;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
// Set that in the temporary storage and point to that for the argument.
|
|
||||||
temporary_timeout_storage.sec = RCL_NS_TO_S(min_timeout);
|
|
||||||
temporary_timeout_storage.nsec = min_timeout % 1000000000;
|
|
||||||
timeout_argument = &temporary_timeout_storage;
|
|
||||||
}
|
|
||||||
if (timeout == 0) {
|
if (timeout == 0) {
|
||||||
// Then it is non-blocking, so set the temporary storage to 0, 0 and pass it.
|
// Then it is non-blocking, so set the temporary storage to 0, 0 and pass it.
|
||||||
temporary_timeout_storage.sec = 0;
|
temporary_timeout_storage.sec = 0;
|
||||||
temporary_timeout_storage.nsec = 0;
|
temporary_timeout_storage.nsec = 0;
|
||||||
timeout_argument = &temporary_timeout_storage;
|
timeout_argument = &temporary_timeout_storage;
|
||||||
|
} else {
|
||||||
|
int64_t min_timeout = INT64_MAX;
|
||||||
|
if (timeout > 0) {
|
||||||
|
// Compare the timeout to the time until next callback for each timer.
|
||||||
|
min_timeout = timeout;
|
||||||
|
}
|
||||||
|
// Take the lowest and use that for the wait timeout.
|
||||||
|
uint64_t i = 0;
|
||||||
|
for (i = 0; i < wait_set->size_of_timers; ++i) {
|
||||||
|
if (!wait_set->timers[i]) {
|
||||||
|
continue; // Skip NULL timers.
|
||||||
|
}
|
||||||
|
int64_t timer_timeout;
|
||||||
|
rcl_ret_t ret = rcl_timer_get_time_until_next_call(wait_set->timers[i], &timer_timeout);
|
||||||
|
if (ret != RCL_RET_OK) {
|
||||||
|
return ret; // The rcl error state should already be set.
|
||||||
|
}
|
||||||
|
if (timer_timeout < min_timeout) {
|
||||||
|
min_timeout = timer_timeout;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (min_timeout == INT64_MAX) {
|
||||||
|
timeout_argument = NULL;
|
||||||
|
} else {
|
||||||
|
// If min_timeout was negative, we need to wake up immediately.
|
||||||
|
if (min_timeout < 0) {
|
||||||
|
min_timeout = 0;
|
||||||
|
}
|
||||||
|
temporary_timeout_storage.sec = RCL_NS_TO_S(min_timeout);
|
||||||
|
temporary_timeout_storage.nsec = min_timeout % 1000000000;
|
||||||
|
timeout_argument = &temporary_timeout_storage;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Wait.
|
// Wait.
|
||||||
rmw_ret_t ret = rmw_wait(
|
rmw_ret_t ret = rmw_wait(
|
||||||
&wait_set->impl->rmw_subscriptions,
|
&wait_set->impl->rmw_subscriptions,
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue