ROS 2 rclcpp + rcl - rolling  rolling-20536064
ROS 2 C++ Client Library with ROS Client Library
create_timer.hpp
1 // Copyright 2019 Open Source Robotics Foundation, Inc.
2 //
3 // Licensed under the Apache License, Version 2.0 (the "License");
4 // you may not use this file except in compliance with the License.
5 // You may obtain a copy of the License at
6 //
7 // http://www.apache.org/licenses/LICENSE-2.0
8 //
9 // Unless required by applicable law or agreed to in writing, software
10 // distributed under the License is distributed on an "AS IS" BASIS,
11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 // See the License for the specific language governing permissions and
13 // limitations under the License.
14 
15 #ifndef RCLCPP__CREATE_TIMER_HPP_
16 #define RCLCPP__CREATE_TIMER_HPP_
17 
18 #include <chrono>
19 #include <exception>
20 #include <memory>
21 #include <string>
22 #include <utility>
23 
24 #include "rclcpp/duration.hpp"
25 #include "rclcpp/node_interfaces/get_node_base_interface.hpp"
26 #include "rclcpp/node_interfaces/get_node_timers_interface.hpp"
27 #include "rclcpp/node_interfaces/node_base_interface.hpp"
28 #include "rclcpp/node_interfaces/node_clock_interface.hpp"
29 #include "rclcpp/node_interfaces/node_timers_interface.hpp"
30 
31 namespace rclcpp
32 {
33 namespace detail
34 {
36 
44 template<typename DurationRepT, typename DurationT>
45 std::chrono::nanoseconds
46 safe_cast_to_period_in_ns(std::chrono::duration<DurationRepT, DurationT> period)
47 {
48  if (period < std::chrono::duration<DurationRepT, DurationT>::zero()) {
49  throw std::invalid_argument{"timer period cannot be negative"};
50  }
51 
52  // Casting to a double representation might lose precision and allow the check below to succeed
53  // but the actual cast to nanoseconds fail. Using 1 DurationT worth of nanoseconds less than max.
54  constexpr auto maximum_safe_cast_ns =
55  std::chrono::nanoseconds::max() - std::chrono::duration<DurationRepT, DurationT>(1);
56 
57  // If period is greater than nanoseconds::max(), the duration_cast to nanoseconds will overflow
58  // a signed integer, which is undefined behavior. Checking whether any std::chrono::duration is
59  // greater than nanoseconds::max() is a difficult general problem. This is a more conservative
60  // version of Howard Hinnant's (the <chrono> guy>) response here:
61  // https://stackoverflow.com/a/44637334/2089061
62  // However, this doesn't solve the issue for all possible duration types of period.
63  // Follow-up issue: https://github.com/ros2/rclcpp/issues/1177
64  constexpr auto ns_max_as_double =
65  std::chrono::duration_cast<std::chrono::duration<double, std::chrono::nanoseconds::period>>(
66  maximum_safe_cast_ns);
67  if (period > ns_max_as_double) {
68  throw std::invalid_argument{
69  "timer period must be less than std::chrono::nanoseconds::max()"};
70  }
71 
72  const auto period_ns = std::chrono::duration_cast<std::chrono::nanoseconds>(period);
73  if (period_ns < std::chrono::nanoseconds::zero()) {
74  throw std::runtime_error{
75  "Casting timer period to nanoseconds resulted in integer overflow."};
76  }
77 
78  return period_ns;
79 }
80 } // namespace detail
81 
84 template<typename CallbackT>
85 typename rclcpp::TimerBase::SharedPtr
87  std::shared_ptr<node_interfaces::NodeBaseInterface> node_base,
88  std::shared_ptr<node_interfaces::NodeTimersInterface> node_timers,
89  rclcpp::Clock::SharedPtr clock,
90  rclcpp::Duration period,
91  CallbackT && callback,
92  rclcpp::CallbackGroup::SharedPtr group = nullptr,
93  bool autostart = true)
94 {
95  return create_timer(
96  clock,
97  period.to_chrono<std::chrono::nanoseconds>(),
98  std::forward<CallbackT>(callback),
99  group,
100  node_base.get(),
101  node_timers.get(),
102  autostart);
103 }
104 
106 template<typename NodeT, typename CallbackT>
107 typename rclcpp::TimerBase::SharedPtr
109  NodeT node,
110  rclcpp::Clock::SharedPtr clock,
111  rclcpp::Duration period,
112  CallbackT && callback,
113  rclcpp::CallbackGroup::SharedPtr group = nullptr,
114  bool autostart = true)
115 {
116  return create_timer(
117  clock,
118  period.to_chrono<std::chrono::nanoseconds>(),
119  std::forward<CallbackT>(callback),
120  group,
121  rclcpp::node_interfaces::get_node_base_interface(node).get(),
122  rclcpp::node_interfaces::get_node_timers_interface(node).get(),
123  autostart);
124 }
125 
127 
143 template<typename DurationRepT, typename DurationT, typename CallbackT>
146  rclcpp::Clock::SharedPtr clock,
147  std::chrono::duration<DurationRepT, DurationT> period,
148  CallbackT callback,
149  rclcpp::CallbackGroup::SharedPtr group,
152  bool autostart = true)
153 {
154  if (clock == nullptr) {
155  throw std::invalid_argument{"clock cannot be null"};
156  }
157  if (node_base == nullptr) {
158  throw std::invalid_argument{"input node_base cannot be null"};
159  }
160  if (node_timers == nullptr) {
161  throw std::invalid_argument{"input node_timers cannot be null"};
162  }
163 
164  const std::chrono::nanoseconds period_ns = detail::safe_cast_to_period_in_ns(period);
165 
166  // Add a new generic timer.
168  std::move(clock), period_ns, std::move(callback), node_base->get_context(), autostart);
169  node_timers->add_timer(timer, group);
170  return timer;
171 }
172 
174 
188 template<typename DurationRepT, typename DurationT, typename CallbackT>
191  std::chrono::duration<DurationRepT, DurationT> period,
192  CallbackT callback,
193  rclcpp::CallbackGroup::SharedPtr group,
196  bool autostart = true)
197 {
198  if (node_base == nullptr) {
199  throw std::invalid_argument{"input node_base cannot be null"};
200  }
201 
202  if (node_timers == nullptr) {
203  throw std::invalid_argument{"input node_timers cannot be null"};
204  }
205 
206  const std::chrono::nanoseconds period_ns = detail::safe_cast_to_period_in_ns(period);
207 
208  // Add a new wall timer.
210  period_ns, std::move(callback), node_base->get_context(), autostart);
211  node_timers->add_timer(timer, group);
212  return timer;
213 }
214 } // namespace rclcpp
215 
216 #endif // RCLCPP__CREATE_TIMER_HPP_
DurationT to_chrono() const
Convert Duration into a std::chrono::Duration.
Definition: duration.hpp:147
Generic timer. Periodically executes a user-specified callback.
Definition: timer.hpp:228
Pure virtual interface class for the NodeBase part of the Node API.
virtual RCLCPP_PUBLIC rclcpp::Context::SharedPtr get_context()=0
Return the context of the node.
Pure virtual interface class for the NodeTimers part of the Node API.
virtual RCLCPP_PUBLIC void add_timer(rclcpp::TimerBase::SharedPtr timer, rclcpp::CallbackGroup::SharedPtr callback_group)=0
Add a timer to the node.
Versions of rosidl_typesupport_cpp::get_message_type_support_handle that handle adapted types.
rclcpp::TimerBase::SharedPtr create_timer(std::shared_ptr< node_interfaces::NodeBaseInterface > node_base, std::shared_ptr< node_interfaces::NodeTimersInterface > node_timers, rclcpp::Clock::SharedPtr clock, rclcpp::Duration period, CallbackT &&callback, rclcpp::CallbackGroup::SharedPtr group=nullptr, bool autostart=true)
rclcpp::WallTimer< CallbackT >::SharedPtr create_wall_timer(std::chrono::duration< DurationRepT, DurationT > period, CallbackT callback, rclcpp::CallbackGroup::SharedPtr group, node_interfaces::NodeBaseInterface *node_base, node_interfaces::NodeTimersInterface *node_timers, bool autostart=true)
Convenience method to create a wall timer with node resources.