26 #include <rclcpp/timer.hpp>
27 #include <rclcpp/utilities.hpp>
29 namespace rclcpp::executors::cbg_executor
41 std::mutex pred_mutex_;
42 bool shutdown_ =
false;
43 rclcpp::Context::SharedPtr context_;
45 ClockWaiter::UniquePtr clock_;
49 :context_(std::move(context))
51 if (!context_ || !context_->is_valid()) {
52 throw std::runtime_error(
"context cannot be slept with because it's invalid");
55 shutdown_cb_handle_ = context_->add_on_shutdown_callback(
57 std::unique_lock lock(pred_mutex_);
67 context_->remove_on_shutdown_callback(shutdown_cb_handle_);
72 std::unique_lock<std::mutex> & lock,
const rclcpp::Clock::SharedPtr & clock,
74 const std::function<
bool ()> & pred)
76 if(lock.mutex() != &pred_mutex_) {
77 throw std::runtime_error(
78 "ClockConditionalVariable::wait_until: Internal error, given lock does not use"
79 " mutex returned by this->mutex()");
86 clock_ = std::make_unique<ClockWaiter>(clock);
88 clock_->wait_until(lock, until, [
this, &pred] () ->
bool {
89 return shutdown_ || pred();
100 std::unique_lock lock(pred_mutex_);
103 clock_->notify_one();
125 std::shared_ptr<const rcl_timer_t> rcl_ref;
126 std::weak_ptr<rclcpp::TimerBase> timer_ref;
127 rclcpp::Clock::SharedPtr clock;
128 bool in_running_list =
false;
129 std::function<void(
const std::function<
void()> & executed_cb)> timer_ready_callback;
134 : timer_type(timer_type), clock_waiter(context)
138 trigger_thread = std::thread([
this]() {
152 std::scoped_lock l(mutex);
153 wakeup_timer_thread();
155 if(trigger_thread.joinable()) {
156 trigger_thread.join();
159 std::scoped_lock l(mutex);
161 for (
auto & tData : all_timers) {
162 if (
auto shrPtr = tData->timer_ref.lock()) {
163 shrPtr->clear_on_reset_callback();
181 std::shared_ptr<const rcl_timer_t> handle = timer->get_timer_handle();
190 if (clock_type_of_timer->type != timer_type) {
195 std::scoped_lock l(mutex);
199 timer->clear_on_reset_callback();
201 auto it = std::find_if(
202 all_timers.begin(), all_timers.end(),
203 [rcl_ref = timer->get_timer_handle()](
const std::unique_ptr<TimerData> & d)
205 return d->rcl_ref == rcl_ref;
208 if (it != all_timers.end()) {
209 const TimerData * data_ptr = it->get();
211 auto it2 = std::find_if(
212 running_timers.begin(), running_timers.end(), [data_ptr](
const auto & e) {
213 return e.second == data_ptr;
216 if(it2 != running_timers.end()) {
217 running_timers.erase(it2);
219 all_timers.erase(it);
222 wakeup_timer_thread();
235 const rclcpp::TimerBase::SharedPtr & timer,
236 const std::function<
void(
const std::function<
void()> executed_cb)> & timer_ready_callback)
240 std::shared_ptr<const rcl_timer_t> handle = timer->get_timer_handle();
249 if (clock_type_of_timer->type != timer_type) {
254 std::unique_ptr<TimerData> data = std::make_unique<TimerData>(TimerData{std::move(handle),
256 timer->get_clock(),
false, timer_ready_callback});
258 timer->set_on_reset_callback(
259 [data_ptr = data.get(),
this](size_t) {
260 std::scoped_lock l(mutex);
261 if (!remove_if_dropped(data_ptr)) {
262 add_timer_to_running_map(data_ptr);
267 std::scoped_lock l(mutex);
269 add_timer_to_running_map(data.get());
271 all_timers.emplace_back(std::move(data) );
280 void wakeup_timer_thread()
282 if(used_clock_for_timers) {
284 std::unique_lock<std::mutex> l(clock_waiter.mutex());
287 clock_waiter.notify_one();
289 thread_conditional.notify_all();
299 bool remove_if_dropped(
const TimerData * timer_data)
301 if (timer_data->rcl_ref.use_count() == 1) {
310 auto it = std::find_if(
311 all_timers.begin(), all_timers.end(), [timer_data](
const std::unique_ptr<TimerData> & e) {
312 return timer_data == e.get();
316 if (it != all_timers.end()) {
317 all_timers.erase(it);
331 void add_timer_to_running_map(TimerData * timer_data)
333 bool wasEmpty = running_timers.empty();
334 std::chrono::nanoseconds old_next_call_time(-1);
336 old_next_call_time = running_timers.begin()->first;
341 if(timer_data->in_running_list) {
342 for(
auto it = running_timers.begin() ; it != running_timers.end(); it++) {
343 if(it->second == timer_data) {
344 running_timers.erase(it);
348 timer_data->in_running_list =
false;
351 int64_t next_call_time{};
358 running_timers.emplace(next_call_time, timer_data);
359 timer_data->in_running_list =
true;
361 if(wasEmpty || running_timers.begin()->first < old_next_call_time) {
363 wakeup_timer_thread();
367 std::vector<std::function<void()>> get_ready_timer_callbacks()
369 std::vector<std::function<void()>> ready_timer_callbacks;
370 ready_timer_callbacks.reserve(running_timers.size());
371 while (!running_timers.empty()) {
372 if(remove_if_dropped(running_timers.begin()->second)) {
373 running_timers.erase(running_timers.begin());
377 int64_t time_until_call{};
378 TimerData *timer_data(running_timers.begin()->second);
380 const rcl_timer_t * rcl_timer_ref = timer_data->rcl_ref.get();
383 timer_data->in_running_list =
false;
384 running_timers.erase(running_timers.begin());
388 if (time_until_call <= 0) {
389 auto timer_done_callback = [timer_data = timer_data,
this] ()
396 std::scoped_lock l(mutex);
397 add_timer_to_running_map(timer_data);
401 ready_timer_callbacks.push_back([ready_callback =
402 timer_data->timer_ready_callback,
403 done_callback = std::move(timer_done_callback)] () {
404 ready_callback(done_callback);
409 timer_data->in_running_list =
false;
410 running_timers.erase(running_timers.begin());
417 return ready_timer_callbacks;
423 std::chrono::nanoseconds next_wakeup_time{};
424 std::vector<std::function<void()>> ready_timer_callbacks;
425 rclcpp::Clock::SharedPtr used_clock;
427 std::scoped_lock l(mutex);
428 ready_timer_callbacks = get_ready_timer_callbacks();
430 if(running_timers.empty()) {
431 used_clock_for_timers.reset();
433 used_clock_for_timers = running_timers.begin()->second->clock;
434 next_wakeup_time = running_timers.begin()->first;
435 used_clock = used_clock_for_timers;
439 for(
const std::function<
void()> & timer_ready_fun : ready_timer_callbacks) {
448 used_clock->wait_until_started();
450 std::unique_lock<std::mutex> l(clock_waiter.mutex());
451 clock_waiter.wait_until(l, used_clock,
452 rclcpp::Time(next_wakeup_time.count(), timer_type), [
this] () ->
bool {
453 return wake_up || !running || !rclcpp::ok();
456 }
catch (
const std::runtime_error &) {
462 std::unique_lock l(mutex);
463 thread_conditional.wait(l, [
this]() {
464 return !running_timers.empty() || !running || !
rclcpp::ok();
468 thread_terminated =
true;
473 rclcpp::Clock::SharedPtr used_clock_for_timers;
475 ClockConditionalVariable clock_waiter;
476 bool wake_up =
false;
480 std::atomic_bool running =
true;
481 std::atomic_bool thread_terminated =
false;
483 std::vector<std::unique_ptr<TimerData>> all_timers;
485 using TimerMap = std::multimap<std::chrono::nanoseconds, TimerData *>;
486 TimerMap running_timers;
488 std::thread trigger_thread;
490 std::condition_variable thread_conditional;
495 static constexpr
size_t NUM_TYPES_OF_TIMERS = 3;
496 std::array<TimerQueue, NUM_TYPES_OF_TIMERS> timer_queues;
499 explicit TimerManager(
const rclcpp::Context::SharedPtr & context)
505 void remove_timer(
const rclcpp::TimerBase::SharedPtr & timer)
508 q.remove_timer(timer);
513 const rclcpp::TimerBase::SharedPtr & timer,
514 const std::function<
void(
const std::function<
void()> executed_cb)> & timer_ready_callback)
517 q.add_timer(timer, timer_ready_callback);
A class for managing a queue of timers.
void remove_timer(const rclcpp::TimerBase::SharedPtr &timer)
Removes a new timer from the queue. This function is thread safe.
void add_timer(const rclcpp::TimerBase::SharedPtr &timer, const std::function< void(const std::function< void()> executed_cb)> &timer_ready_callback)
Adds a new timer to the queue. This function is thread safe.
RCLCPP_PUBLIC bool ok(const rclcpp::Context::SharedPtr &context=rclcpp::contexts::get_global_default_context())
Check rclcpp's status.
Encapsulation of a time source.
Structure which encapsulates a ROS Timer.
enum rcl_clock_type_e rcl_clock_type_t
Time source type, used to indicate the source of a time measurement.
@ RCL_ROS_TIME
Use ROS time.
@ RCL_SYSTEM_TIME
Use system time.
@ RCL_STEADY_TIME
Use a steady clock time.
RCL_PUBLIC RCL_WARN_UNUSED rcl_ret_t rcl_timer_get_next_call_time(const rcl_timer_t *timer, int64_t *next_call_time)
Retrieve the time when the next call to rcl_timer_call() shall occur.
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.
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.
RCL_PUBLIC RCL_WARN_UNUSED rcl_ret_t rcl_timer_clock(const rcl_timer_t *timer, rcl_clock_t **clock)
Retrieve the clock of the timer.
#define RCL_RET_OK
Success return code.
#define RCL_RET_TIMER_CANCELED
Given timer was canceled return code.
rmw_ret_t rcl_ret_t
The type that holds an rcl return code.