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