ROS 2 rclcpp + rcl - rolling  rolling-20536064
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 
20 #include "rclcpp/exceptions.hpp"
21 #include "rclcpp/utilities.hpp"
22 
23 #include "rcpputils/scope_exit.hpp"
24 #include "rcutils/logging_macros.h"
25 
26 namespace rclcpp
27 {
28 
30 {
31 public:
32  explicit Impl(rcl_clock_type_t clock_type)
33  : allocator_{rcl_get_default_allocator()}
34  {
35  rcl_ret_t ret = rcl_clock_init(clock_type, &rcl_clock_, &allocator_);
36  if (ret != RCL_RET_OK) {
37  exceptions::throw_from_rcl_error(ret, "failed to initialize rcl clock");
38  }
39  }
40 
41  ~Impl()
42  {
43  rcl_ret_t ret = rcl_clock_fini(&rcl_clock_);
44  if (ret != RCL_RET_OK) {
45  RCUTILS_LOG_ERROR("Failed to fini rcl clock.");
46  }
47  }
48 
49  rcl_clock_t rcl_clock_;
50  rcl_allocator_t allocator_;
51  bool stop_sleeping_ = false;
52  bool shutdown_ = false;
53  std::condition_variable cv_;
54  std::mutex wait_mutex_;
55  std::mutex clock_mutex_;
56 };
57 
58 JumpHandler::JumpHandler(
59  pre_callback_t pre_callback,
60  post_callback_t post_callback,
61  const rcl_jump_threshold_t & threshold)
62 : pre_callback(std::move(pre_callback)),
63  post_callback(std::move(post_callback)),
64  notice_threshold(threshold)
65 {}
66 
68 : impl_(new Clock::Impl(clock_type)) {}
69 
70 Clock::~Clock() {}
71 
72 Time
73 Clock::now() const
74 {
75  Time now(0, 0, impl_->rcl_clock_.type);
76 
77  auto ret = rcl_clock_get_now(&impl_->rcl_clock_, &now.rcl_time_.nanoseconds);
78  if (ret != RCL_RET_OK) {
79  exceptions::throw_from_rcl_error(ret, "could not get current time stamp");
80  }
81 
82  return now;
83 }
84 
85 bool
87  const Time & until,
88  const Context::SharedPtr & context)
89 {
90  if (!context || !context->is_valid()) {
91  throw std::runtime_error("context cannot be slept with because it's invalid");
92  }
93  const auto this_clock_type = get_clock_type();
94  if (until.get_clock_type() != this_clock_type) {
95  throw std::runtime_error("until's clock type does not match this clock's type");
96  }
97  bool time_source_changed = false;
98 
99  // Wake this thread if the context is shutdown
100  rclcpp::OnShutdownCallbackHandle shutdown_cb_handle = context->add_on_shutdown_callback(
101  [this]() {
102  {
103  std::unique_lock lock(impl_->wait_mutex_);
104  impl_->shutdown_ = true;
105  }
106  impl_->cv_.notify_one();
107  });
108  // No longer need the shutdown callback when this function exits
109  auto callback_remover = rcpputils::scope_exit(
110  [context, &shutdown_cb_handle]() {
111  context->remove_on_shutdown_callback(shutdown_cb_handle);
112  });
113 
114  if (this_clock_type == RCL_STEADY_TIME) {
115  // Synchronize because RCL steady clock epoch might differ from chrono::steady_clock epoch
116  const Time rcl_entry = now();
117  const std::chrono::steady_clock::time_point chrono_entry = std::chrono::steady_clock::now();
118  const Duration delta_t = until - rcl_entry;
119  const std::chrono::steady_clock::time_point chrono_until =
120  chrono_entry + std::chrono::nanoseconds(delta_t.nanoseconds());
121 
122  // loop over spurious wakeups but notice shutdown or stop of sleep
123  std::unique_lock lock(impl_->wait_mutex_);
124  while (now() < until && !impl_->stop_sleeping_ && !impl_->shutdown_ && context->is_valid()) {
125  impl_->cv_.wait_until(lock, chrono_until);
126  }
127  impl_->stop_sleeping_ = false;
128  } else if (this_clock_type == RCL_SYSTEM_TIME) {
129  auto system_time = std::chrono::system_clock::time_point(
130  // Cast because system clock resolution is too big for nanoseconds on some systems
131  std::chrono::duration_cast<std::chrono::system_clock::duration>(
132  std::chrono::nanoseconds(until.nanoseconds())));
133 
134  // loop over spurious wakeups but notice shutdown or stop of sleep
135  std::unique_lock lock(impl_->wait_mutex_);
136  while (now() < until && !impl_->stop_sleeping_ && !impl_->shutdown_ && context->is_valid()) {
137  impl_->cv_.wait_until(lock, system_time);
138  }
139  impl_->stop_sleeping_ = false;
140  } else if (this_clock_type == RCL_ROS_TIME) {
141  // Install jump handler for any amount of time change, for two purposes:
142  // - if ROS time is active, check if time reached on each new clock sample
143  // - Trigger via on_clock_change to detect if time source changes, to invalidate sleep
144  rcl_jump_threshold_t threshold;
145  threshold.on_clock_change = true;
146  // 0 is disable, so -1 and 1 are smallest possible time changes
147  threshold.min_backward.nanoseconds = -1;
148  threshold.min_forward.nanoseconds = 1;
149  auto clock_handler = create_jump_callback(
150  nullptr,
151  [this, &time_source_changed](const rcl_time_jump_t & jump) {
152  if (jump.clock_change != RCL_ROS_TIME_NO_CHANGE) {
153  std::lock_guard<std::mutex> lk(impl_->wait_mutex_);
154  time_source_changed = true;
155  }
156  impl_->cv_.notify_one();
157  },
158  threshold);
159 
160  if (!ros_time_is_active()) {
161  auto system_time = std::chrono::system_clock::time_point(
162  // Cast because system clock resolution is too big for nanoseconds on some systems
163  std::chrono::duration_cast<std::chrono::system_clock::duration>(
164  std::chrono::nanoseconds(until.nanoseconds())));
165 
166  // loop over spurious wakeups but notice shutdown, stop of sleep or time source change
167  std::unique_lock lock(impl_->wait_mutex_);
168  while (now() < until && !impl_->stop_sleeping_ && !impl_->shutdown_ && context->is_valid() &&
169  !time_source_changed)
170  {
171  impl_->cv_.wait_until(lock, system_time);
172  }
173  impl_->stop_sleeping_ = false;
174  } else {
175  // RCL_ROS_TIME with ros_time_is_active.
176  // Just wait without "until" because installed
177  // jump callbacks wake the cv on every new sample.
178  std::unique_lock lock(impl_->wait_mutex_);
179  while (now() < until && !impl_->stop_sleeping_ && !impl_->shutdown_ && context->is_valid() &&
180  !time_source_changed)
181  {
182  impl_->cv_.wait(lock);
183  }
184  impl_->stop_sleeping_ = false;
185  }
186  }
187 
188  if (!context->is_valid() || time_source_changed) {
189  return false;
190  }
191 
192  return now() >= until;
193 }
194 
195 bool
196 Clock::sleep_for(const Duration & rel_time, const Context::SharedPtr & context)
197 {
198  return sleep_until(now() + rel_time, context);
199 }
200 
201 bool
203 {
205  throw std::runtime_error("clock is not rcl_clock_valid");
206  }
208 }
209 
210 bool
211 Clock::wait_until_started(const Context::SharedPtr & context)
212 {
213  if (!context || !context->is_valid()) {
214  throw std::runtime_error("context cannot be slept with because it's invalid");
215  }
217  throw std::runtime_error("clock cannot be waited on as it is not rcl_clock_valid");
218  }
219 
220  if (started()) {
221  return true;
222  } else {
223  // Wait until the first non-zero time
224  return sleep_until(rclcpp::Time(0, 1, get_clock_type()), context);
225  }
226 }
227 
228 bool
230  const Duration & timeout,
231  const Context::SharedPtr & context,
232  const Duration & wait_tick_ns)
233 {
234  if (!context || !context->is_valid()) {
235  throw std::runtime_error("context cannot be slept with because it's invalid");
236  }
238  throw std::runtime_error("clock cannot be waited on as it is not rcl_clock_valid");
239  }
240 
241  Clock timeout_clock = Clock(RCL_STEADY_TIME);
242  Time start = timeout_clock.now();
243 
244  // Check if the clock has started every wait_tick_ns nanoseconds
245  // Context check checks for rclcpp::shutdown()
246  while (!started() && context->is_valid()) {
247  if (timeout < wait_tick_ns) {
248  timeout_clock.sleep_for(timeout);
249  } else {
250  Duration time_left = start + timeout - timeout_clock.now();
251  if (time_left > wait_tick_ns) {
252  timeout_clock.sleep_for(Duration(wait_tick_ns));
253  } else {
254  timeout_clock.sleep_for(time_left);
255  }
256  }
257 
258  if (timeout_clock.now() - start > timeout) {
259  return started();
260  }
261  }
262  return started();
263 }
264 
265 
266 bool
268 {
269  if (!rcl_clock_valid(&impl_->rcl_clock_)) {
270  RCUTILS_LOG_ERROR("ROS time not valid!");
271  return false;
272  }
273 
274  bool is_enabled = false;
275  auto ret = rcl_is_enabled_ros_time_override(&impl_->rcl_clock_, &is_enabled);
276  if (ret != RCL_RET_OK) {
277  exceptions::throw_from_rcl_error(
278  ret, "Failed to check ros_time_override_status");
279  }
280  return is_enabled;
281 }
282 
283 rcl_clock_t *
285 {
286  return &impl_->rcl_clock_;
287 }
288 
290 Clock::get_clock_type() const noexcept
291 {
292  return impl_->rcl_clock_.type;
293 }
294 
295 std::mutex &
297 {
298  return impl_->clock_mutex_;
299 }
300 
301 void
302 Clock::on_time_jump(
303  const rcl_time_jump_t * time_jump,
304  bool before_jump,
305  void * user_data)
306 {
307  const auto * handler = static_cast<JumpHandler *>(user_data);
308  if (nullptr == handler) {
309  return;
310  }
311  if (before_jump && handler->pre_callback) {
312  handler->pre_callback();
313  } else if (!before_jump && handler->post_callback) {
314  handler->post_callback(*time_jump);
315  }
316 }
317 
318 JumpHandler::SharedPtr
320  const JumpHandler::pre_callback_t & pre_callback,
321  const JumpHandler::post_callback_t & post_callback,
322  const rcl_jump_threshold_t & threshold)
323 {
324  // Allocate a new jump handler
325  JumpHandler::UniquePtr handler(new JumpHandler(pre_callback, post_callback, threshold));
326  if (nullptr == handler) {
327  throw std::bad_alloc{};
328  }
329 
330  {
331  std::lock_guard<std::mutex> clock_guard(impl_->clock_mutex_);
332  // Try to add the jump callback to the clock
334  &impl_->rcl_clock_, threshold, Clock::on_time_jump,
335  handler.get());
336  if (RCL_RET_OK != ret) {
337  exceptions::throw_from_rcl_error(ret, "Failed to add time jump callback");
338  }
339  }
340 
341  std::weak_ptr<Clock::Impl> weak_impl = impl_;
342  // *INDENT-OFF*
343  // create shared_ptr that removes the callback automatically when all copies are destructed
344  return JumpHandler::SharedPtr(handler.release(), [weak_impl](JumpHandler * handler) noexcept {
345  auto shared_impl = weak_impl.lock();
346  if (shared_impl) {
347  std::lock_guard<std::mutex> clock_guard(shared_impl->clock_mutex_);
348  rcl_ret_t ret = rcl_clock_remove_jump_callback(&shared_impl->rcl_clock_,
349  Clock::on_time_jump, handler);
350  if (RCL_RET_OK != ret) {
351  RCUTILS_LOG_ERROR("Failed to remove time jump callback");
352  }
353  }
354  delete handler;
355  });
356  // *INDENT-ON*
357 }
358 
360 {
361 private:
362  std::condition_variable cv_;
363 
364  rclcpp::Clock::SharedPtr clock_;
365  bool time_source_changed_ = false;
366  std::function<void(const rcl_time_jump_t &)> post_time_jump_callback;
367 
368  bool
369  wait_until_system_time(
370  std::unique_lock<std::mutex> & lock,
371  const rclcpp::Time & abs_time, const std::function<bool ()> & pred)
372  {
373  auto system_time = std::chrono::system_clock::time_point(
374  // Cast because system clock resolution is too big for nanoseconds on some systems
375  std::chrono::duration_cast<std::chrono::system_clock::duration>(
376  std::chrono::nanoseconds(abs_time.nanoseconds())));
377 
378  return cv_.wait_until(lock, system_time, pred);
379  }
380 
381  bool
382  wait_until_steady_time(
383  std::unique_lock<std::mutex> & lock,
384  const rclcpp::Time & abs_time, const std::function<bool ()> & pred)
385  {
386  // Synchronize because RCL steady clock epoch might differ from chrono::steady_clock epoch
387  const rclcpp::Time rcl_entry = clock_->now();
388  const std::chrono::steady_clock::time_point chrono_entry = std::chrono::steady_clock::now();
389  const rclcpp::Duration delta_t = abs_time - rcl_entry;
390  const std::chrono::steady_clock::time_point chrono_until =
391  chrono_entry + std::chrono::nanoseconds(delta_t.nanoseconds());
392 
393  return cv_.wait_until(lock, chrono_until, pred);
394  }
395 
396 
397  bool
398  wait_until_ros_time(
399  std::unique_lock<std::mutex> & lock,
400  const rclcpp::Time & abs_time, const std::function<bool ()> & pred)
401  {
402  // Install jump handler for any amount of time change, for two purposes:
403  // - if ROS time is active, check if time reached on each new clock sample
404  // - Trigger via on_clock_change to detect if time source changes, to invalidate sleep
405  rcl_jump_threshold_t threshold;
406  threshold.on_clock_change = true;
407  // 0 is disable, so -1 and 1 are smallest possible time changes
408  threshold.min_backward.nanoseconds = -1;
409  threshold.min_forward.nanoseconds = 1;
410 
411  time_source_changed_ = false;
412 
413  post_time_jump_callback = [this, &lock] (const rcl_time_jump_t & jump)
414  {
415  if (jump.clock_change != RCL_ROS_TIME_NO_CHANGE) {
416  std::lock_guard<std::mutex> lk(*lock.mutex());
417  time_source_changed_ = true;
418  }
419  cv_.notify_one();
420  };
421 
422  // Note this is a trade-off. Adding the callback for every call
423  // is expensive for high frequency calls. For low frequency waits
424  // its more overhead to have the callback being called all the time.
425  // As we expect the use case to be low frequency calls to wait_until
426  // with relative big pauses between the calls, we install it on demand.
427  auto clock_handler = clock_->create_jump_callback(
428  nullptr,
429  post_time_jump_callback,
430  threshold);
431 
432  if (!clock_->ros_time_is_active()) {
433  auto system_time = std::chrono::system_clock::time_point(
434  // Cast because system clock resolution is too big for nanoseconds on some systems
435  std::chrono::duration_cast<std::chrono::system_clock::duration>(
436  std::chrono::nanoseconds(abs_time.nanoseconds())));
437 
438  return cv_.wait_until(lock, system_time, [this, &pred] () {
439  return time_source_changed_ || pred();
440  });
441  }
442 
443  // RCL_ROS_TIME with ros_time_is_active.
444  // Just wait without "until" because installed
445  // jump callbacks wake the cv on every new sample.
446  cv_.wait(lock, [this, &pred, &abs_time] () {
447  return clock_->now() >= abs_time || time_source_changed_ || pred();
448  });
449 
450  return clock_->now() < abs_time;
451  }
452 
453 public:
454  explicit ClockWaiterImpl(const rclcpp::Clock::SharedPtr & clock)
455  :clock_(clock)
456  {
457  }
458 
459  bool
460  wait_until(
461  std::unique_lock<std::mutex> & lock,
462  const rclcpp::Time & abs_time, const std::function<bool ()> & pred)
463  {
464  switch(clock_->get_clock_type()) {
466  throw std::runtime_error("Error, wait on uninitialized clock called");
467  case RCL_ROS_TIME:
468  return wait_until_ros_time(lock, abs_time, pred);
469  break;
470  case RCL_STEADY_TIME:
471  return wait_until_steady_time(lock, abs_time, pred);
472  break;
473  case RCL_SYSTEM_TIME:
474  return wait_until_system_time(lock, abs_time, pred);
475  break;
476  }
477 
478  return false;
479  }
480 
481  void
482  notify_one()
483  {
484  cv_.notify_one();
485  }
486 };
487 
488 ClockWaiter::ClockWaiter(const rclcpp::Clock::SharedPtr & clock)
489 :impl_(std::make_unique<ClockWaiterImpl>(clock))
490 {
491 }
492 
493 ClockWaiter::~ClockWaiter() = default;
494 
495 bool
497  std::unique_lock<std::mutex> & lock,
498  const rclcpp::Time & abs_time, const std::function<bool ()> & pred)
499 {
500  return impl_->wait_until(lock, abs_time, pred);
501 }
502 
503 void
505 {
506  impl_->notify_one();
507 }
508 
510 {
511  std::mutex pred_mutex_;
512  bool shutdown_ = false;
513  rclcpp::Context::SharedPtr context_;
514  rclcpp::OnShutdownCallbackHandle shutdown_cb_handle_;
515  ClockWaiter::UniquePtr clock_;
516 
517 public:
518  Impl(const rclcpp::Clock::SharedPtr & clock, const rclcpp::Context::SharedPtr & context)
519  :context_(context),
520  clock_(std::make_unique<ClockWaiter>(clock))
521  {
522  if (!context_ || !context_->is_valid()) {
523  throw std::runtime_error("context cannot be slept with because it's invalid");
524  }
525  // Wake this thread if the context is shutdown
526  shutdown_cb_handle_ = context_->add_on_shutdown_callback(
527  [this]() {
528  {
529  std::unique_lock lock(pred_mutex_);
530  shutdown_ = true;
531  }
532  clock_->notify_one();
533  });
534  }
535 
536  ~Impl()
537  {
538  context_->remove_on_shutdown_callback(shutdown_cb_handle_);
539  }
540 
541  bool
542  wait_until(
543  std::unique_lock<std::mutex> & lock, const rclcpp::Time & until,
544  const std::function<bool ()> & pred)
545  {
546  if(lock.mutex() != &pred_mutex_) {
547  throw std::runtime_error(
548  "ClockConditionalVariable::wait_until: Internal error, given lock does not use"
549  " mutex returned by this->mutex()");
550  }
551 
552  clock_->wait_until(lock, until, [this, &pred] () -> bool {
553  return shutdown_ || pred();
554  });
555  return true;
556  }
557 
558  void
559  notify_one()
560  {
561  clock_->notify_one();
562  }
563 
564  std::mutex &
565  mutex()
566  {
567  return pred_mutex_;
568  }
569 };
570 
571 ClockConditionalVariable::ClockConditionalVariable(
572  const rclcpp::Clock::SharedPtr & clock,
573  const rclcpp::Context::SharedPtr & context)
574 :impl_(std::make_unique<Impl>(clock, context))
575 {
576 }
577 
578 ClockConditionalVariable::~ClockConditionalVariable() = default;
579 
580 void
582 {
583  impl_->notify_one();
584 }
585 
586 bool
588  std::unique_lock<std::mutex> & lock, const rclcpp::Time & until,
589  const std::function<bool ()> & pred)
590 {
591  return impl_->wait_until(lock, until, pred);
592 }
593 
594 std::mutex &
596 {
597  return impl_->mutex();
598 }
599 
600 } // 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 void notify_one()
Definition: clock.cpp:581
RCLCPP_PUBLIC std::mutex & mutex()
Definition: clock.cpp:595
RCLCPP_PUBLIC bool wait_until(std::unique_lock< std::mutex > &lock, const rclcpp::Time &until, const std::function< bool()> &pred)
Definition: clock.cpp:587
RCLCPP_PUBLIC bool wait_until(std::unique_lock< std::mutex > &lock, const rclcpp::Time &abs_time, const std::function< bool()> &pred)
Definition: clock.cpp:496
RCLCPP_PUBLIC void notify_one()
Definition: clock.cpp:504
RCLCPP_PUBLIC rcl_clock_t * get_clock_handle() noexcept
Return the rcl_clock_t clock handle.
Definition: clock.cpp:284
RCLCPP_PUBLIC bool ros_time_is_active()
Definition: clock.cpp:267
RCLCPP_PUBLIC Time now() const
Definition: clock.cpp:73
RCLCPP_PUBLIC bool started()
Definition: clock.cpp:202
RCLCPP_PUBLIC JumpHandler::SharedPtr create_jump_callback(const JumpHandler::pre_callback_t &pre_callback, const JumpHandler::post_callback_t &post_callback, const rcl_jump_threshold_t &threshold)
Add a callback to invoke if the jump threshold is exceeded.
Definition: clock.cpp:319
RCLCPP_PUBLIC bool sleep_for(const Duration &rel_time, const Context::SharedPtr &context=contexts::get_global_default_context())
Definition: clock.cpp:196
RCLCPP_PUBLIC bool wait_until_started(const Context::SharedPtr &context=contexts::get_global_default_context())
Definition: clock.cpp:211
RCLCPP_PUBLIC Clock(rcl_clock_type_t clock_type=RCL_SYSTEM_TIME)
Default c'tor.
Definition: clock.cpp:67
RCLCPP_PUBLIC std::mutex & get_clock_mutex() noexcept
Get the clock's mutex.
Definition: clock.cpp:296
RCLCPP_PUBLIC bool sleep_until(const Time &until, const Context::SharedPtr &context=contexts::get_global_default_context())
Definition: clock.cpp:86
rcl_duration_value_t nanoseconds() const
Get duration in nanosecods.
Definition: duration.cpp:248
RCLCPP_PUBLIC rcl_time_point_value_t nanoseconds() const
Get the nanoseconds since epoch.
Definition: time.cpp:215
RCLCPP_PUBLIC rcl_clock_type_t get_clock_type() const
Get the clock type.
Definition: time.cpp:227
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:261
RCL_PUBLIC RCL_WARN_UNUSED rcl_ret_t rcl_clock_fini(rcl_clock_t *clock)
Finalize a clock.
Definition: time.c:131
@ 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:390
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:341
@ RCL_ROS_TIME
Use ROS time.
Definition: time.h:66
@ RCL_SYSTEM_TIME
Use system time.
Definition: time.h:68
@ RCL_CLOCK_UNINITIALIZED
Clock uninitialized.
Definition: time.h:64
@ 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:27
rmw_ret_t rcl_ret_t
The type that holds an rcl return code.
Definition: types.h:24