26 #include <rclcpp/timer.hpp>
28 namespace rclcpp::executors::cbg_executor
40 std::mutex pred_mutex_;
41 bool shutdown_ =
false;
42 rclcpp::Context::SharedPtr context_;
44 ClockWaiter::UniquePtr clock_;
48 :context_(std::move(context))
50 if (!context_ || !context_->is_valid()) {
51 throw std::runtime_error(
"context cannot be slept with because it's invalid");
54 shutdown_cb_handle_ = context_->add_on_shutdown_callback(
56 std::unique_lock lock(pred_mutex_);
66 context_->remove_on_shutdown_callback(shutdown_cb_handle_);
71 std::unique_lock<std::mutex> & lock,
const rclcpp::Clock::SharedPtr & clock,
73 const std::function<
bool ()> & pred)
75 if(lock.mutex() != &pred_mutex_) {
76 throw std::runtime_error(
77 "ClockConditionalVariable::wait_until: Internal error, given lock does not use"
78 " mutex returned by this->mutex()");
85 clock_ = std::make_unique<ClockWaiter>(clock);
87 clock_->wait_until(lock, until, [
this, &pred] () ->
bool {
88 return shutdown_ || pred();
99 std::unique_lock lock(pred_mutex_);
102 clock_->notify_one();
124 std::shared_ptr<const rcl_timer_t> rcl_ref;
125 std::weak_ptr<rclcpp::TimerBase> timer_ref;
126 rclcpp::Clock::SharedPtr clock;
127 bool in_running_list =
false;
128 std::function<void(
const std::function<
void()> & executed_cb)> timer_ready_callback;
133 : timer_type(timer_type), clock_waiter(context)
137 trigger_thread = std::thread([
this]() {
151 std::scoped_lock l(mutex);
152 wakeup_timer_thread();
154 if(trigger_thread.joinable()) {
155 trigger_thread.join();
158 std::scoped_lock l(mutex);
160 for (
auto & tData : all_timers) {
161 if (
auto shrPtr = tData->timer_ref.lock()) {
162 shrPtr->clear_on_reset_callback();
180 std::shared_ptr<const rcl_timer_t> handle = timer->get_timer_handle();
189 if (clock_type_of_timer->type != timer_type) {
194 std::scoped_lock l(mutex);
198 timer->clear_on_reset_callback();
200 auto it = std::find_if(
201 all_timers.begin(), all_timers.end(),
202 [rcl_ref = timer->get_timer_handle()](
const std::unique_ptr<TimerData> & d)
204 return d->rcl_ref == rcl_ref;
207 if (it != all_timers.end()) {
208 const TimerData * data_ptr = it->get();
210 auto it2 = std::find_if(
211 running_timers.begin(), running_timers.end(), [data_ptr](
const auto & e) {
212 return e.second == data_ptr;
215 if(it2 != running_timers.end()) {
216 running_timers.erase(it2);
218 all_timers.erase(it);
221 wakeup_timer_thread();
234 const rclcpp::TimerBase::SharedPtr & timer,
235 const std::function<
void(
const std::function<
void()> executed_cb)> & timer_ready_callback)
239 std::shared_ptr<const rcl_timer_t> handle = timer->get_timer_handle();
248 if (clock_type_of_timer->type != timer_type) {
253 std::unique_ptr<TimerData> data = std::make_unique<TimerData>(TimerData{std::move(handle),
255 timer->get_clock(),
false, timer_ready_callback});
257 timer->set_on_reset_callback(
258 [data_ptr = data.get(),
this](size_t) {
259 std::scoped_lock l(mutex);
260 if (!remove_if_dropped(data_ptr)) {
261 add_timer_to_running_map(data_ptr);
266 std::scoped_lock l(mutex);
268 add_timer_to_running_map(data.get());
270 all_timers.emplace_back(std::move(data) );
279 void wakeup_timer_thread()
281 if(used_clock_for_timers) {
283 std::unique_lock<std::mutex> l(clock_waiter.mutex());
286 clock_waiter.notify_one();
288 thread_conditional.notify_all();
298 bool remove_if_dropped(
const TimerData * timer_data)
300 if (timer_data->rcl_ref.use_count() == 1) {
309 auto it = std::find_if(
310 all_timers.begin(), all_timers.end(), [timer_data](
const std::unique_ptr<TimerData> & e) {
311 return timer_data == e.get();
315 if (it != all_timers.end()) {
316 all_timers.erase(it);
330 void add_timer_to_running_map(TimerData * timer_data)
332 bool wasEmpty = running_timers.empty();
333 std::chrono::nanoseconds old_next_call_time(-1);
335 old_next_call_time = running_timers.begin()->first;
340 if(timer_data->in_running_list) {
341 for(
auto it = running_timers.begin() ; it != running_timers.end(); it++) {
342 if(it->second == timer_data) {
343 running_timers.erase(it);
347 timer_data->in_running_list =
false;
350 int64_t next_call_time{};
357 running_timers.emplace(next_call_time, timer_data);
358 timer_data->in_running_list =
true;
360 if(wasEmpty || running_timers.begin()->first < old_next_call_time) {
362 wakeup_timer_thread();
366 std::vector<std::function<void()>> get_ready_timer_callbacks()
368 std::vector<std::function<void()>> ready_timer_callbacks;
369 ready_timer_callbacks.reserve(running_timers.size());
370 while (!running_timers.empty()) {
371 if(remove_if_dropped(running_timers.begin()->second)) {
372 running_timers.erase(running_timers.begin());
376 int64_t time_until_call{};
377 TimerData *timer_data(running_timers.begin()->second);
379 const rcl_timer_t * rcl_timer_ref = timer_data->rcl_ref.get();
382 timer_data->in_running_list =
false;
383 running_timers.erase(running_timers.begin());
387 if (time_until_call <= 0) {
388 auto timer_done_callback = [timer_data = timer_data,
this] ()
395 std::scoped_lock l(mutex);
396 add_timer_to_running_map(timer_data);
400 ready_timer_callbacks.push_back([ready_callback =
401 timer_data->timer_ready_callback,
402 done_callback = std::move(timer_done_callback)] () {
403 ready_callback(done_callback);
408 timer_data->in_running_list =
false;
409 running_timers.erase(running_timers.begin());
416 return ready_timer_callbacks;
422 std::chrono::nanoseconds next_wakeup_time{};
423 std::vector<std::function<void()>> ready_timer_callbacks;
424 rclcpp::Clock::SharedPtr used_clock;
426 std::scoped_lock l(mutex);
427 ready_timer_callbacks = get_ready_timer_callbacks();
429 if(running_timers.empty()) {
430 used_clock_for_timers.reset();
432 used_clock_for_timers = running_timers.begin()->second->clock;
433 next_wakeup_time = running_timers.begin()->first;
434 used_clock = used_clock_for_timers;
438 for(
const std::function<
void()> & timer_ready_fun : ready_timer_callbacks) {
447 used_clock->wait_until_started();
449 std::unique_lock<std::mutex> l(clock_waiter.mutex());
450 clock_waiter.wait_until(l, used_clock,
451 rclcpp::Time(next_wakeup_time.count(), timer_type), [
this] () ->
bool {
452 return wake_up || !running || !rclcpp::ok();
455 }
catch (
const std::runtime_error &) {
461 std::unique_lock l(mutex);
462 thread_conditional.wait(l, [
this]() {
463 return !running_timers.empty() || !running || !
rclcpp::ok();
467 thread_terminated =
true;
472 rclcpp::Clock::SharedPtr used_clock_for_timers;
474 ClockConditionalVariable clock_waiter;
475 bool wake_up =
false;
479 std::atomic_bool running =
true;
480 std::atomic_bool thread_terminated =
false;
482 std::vector<std::unique_ptr<TimerData>> all_timers;
484 using TimerMap = std::multimap<std::chrono::nanoseconds, TimerData *>;
485 TimerMap running_timers;
487 std::thread trigger_thread;
489 std::condition_variable thread_conditional;
494 static constexpr
size_t NUM_TYPES_OF_TIMERS = 3;
495 std::array<TimerQueue, NUM_TYPES_OF_TIMERS> timer_queues;
498 explicit TimerManager(
const rclcpp::Context::SharedPtr & context)
504 void remove_timer(
const rclcpp::TimerBase::SharedPtr & timer)
507 q.remove_timer(timer);
512 const rclcpp::TimerBase::SharedPtr & timer,
513 const std::function<
void(
const std::function<
void()> executed_cb)> & timer_ready_callback)
516 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(rclcpp::Context::SharedPtr context=nullptr)
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.