2323
2424#include " rclcpp/duration.hpp"
2525#include " rclcpp/node_interfaces/get_node_base_interface.hpp"
26- #include " rclcpp/node_interfaces/get_node_clock_interface.hpp"
2726#include " rclcpp/node_interfaces/get_node_timers_interface.hpp"
2827#include " rclcpp/node_interfaces/node_base_interface.hpp"
29- #include " rclcpp/node_interfaces/node_clock_interface.hpp"
3028#include " rclcpp/node_interfaces/node_timers_interface.hpp"
3129
3230namespace rclcpp
3331{
34- namespace detail
35- {
36- // / Perform a safe cast to a timer period in nanoseconds
37- /* *
38- *
39- * \tparam DurationRepT
40- * \tparam DurationT
41- * \param period period to execute callback. This duration must be 0 <= period < nanoseconds::max()
42- * \return period, expressed as chrono::duration::nanoseconds
43- * \throws std::invalid_argument if period is negative or too large
44- */
45- template <typename DurationRepT, typename DurationT>
46- std::chrono::nanoseconds
47- safe_cast_to_period_in_ns (std::chrono::duration<DurationRepT, DurationT> period)
48- {
49- if (period < std::chrono::duration<DurationRepT, DurationT>::zero ()) {
50- throw std::invalid_argument{" timer period cannot be negative" };
51- }
52-
53- // Casting to a double representation might lose precision and allow the check below to succeed
54- // but the actual cast to nanoseconds fail. Using 1 DurationT worth of nanoseconds less than max.
55- constexpr auto maximum_safe_cast_ns =
56- std::chrono::nanoseconds::max () - std::chrono::duration<DurationRepT, DurationT>(1 );
57-
58- // If period is greater than nanoseconds::max(), the duration_cast to nanoseconds will overflow
59- // a signed integer, which is undefined behavior. Checking whether any std::chrono::duration is
60- // greater than nanoseconds::max() is a difficult general problem. This is a more conservative
61- // version of Howard Hinnant's (the <chrono> guy>) response here:
62- // https://stackoverflow.com/a/44637334/2089061
63- // However, this doesn't solve the issue for all possible duration types of period.
64- // Follow-up issue: https://github.com/ros2/rclcpp/issues/1177
65- constexpr auto ns_max_as_double =
66- std::chrono::duration_cast<std::chrono::duration<double , std::chrono::nanoseconds::period>>(
67- maximum_safe_cast_ns);
68- if (period > ns_max_as_double) {
69- throw std::invalid_argument{
70- " timer period must be less than std::chrono::nanoseconds::max()" };
71- }
72-
73- const auto period_ns = std::chrono::duration_cast<std::chrono::nanoseconds>(period);
74- if (period_ns < std::chrono::nanoseconds::zero ()) {
75- throw std::runtime_error{
76- " Casting timer period to nanoseconds resulted in integer overflow." };
77- }
78-
79- return period_ns;
80- }
81- } // namespace detail
82-
8332// / Create a timer with a given clock
8433// / \internal
8534template <typename CallbackT>
@@ -92,13 +41,14 @@ create_timer(
9241 CallbackT && callback,
9342 rclcpp::CallbackGroup::SharedPtr group = nullptr )
9443{
95- return create_timer (
96- node_base.get (),
97- node_timers.get (),
44+ auto timer = rclcpp::GenericTimer<CallbackT>::make_shared (
9845 clock,
9946 period.to_chrono <std::chrono::nanoseconds>(),
10047 std::forward<CallbackT>(callback),
101- group);
48+ node_base->get_context ());
49+
50+ node_timers->add_timer (timer, group);
51+ return timer;
10252}
10353
10454// / Create a timer with a given clock
@@ -112,99 +62,82 @@ create_timer(
11262 rclcpp::CallbackGroup::SharedPtr group = nullptr )
11363{
11464 return create_timer (
65+ rclcpp::node_interfaces::get_node_base_interface (node),
66+ rclcpp::node_interfaces::get_node_timers_interface (node),
11567 clock,
116- period. to_chrono <std::chrono::nanoseconds>() ,
68+ period,
11769 std::forward<CallbackT>(callback),
118- group,
119- rclcpp::node_interfaces::get_node_base_interface (node).get (),
120- rclcpp::node_interfaces::get_node_timers_interface (node).get ());
70+ group);
12171}
12272
123- // / Convenience method to create a general timer with node resources.
73+ // / Convenience method to create a timer with node resources.
12474/* *
12575 *
12676 * \tparam DurationRepT
12777 * \tparam DurationT
12878 * \tparam CallbackT
129- * \param clock clock to be used
13079 * \param period period to execute callback. This duration must be 0 <= period < nanoseconds::max()
13180 * \param callback callback to execute via the timer period
132- * \param group callback group
133- * \param node_base node base interface
134- * \param node_timers node timer interface
135- * \return shared pointer to a generic timer
136- * \throws std::invalid_argument if either clock, node_base or node_timers
137- * are nullptr , or period is negative or too large
81+ * \param group
82+ * \param node_base
83+ * \param node_timers
84+ * \return
85+ * \throws std::invalid argument if either node_base or node_timers
86+ * are null , or period is negative or too large
13887 */
13988template <typename DurationRepT, typename DurationT, typename CallbackT>
140- typename rclcpp::GenericTimer<CallbackT>::SharedPtr
141- create_timer (
142- rclcpp::Clock::SharedPtr clock,
89+ typename rclcpp::WallTimer<CallbackT>::SharedPtr
90+ create_wall_timer (
14391 std::chrono::duration<DurationRepT, DurationT> period,
14492 CallbackT callback,
14593 rclcpp::CallbackGroup::SharedPtr group,
14694 node_interfaces::NodeBaseInterface * node_base,
14795 node_interfaces::NodeTimersInterface * node_timers)
14896{
149- if (clock == nullptr ) {
150- throw std::invalid_argument{" clock cannot be null" };
151- }
15297 if (node_base == nullptr ) {
15398 throw std::invalid_argument{" input node_base cannot be null" };
15499 }
100+
155101 if (node_timers == nullptr ) {
156102 throw std::invalid_argument{" input node_timers cannot be null" };
157103 }
158104
159- const std::chrono::nanoseconds period_ns = detail::safe_cast_to_period_in_ns (period);
105+ if (period < std::chrono::duration<DurationRepT, DurationT>::zero ()) {
106+ throw std::invalid_argument{" timer period cannot be negative" };
107+ }
160108
161- // Add a new generic timer.
162- auto timer = rclcpp::GenericTimer<CallbackT>::make_shared (
163- std::move (clock), period_ns, std::move (callback), node_base->get_context ());
164- node_timers->add_timer (timer, group);
165- return timer;
166- }
109+ // Casting to a double representation might lose precision and allow the check below to succeed
110+ // but the actual cast to nanoseconds fail. Using 1 DurationT worth of nanoseconds less than max.
111+ constexpr auto maximum_safe_cast_ns =
112+ std::chrono::nanoseconds::max () - std::chrono::duration<DurationRepT, DurationT>(1 );
167113
168- // / Convenience method to create a wall timer with node resources.
169- /* *
170- *
171- * \tparam DurationRepT
172- * \tparam DurationT
173- * \tparam CallbackT
174- * \param period period to execute callback. This duration must be 0 <= period < nanoseconds::max()
175- * \param callback callback to execute via the timer period
176- * \param group callback group
177- * \param node_base node base interface
178- * \param node_timers node timer interface
179- * \return shared pointer to a wall timer
180- * \throws std::invalid_argument if either node_base or node_timers
181- * are null, or period is negative or too large
182- */
183- template <typename DurationRepT, typename DurationT, typename CallbackT>
184- typename rclcpp::WallTimer<CallbackT>::SharedPtr
185- create_wall_timer (
186- std::chrono::duration<DurationRepT, DurationT> period,
187- CallbackT callback,
188- rclcpp::CallbackGroup::SharedPtr group,
189- node_interfaces::NodeBaseInterface * node_base,
190- node_interfaces::NodeTimersInterface * node_timers)
191- {
192- if (node_base == nullptr ) {
193- throw std::invalid_argument{" input node_base cannot be null" };
114+ // If period is greater than nanoseconds::max(), the duration_cast to nanoseconds will overflow
115+ // a signed integer, which is undefined behavior. Checking whether any std::chrono::duration is
116+ // greater than nanoseconds::max() is a difficult general problem. This is a more conservative
117+ // version of Howard Hinnant's (the <chrono> guy>) response here:
118+ // https://stackoverflow.com/a/44637334/2089061
119+ // However, this doesn't solve the issue for all possible duration types of period.
120+ // Follow-up issue: https://github.com/ros2/rclcpp/issues/1177
121+ constexpr auto ns_max_as_double =
122+ std::chrono::duration_cast<std::chrono::duration<double , std::chrono::nanoseconds::period>>(
123+ maximum_safe_cast_ns);
124+ if (period > ns_max_as_double) {
125+ throw std::invalid_argument{
126+ " timer period must be less than std::chrono::nanoseconds::max()" };
194127 }
195128
196- if (node_timers == nullptr ) {
197- throw std::invalid_argument{" input node_timers cannot be null" };
129+ const auto period_ns = std::chrono::duration_cast<std::chrono::nanoseconds>(period);
130+ if (period_ns < std::chrono::nanoseconds::zero ()) {
131+ throw std::runtime_error{
132+ " Casting timer period to nanoseconds resulted in integer overflow." };
198133 }
199134
200- const std::chrono::nanoseconds period_ns = detail::safe_cast_to_period_in_ns (period);
201-
202- // Add a new wall timer.
203135 auto timer = rclcpp::WallTimer<CallbackT>::make_shared (
204136 period_ns, std::move (callback), node_base->get_context ());
205137 node_timers->add_timer (timer, group);
206138 return timer;
207139}
140+
208141} // namespace rclcpp
209142
210143#endif // RCLCPP__CREATE_TIMER_HPP_
0 commit comments