ROS 2 rclcpp + rcl - rolling  rolling-20536064
ROS 2 C++ Client Library with ROS Client Library
timer.cpp
1 // Copyright 2015 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 #include "rclcpp/timer.hpp"
16 
17 #include <chrono>
18 #include <string>
19 #include <memory>
20 
21 #include "rmw/impl/cpp/demangle.hpp"
22 
23 #include "rclcpp/contexts/default_context.hpp"
24 #include "rclcpp/detail/cpp_callback_trampoline.hpp"
25 #include "rclcpp/exceptions.hpp"
26 #include "rclcpp/logging.hpp"
27 #include "rcutils/logging_macros.h"
28 
29 using rclcpp::TimerBase;
30 
31 TimerBase::TimerBase(
32  rclcpp::Clock::SharedPtr clock,
33  std::chrono::nanoseconds period,
34  rclcpp::Context::SharedPtr context,
35  bool autostart)
36 : clock_(clock), timer_handle_(nullptr)
37 {
38  if (nullptr == context) {
39  context = rclcpp::contexts::get_global_default_context();
40  }
41 
42  auto rcl_context = context->get_rcl_context();
43 
44  timer_handle_ = std::shared_ptr<rcl_timer_t>(
45  new rcl_timer_t, [ = ](rcl_timer_t * timer) mutable
46  {
47  {
48  std::lock_guard<std::mutex> clock_guard(clock->get_clock_mutex());
49  if (rcl_timer_fini(timer) != RCL_RET_OK) {
50  RCUTILS_LOG_ERROR_NAMED(
51  "rclcpp",
52  "Failed to clean up rcl timer handle: %s", rcl_get_error_string().str);
53  rcl_reset_error();
54  }
55  }
56  delete timer;
57  // Captured shared pointers by copy, reset to make sure timer is finalized before clock
58  clock.reset();
59  rcl_context.reset();
60  });
61 
62  *timer_handle_.get() = rcl_get_zero_initialized_timer();
63 
64  rcl_clock_t * clock_handle = clock_->get_clock_handle();
65  {
66  std::lock_guard<std::mutex> clock_guard(clock_->get_clock_mutex());
68  timer_handle_.get(), clock_handle, rcl_context.get(), period.count(),
69  nullptr, rcl_get_default_allocator(), autostart);
70  if (ret != RCL_RET_OK) {
71  rclcpp::exceptions::throw_from_rcl_error(ret, "Couldn't initialize rcl timer handle");
72  }
73  }
74 }
75 
77 {
79 }
80 
81 void
83 {
84  rcl_ret_t ret = rcl_timer_cancel(timer_handle_.get());
85  if (ret != RCL_RET_OK) {
86  rclcpp::exceptions::throw_from_rcl_error(ret, "Couldn't cancel timer");
87  }
88 }
89 
90 bool
92 {
93  bool is_canceled = false;
94  rcl_ret_t ret = rcl_timer_is_canceled(timer_handle_.get(), &is_canceled);
95  if (ret != RCL_RET_OK) {
96  rclcpp::exceptions::throw_from_rcl_error(ret, "Couldn't get timer cancelled state");
97  }
98  return is_canceled;
99 }
100 
101 void
103 {
104  rcl_ret_t ret = RCL_RET_OK;
105  {
106  std::lock_guard<std::recursive_mutex> lock(callback_mutex_);
107  ret = rcl_timer_reset(timer_handle_.get());
108  }
109  if (ret != RCL_RET_OK) {
110  rclcpp::exceptions::throw_from_rcl_error(ret, "Couldn't reset timer");
111  }
112 }
113 
114 bool
116 {
117  bool ready = false;
118  rcl_ret_t ret = rcl_timer_is_ready(timer_handle_.get(), &ready);
119  if (ret != RCL_RET_OK) {
120  rclcpp::exceptions::throw_from_rcl_error(ret, "Failed to check timer");
121  }
122  return ready;
123 }
124 
125 std::chrono::nanoseconds
127 {
128  int64_t time_until_next_call = 0;
130  timer_handle_.get(), &time_until_next_call);
131  if (ret == RCL_RET_TIMER_CANCELED) {
132  return std::chrono::nanoseconds::max();
133  } else if (ret != RCL_RET_OK) {
134  rclcpp::exceptions::throw_from_rcl_error(ret, "Timer could not get time until next call");
135  }
136  return std::chrono::nanoseconds(time_until_next_call);
137 }
138 
139 std::shared_ptr<const rcl_timer_t>
140 TimerBase::get_timer_handle()
141 {
142  return timer_handle_;
143 }
144 
145 bool
147 {
148  return in_use_by_wait_set_.exchange(in_use_state);
149 }
150 
151 void
152 TimerBase::set_on_reset_callback(const std::function<void(size_t)> & callback)
153 {
154  if (!callback) {
155  throw std::invalid_argument(
156  "The callback passed to set_on_reset_callback "
157  "is not callable.");
158  }
159 
160  auto new_callback =
161  [callback, this](size_t reset_calls) {
162  try {
163  callback(reset_calls);
164  } catch (const std::exception & exception) {
165  RCLCPP_ERROR_STREAM(
166  rclcpp::get_logger("rclcpp"),
167  "rclcpp::TimerBase@" << this <<
168  " caught " << rmw::impl::cpp::demangle(exception) <<
169  " exception in user-provided callback for the 'on reset' callback: " <<
170  exception.what());
171  } catch (...) {
172  RCLCPP_ERROR_STREAM(
173  rclcpp::get_logger("rclcpp"),
174  "rclcpp::TimerBase@" << this <<
175  " caught unhandled exception in user-provided callback " <<
176  "for the 'on reset' callback");
177  }
178  };
179 
180  std::lock_guard<std::recursive_mutex> lock(callback_mutex_);
181 
182  // Set it temporarily to the new callback, while we replace the old one.
183  // This two-step setting, prevents a gap where the old std::function has
184  // been replaced but rcl hasn't been told about the new one yet.
186  rclcpp::detail::cpp_callback_trampoline<
187  decltype(new_callback), const void *, size_t>,
188  static_cast<const void *>(&new_callback));
189 
190  // Store the std::function to keep it in scope, also overwrites the existing one.
191  on_reset_callback_ = new_callback;
192 
193  // Set it again, now using the permanent storage.
195  rclcpp::detail::cpp_callback_trampoline<
196  decltype(on_reset_callback_), const void *, size_t>,
197  static_cast<const void *>(&on_reset_callback_));
198 }
199 
200 void
202 {
203  std::lock_guard<std::recursive_mutex> lock(callback_mutex_);
204 
205  if (on_reset_callback_) {
206  set_on_reset_callback(nullptr, nullptr);
207  on_reset_callback_ = nullptr;
208  }
209 }
210 void
211 TimerBase::set_on_reset_callback(rcl_event_callback_t callback, const void * user_data)
212 {
213  rcl_ret_t ret = rcl_timer_set_on_reset_callback(timer_handle_.get(), callback, user_data);
214 
215  if (ret != RCL_RET_OK) {
216  rclcpp::exceptions::throw_from_rcl_error(ret, "Failed to set timer on reset callback");
217  }
218 }
219 
220 const rclcpp::Clock::SharedPtr & TimerBase::get_clock() const
221 {
222  return clock_;
223 }
#define rcl_get_default_allocator
Return a properly initialized rcl_allocator_t with default values.
Definition: allocator.h:37
virtual RCLCPP_PUBLIC ~TimerBase()
TimerBase destructor.
Definition: timer.cpp:76
RCLCPP_PUBLIC const Clock::SharedPtr & get_clock() const
Returns the clock this timer uses.
Definition: timer.cpp:220
RCLCPP_PUBLIC void set_on_reset_callback(const std::function< void(size_t)> &callback)
Set a callback to be called when the timer is reset.
Definition: timer.cpp:152
RCLCPP_PUBLIC bool is_canceled()
Return the timer cancellation state.
Definition: timer.cpp:91
RCLCPP_PUBLIC void reset()
Reset the timer.
Definition: timer.cpp:102
RCLCPP_PUBLIC void clear_on_reset_callback()
Unset the callback registered for reset timer.
Definition: timer.cpp:201
RCLCPP_PUBLIC bool exchange_in_use_by_wait_set_state(bool in_use_state)
Exchange the "in use by wait set" state for this timer.
Definition: timer.cpp:146
RCLCPP_PUBLIC bool is_ready()
Check if the timer is ready to trigger the callback.
Definition: timer.cpp:115
RCLCPP_PUBLIC std::chrono::nanoseconds time_until_trigger()
Check how long the timer has until its next scheduled callback.
Definition: timer.cpp:126
RCLCPP_PUBLIC void cancel()
Cancel the timer.
Definition: timer.cpp:82
RCLCPP_PUBLIC Logger get_logger(const std::string &name)
Return a named logger.
Definition: logger.cpp:32
Encapsulation of a time source.
Definition: time.h:138
Structure which encapsulates a ROS Timer.
Definition: timer.h:41
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.
Definition: timer.c:337
RCL_PUBLIC RCL_WARN_UNUSED rcl_ret_t rcl_timer_reset(rcl_timer_t *timer)
Reset a timer.
Definition: timer.c:459
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.
Definition: timer.c:509
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.
Definition: timer.c:104
RCL_PUBLIC RCL_WARN_UNUSED rcl_ret_t rcl_timer_cancel(rcl_timer_t *timer)
Cancel a timer.
Definition: timer.c:436
RCL_PUBLIC RCL_WARN_UNUSED rcl_timer_t rcl_get_zero_initialized_timer(void)
Return a zero initialized timer.
Definition: timer.c:32
RCL_PUBLIC RCL_WARN_UNUSED rcl_ret_t rcl_timer_fini(rcl_timer_t *timer)
Finalize a timer.
Definition: timer.c:200
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.
Definition: timer.c:449
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.
Definition: timer.c:302
#define RCL_RET_OK
Success return code.
Definition: types.h:27
#define RCL_RET_TIMER_CANCELED
Given timer was canceled return code.
Definition: types.h:95
rmw_ret_t rcl_ret_t
The type that holds an rcl return code.
Definition: types.h:24