-
Notifications
You must be signed in to change notification settings - Fork 0
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Fixes for Timers #5
base: agalbachicar/basic_timer_api
Are you sure you want to change the base?
Fixes for Timers #5
Conversation
Signed-off-by: Michael X. Grey <greyxmike@gmail.com>
Signed-off-by: Michael X. Grey <greyxmike@gmail.com>
Signed-off-by: Michael X. Grey <greyxmike@gmail.com>
Signed-off-by: Michael X. Grey <greyxmike@gmail.com>
Signed-off-by: Michael X. Grey <greyxmike@gmail.com>
Signed-off-by: Michael X. Grey <greyxmike@gmail.com>
Signed-off-by: Michael X. Grey <greyxmike@gmail.com>
Before diving into this feedback and the code itself to assimilate it, just simply let me say thank you, @mxgrey 😄 |
rclrs/src/timer.rs
Outdated
Ok(()) | ||
} | ||
|
||
/// Updates the state of the rcl_timer to know that it has been called. This | ||
/// should only be called by [`Self::execute`]. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Minor, this should be [Self::call
] I think.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Good catch, those functions were refactored after I wrote that documentation. Fixed here: 464d5d9
Signed-off-by: Michael X. Grey <greyxmike@gmail.com>
Signed-off-by: Michael X. Grey <greyxmike@gmail.com>
Note that I've pushed c7c1cba which renames |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
LGTM
Sorry for the delay on this and happy new year @mxgrey. Year transitions are usually high on the workload and turbulences.
I appreciate a lot the effort put into this PR to educate us and get a way better timer implementation. I am dubious about moving forward with this PR provided that yours is a complete rewrite that follows the manners of the project and rust way more than ours. I think the team here would not hesitate to just let yours move forward and use this PR as a learning experience. Otherwise, we are happy to contribute on anything we can from your or any other maintainer review in the future to push till completion and merge.
Thanks for kicking off the effort on
Timers
for rclrs with ros2-rust#440Rather than write out a huge amount of feedback and leave it to you to interpret and implement it, I went ahead and cranked out a series of fixes that I recommend for your PR. I'll explain the various changes in a series of sections.
Fixing object lifecycles
The
test_new_with_source_clock
test was having a segmentation fault in your original PR. I didn't identify the specific cause of this, but the overarching issue was that the lifecycles ofrcl_timer_t
and possiblyrcl_clock_t
were not being managed in a way that's compatible withrcl
.This PR changes both of those objects to use in-place initialization. That means we first create the
Arc<Mutex<T>>
that will hold the data structure (zero-initializing it to start), and then we callrcl_[...]_init
on the object while the mutex is locked. This ensures that the address of the rcl object is never changed. Sometimesrcl
is sensitive to the address of its objects moving, other times it's not, but it never hurts to take precautions and keep the addresses fixed throughout the lifetimes of the objects.Secondly this PR introduces
TimerHandle
which guarantees that thercl_clock_t
being used by thercl_timer_t
cannot be dropped prematurely. In this new structure,rcl_timer_fini
is only called whenTimerHandle
is dropped, ensuring thatrcl_clock_fini
cannot be called until afterwards.Streamlining API
In the original PR there were some superfluous arguments being passed into
create_timer
. TheContext
is already known by theNode
so there's no reason for the user to pass that in. We can usually assume that the user will want a steady clock for their timer, so we shouldn't make it mandatory for them to pass in a clock.In this PR we change the
create_timer
API to useTimerOptions
to start off the user with a sensible set of default options, andIntoTimerOptions
to conveniently buildTimerOptions
off of aDuration
. This follows the same_Options
pattern that was introduced in ros2-rust#422 and which will be applied to all create functions with ros2-rust#429.One-shot callbacks
A widely desired feature which hasn't made it into rclcpp yet is one-shot timers. The ability to define a one-shot timer is especially important for
rclrs
since Rust draws a very important categorical difference betweenFnMut
andFnOnce
. If we do not have built-in support for one-shot timers then users can never useFnOnce
with their timers.I've generalized the definition of timer callbacks to also support one-shot timers (see
AnyTimerCallback
) and added APIs to help users with creating repeating timers and one-shot timers. Users can also change the callback of a timer whenever they want, as long as they have access to the&Timer
. This means after a one-shot callback has been triggered, the user can pass a new one-shot callback into the same timer, and that new callback will be called the next time the Timer is triggered. In fact the current one-shot callback itself can reload the timer with a new one-shot callback. Users can also switch between repeating, one-shot, or removed callback whenever they want.Callback flexibility
In the original PR users are expected to pass in a
Box<dyn Fn(i64) + ...>
as a callback, but most users will find it inconvenient to box their callback before providing it. They are also likely to be confused about what thei64
means.In this PR we introduce
trait TimerCallRepeatedly
andtrait TimerCallOnce
which allow users to directly pass in anFnMut(_)
andFnOnce(_)
for repeated and one-shot timers respectively. This is similar toSubscriptionCallback
which is used to make the subscription API very flexible.We support three different choices of arguments for these callbacks:
&Timer
: The callback will receive a borrow of the timer that it belongs to. The user can call any function on the timer, includingTime::set_callback
to change the callback of the Timer andTimer::last_elapsed
to check how much time elapsed since their callback was last called. OrTimer::clock
to get the clock for the timer and check the time.Time
: The callback will receive the current time of the clock that is being used by the timerWe could also support
Duration
which would provide the time lapsed since the last call to the callback. I'm happy to add that if anyone would find that interesting.Idiomatic API
In the original PR there was significant use of
i64
with function names that include_nanos
to signal to the user that they should interpret the integers as nanoseconds. This is not idiomatic for Rust. We should prefer rigid explicit types likeDuration
andTime
. This PR updates all rclrs API function arguments and fields to explicitly useTime
andDuration
, and not usei64
anywhere.