ROS 2 rclcpp + rcl - humble  humble
ROS 2 C++ Client Library with ROS Client Library
clock.cpp
1 // Copyright 2017 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/clock.hpp"
16 
17 #include <condition_variable>
18 #include <memory>
19 #include <thread>
20 
21 #include "rclcpp/exceptions.hpp"
22 #include "rclcpp/utilities.hpp"
23 
24 #include "rcpputils/scope_exit.hpp"
25 #include "rcutils/logging_macros.h"
26 
27 namespace rclcpp
28 {
29 
31 {
32 public:
33  explicit Impl(rcl_clock_type_t clock_type)
34  : allocator_{rcl_get_default_allocator()}
35  {
36  rcl_ret_t ret = rcl_clock_init(clock_type, &rcl_clock_, &allocator_);
37  if (ret != RCL_RET_OK) {
38  exceptions::throw_from_rcl_error(ret, "failed to initialize rcl clock");
39  }
40  }
41 
42  ~Impl()
43  {
44  rcl_ret_t ret = rcl_clock_fini(&rcl_clock_);
45  if (ret != RCL_RET_OK) {
46  RCUTILS_LOG_ERROR("Failed to fini rcl clock.");
47  }
48  }
49 
50  rcl_clock_t rcl_clock_;
51  rcl_allocator_t allocator_;
52  std::mutex clock_mutex_;
53 };
54 
55 JumpHandler::JumpHandler(
56  pre_callback_t pre_callback,
57  post_callback_t post_callback,
58  const rcl_jump_threshold_t & threshold)
59 : pre_callback(pre_callback),
60  post_callback(post_callback),
61  notice_threshold(threshold)
62 {}
63 
65 : impl_(new Clock::Impl(clock_type)) {}
66 
67 Clock::~Clock() {}
68 
69 Time
71 {
72  Time now(0, 0, impl_->rcl_clock_.type);
73 
74  auto ret = rcl_clock_get_now(&impl_->rcl_clock_, &now.rcl_time_.nanoseconds);
75  if (ret != RCL_RET_OK) {
76  exceptions::throw_from_rcl_error(ret, "could not get current time stamp");
77  }
78 
79  return now;
80 }
81 
82 bool
83 Clock::sleep_until(Time until, Context::SharedPtr context)
84 {
85  if (!context || !context->is_valid()) {
86  throw std::runtime_error("context cannot be slept with because it's invalid");
87  }
88  const auto this_clock_type = get_clock_type();
89  if (until.get_clock_type() != this_clock_type) {
90  throw std::runtime_error("until's clock type does not match this clock's type");
91  }
92  bool time_source_changed = false;
93 
94  std::condition_variable cv;
95 
96  // Wake this thread if the context is shutdown
97  rclcpp::OnShutdownCallbackHandle shutdown_cb_handle = context->add_on_shutdown_callback(
98  [&cv]() {
99  cv.notify_one();
100  });
101  // No longer need the shutdown callback when this function exits
102  auto callback_remover = rcpputils::scope_exit(
103  [context, &shutdown_cb_handle]() {
104  context->remove_on_shutdown_callback(shutdown_cb_handle);
105  });
106 
107  if (this_clock_type == RCL_STEADY_TIME) {
108  // Synchronize because RCL steady clock epoch might differ from chrono::steady_clock epoch
109  const Time rcl_entry = now();
110  const std::chrono::steady_clock::time_point chrono_entry = std::chrono::steady_clock::now();
111  const Duration delta_t = until - rcl_entry;
112  const std::chrono::steady_clock::time_point chrono_until =
113  chrono_entry + std::chrono::nanoseconds(delta_t.nanoseconds());
114 
115  // loop over spurious wakeups but notice shutdown
116  std::unique_lock lock(impl_->clock_mutex_);
117  while (now() < until && context->is_valid()) {
118  cv.wait_until(lock, chrono_until);
119  }
120  } else if (this_clock_type == RCL_SYSTEM_TIME) {
121  auto system_time = std::chrono::system_clock::time_point(
122  // Cast because system clock resolution is too big for nanoseconds on some systems
123  std::chrono::duration_cast<std::chrono::system_clock::duration>(
124  std::chrono::nanoseconds(until.nanoseconds())));
125 
126  // loop over spurious wakeups but notice shutdown
127  std::unique_lock lock(impl_->clock_mutex_);
128  while (now() < until && context->is_valid()) {
129  cv.wait_until(lock, system_time);
130  }
131  } else if (this_clock_type == RCL_ROS_TIME) {
132  // Install jump handler for any amount of time change, for two purposes:
133  // - if ROS time is active, check if time reached on each new clock sample
134  // - Trigger via on_clock_change to detect if time source changes, to invalidate sleep
135  rcl_jump_threshold_t threshold;
136  threshold.on_clock_change = true;
137  // 0 is disable, so -1 and 1 are smallest possible time changes
138  threshold.min_backward.nanoseconds = -1;
139  threshold.min_forward.nanoseconds = 1;
140  auto clock_handler = create_jump_callback(
141  nullptr,
142  [&cv, &time_source_changed](const rcl_time_jump_t & jump) {
143  if (jump.clock_change != RCL_ROS_TIME_NO_CHANGE) {
144  time_source_changed = true;
145  }
146  cv.notify_one();
147  },
148  threshold);
149 
150  if (!ros_time_is_active()) {
151  auto system_time = std::chrono::system_clock::time_point(
152  // Cast because system clock resolution is too big for nanoseconds on some systems
153  std::chrono::duration_cast<std::chrono::system_clock::duration>(
154  std::chrono::nanoseconds(until.nanoseconds())));
155 
156  // loop over spurious wakeups but notice shutdown or time source change
157  std::unique_lock lock(impl_->clock_mutex_);
158  while (now() < until && context->is_valid() && !time_source_changed) {
159  cv.wait_until(lock, system_time);
160  }
161  } else {
162  // RCL_ROS_TIME with ros_time_is_active.
163  // Just wait without "until" because installed
164  // jump callbacks wake the cv on every new sample.
165  std::unique_lock lock(impl_->clock_mutex_);
166  while (now() < until && context->is_valid() && !time_source_changed) {
167  cv.wait(lock);
168  }
169  }
170  }
171 
172  if (!context->is_valid() || time_source_changed) {
173  return false;
174  }
175 
176  return now() >= until;
177 }
178 
179 bool
180 Clock::sleep_for(Duration rel_time, Context::SharedPtr context)
181 {
182  return sleep_until(now() + rel_time, context);
183 }
184 
185 bool
187 {
189  throw std::runtime_error("clock is not rcl_clock_valid");
190  }
192 }
193 
194 bool
195 Clock::wait_until_started(Context::SharedPtr context)
196 {
197  if (!context || !context->is_valid()) {
198  throw std::runtime_error("context cannot be slept with because it's invalid");
199  }
201  throw std::runtime_error("clock cannot be waited on as it is not rcl_clock_valid");
202  }
203 
204  if (started()) {
205  return true;
206  } else {
207  // Wait until the first non-zero time
208  return sleep_until(rclcpp::Time(0, 1, get_clock_type()), context);
209  }
210 }
211 
212 bool
214  const Duration & timeout,
215  Context::SharedPtr context,
216  const Duration & wait_tick_ns)
217 {
218  if (!context || !context->is_valid()) {
219  throw std::runtime_error("context cannot be slept with because it's invalid");
220  }
222  throw std::runtime_error("clock cannot be waited on as it is not rcl_clock_valid");
223  }
224 
225  Clock timeout_clock = Clock(RCL_STEADY_TIME);
226  Time start = timeout_clock.now();
227 
228  // Check if the clock has started every wait_tick_ns nanoseconds
229  // Context check checks for rclcpp::shutdown()
230  while (!started() && context->is_valid()) {
231  if (timeout < wait_tick_ns) {
232  timeout_clock.sleep_for(timeout);
233  } else {
234  Duration time_left = start + timeout - timeout_clock.now();
235  if (time_left > wait_tick_ns) {
236  timeout_clock.sleep_for(Duration(wait_tick_ns));
237  } else {
238  timeout_clock.sleep_for(time_left);
239  }
240  }
241 
242  if (timeout_clock.now() - start > timeout) {
243  return started();
244  }
245  }
246  return started();
247 }
248 
249 
250 bool
252 {
253  if (!rcl_clock_valid(&impl_->rcl_clock_)) {
254  RCUTILS_LOG_ERROR("ROS time not valid!");
255  return false;
256  }
257 
258  bool is_enabled = false;
259  auto ret = rcl_is_enabled_ros_time_override(&impl_->rcl_clock_, &is_enabled);
260  if (ret != RCL_RET_OK) {
261  exceptions::throw_from_rcl_error(
262  ret, "Failed to check ros_time_override_status");
263  }
264  return is_enabled;
265 }
266 
267 rcl_clock_t *
269 {
270  return &impl_->rcl_clock_;
271 }
272 
274 Clock::get_clock_type() const noexcept
275 {
276  return impl_->rcl_clock_.type;
277 }
278 
279 std::mutex &
281 {
282  return impl_->clock_mutex_;
283 }
284 
285 void
286 Clock::on_time_jump(
287  const rcl_time_jump_t * time_jump,
288  bool before_jump,
289  void * user_data)
290 {
291  const auto * handler = static_cast<JumpHandler *>(user_data);
292  if (nullptr == handler) {
293  return;
294  }
295  if (before_jump && handler->pre_callback) {
296  handler->pre_callback();
297  } else if (!before_jump && handler->post_callback) {
298  handler->post_callback(*time_jump);
299  }
300 }
301 
302 JumpHandler::SharedPtr
304  JumpHandler::pre_callback_t pre_callback,
305  JumpHandler::post_callback_t post_callback,
306  const rcl_jump_threshold_t & threshold)
307 {
308  // Allocate a new jump handler
309  JumpHandler::UniquePtr handler(new JumpHandler(pre_callback, post_callback, threshold));
310  if (nullptr == handler) {
311  throw std::bad_alloc{};
312  }
313 
314  {
315  std::lock_guard<std::mutex> clock_guard(impl_->clock_mutex_);
316  // Try to add the jump callback to the clock
318  &impl_->rcl_clock_, threshold, Clock::on_time_jump,
319  handler.get());
320  if (RCL_RET_OK != ret) {
321  exceptions::throw_from_rcl_error(ret, "Failed to add time jump callback");
322  }
323  }
324 
325  std::weak_ptr<Clock::Impl> weak_impl = impl_;
326  // *INDENT-OFF*
327  // create shared_ptr that removes the callback automatically when all copies are destructed
328  return JumpHandler::SharedPtr(handler.release(), [weak_impl](JumpHandler * handler) noexcept {
329  auto shared_impl = weak_impl.lock();
330  if (shared_impl) {
331  std::lock_guard<std::mutex> clock_guard(shared_impl->clock_mutex_);
332  rcl_ret_t ret = rcl_clock_remove_jump_callback(&shared_impl->rcl_clock_,
333  Clock::on_time_jump, handler);
334  if (RCL_RET_OK != ret) {
335  RCUTILS_LOG_ERROR("Failed to remove time jump callback");
336  }
337  }
338  delete handler;
339  });
340  // *INDENT-ON*
341 }
342 
343 } // namespace rclcpp
#define rcl_get_default_allocator
Return a properly initialized rcl_allocator_t with default values.
Definition: allocator.h:37
rcutils_allocator_t rcl_allocator_t
Encapsulation of an allocator.
Definition: allocator.h:31
RCLCPP_PUBLIC rcl_clock_t * get_clock_handle() noexcept
Return the rcl_clock_t clock handle.
Definition: clock.cpp:268
RCLCPP_PUBLIC bool ros_time_is_active()
Definition: clock.cpp:251
RCLCPP_PUBLIC bool wait_until_started(Context::SharedPtr context=contexts::get_global_default_context())
Definition: clock.cpp:195
RCLCPP_PUBLIC bool started()
Definition: clock.cpp:186
RCLCPP_PUBLIC Time now()
Definition: clock.cpp:70
RCLCPP_PUBLIC JumpHandler::SharedPtr create_jump_callback(JumpHandler::pre_callback_t pre_callback, JumpHandler::post_callback_t post_callback, const rcl_jump_threshold_t &threshold)
Definition: clock.cpp:303
RCLCPP_PUBLIC Clock(rcl_clock_type_t clock_type=RCL_SYSTEM_TIME)
Default c'tor.
Definition: clock.cpp:64
RCLCPP_PUBLIC bool sleep_until(Time until, Context::SharedPtr context=contexts::get_global_default_context())
Definition: clock.cpp:83
RCLCPP_PUBLIC std::mutex & get_clock_mutex() noexcept
Get the clock's mutex.
Definition: clock.cpp:280
RCLCPP_PUBLIC bool sleep_for(Duration rel_time, Context::SharedPtr context=contexts::get_global_default_context())
Definition: clock.cpp:180
rcl_duration_value_t nanoseconds() const
Get duration in nanosecods.
Definition: duration.cpp:226
RCLCPP_PUBLIC rcl_time_point_value_t nanoseconds() const
Get the nanoseconds since epoch.
Definition: time.cpp:219
RCLCPP_PUBLIC rcl_clock_type_t get_clock_type() const
Get the clock type.
Definition: time.cpp:231
Versions of rosidl_typesupport_cpp::get_message_type_support_handle that handle adapted types.
Encapsulation of a time source.
Definition: time.h:138
rcl_duration_value_t nanoseconds
Duration in nanoseconds and its source.
Definition: time.h:77
Describe the prerequisites for calling a time jump callback.
Definition: time.h:114
rcl_duration_t min_forward
Definition: time.h:119
bool on_clock_change
True to call callback when the clock type changes.
Definition: time.h:116
rcl_duration_t min_backward
Definition: time.h:122
Struct to describe a jump in time.
Definition: time.h:95
rcl_clock_change_t clock_change
Indicate whether or not the source of time changed.
Definition: time.h:97
rcl_time_point_value_t nanoseconds
Nanoseconds of the point in time.
Definition: time.h:158
enum rcl_clock_type_e rcl_clock_type_t
Time source type, used to indicate the source of a time measurement.
RCL_PUBLIC RCL_WARN_UNUSED rcl_ret_t rcl_clock_get_now(rcl_clock_t *clock, rcl_time_point_value_t *time_point_value)
Fill the time point value with the current value of the associated clock.
Definition: time.c:263
RCL_PUBLIC RCL_WARN_UNUSED rcl_ret_t rcl_clock_fini(rcl_clock_t *clock)
Finalize a clock.
Definition: time.c:132
@ RCL_ROS_TIME_NO_CHANGE
The source before and after the jump is ROS_TIME.
Definition: time.h:84
RCL_PUBLIC RCL_WARN_UNUSED bool rcl_clock_time_started(rcl_clock_t *clock)
Check if the clock has started.
Definition: time.c:76
RCL_PUBLIC RCL_WARN_UNUSED rcl_ret_t rcl_clock_add_jump_callback(rcl_clock_t *clock, rcl_jump_threshold_t threshold, rcl_jump_callback_t callback, void *user_data)
Add a callback to be called when a time jump exceeds a threshold.
Definition: time.c:392
RCL_PUBLIC RCL_WARN_UNUSED rcl_ret_t rcl_is_enabled_ros_time_override(rcl_clock_t *clock, bool *is_enabled)
Check if the RCL_ROS_TIME time source has the override enabled.
Definition: time.c:343
@ RCL_ROS_TIME
Use ROS time.
Definition: time.h:66
@ RCL_SYSTEM_TIME
Use system time.
Definition: time.h:68
@ RCL_STEADY_TIME
Use a steady clock time.
Definition: time.h:70
RCL_PUBLIC RCL_WARN_UNUSED rcl_ret_t rcl_clock_init(rcl_clock_type_t clock_type, rcl_clock_t *clock, rcl_allocator_t *allocator)
Initialize a clock based on the passed type.
Definition: time.c:98
RCL_PUBLIC RCL_WARN_UNUSED bool rcl_clock_valid(rcl_clock_t *clock)
Check if the clock has valid values.
Definition: time.c:86
#define RCL_RET_OK
Success return code.
Definition: types.h:26
rmw_ret_t rcl_ret_t
The type that holds an rcl return code.
Definition: types.h:23