Skip to content

Commit 6069c3d

Browse files
authored
fix: Expose timers used by rclcpp::Waitables (#2699)
Signed-off-by: Janosch Machowinski <[email protected]>
1 parent 687adf4 commit 6069c3d

File tree

21 files changed

+252
-6
lines changed

21 files changed

+252
-6
lines changed

rclcpp/include/rclcpp/event_handler.hpp

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@
2020
#include <mutex>
2121
#include <stdexcept>
2222
#include <string>
23+
#include <vector>
2324

2425
#include "rcl/error_handling.h"
2526
#include "rcl/event_callback.h"
@@ -221,6 +222,13 @@ class EventHandlerBase : public Waitable
221222
}
222223
}
223224

225+
RCLCPP_PUBLIC
226+
std::vector<std::shared_ptr<rclcpp::TimerBase>>
227+
get_timers() const override
228+
{
229+
return {};
230+
}
231+
224232
protected:
225233
RCLCPP_PUBLIC
226234
void

rclcpp/include/rclcpp/executors/executor_notify_waitable.hpp

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@
1919
#include <memory>
2020
#include <mutex>
2121
#include <set>
22+
#include <vector>
2223

2324
#include "rclcpp/guard_condition.hpp"
2425
#include "rclcpp/waitable.hpp"
@@ -146,6 +147,14 @@ class ExecutorNotifyWaitable : public rclcpp::Waitable
146147
size_t
147148
get_number_of_ready_guard_conditions() override;
148149

150+
/// Returns the number of used Timers
151+
/**
152+
* Will always return an empty vector.
153+
*/
154+
RCLCPP_PUBLIC
155+
std::vector<std::shared_ptr<rclcpp::TimerBase>>
156+
get_timers() const override;
157+
149158
private:
150159
/// Callback to run when waitable executes
151160
std::function<void(void)> execute_callback_;

rclcpp/include/rclcpp/experimental/subscription_intra_process_base.hpp

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@
1919
#include <memory>
2020
#include <mutex>
2121
#include <string>
22+
#include <vector>
2223

2324
#include "rcl/wait.h"
2425
#include "rmw/impl/cpp/demangle.hpp"
@@ -180,6 +181,13 @@ class SubscriptionIntraProcessBase : public rclcpp::Waitable
180181
on_new_message_callback_ = nullptr;
181182
}
182183

184+
RCLCPP_PUBLIC
185+
std::vector<std::shared_ptr<rclcpp::TimerBase>>
186+
get_timers() const override
187+
{
188+
return {};
189+
}
190+
183191
protected:
184192
std::recursive_mutex callback_mutex_;
185193
std::function<void(size_t)> on_new_message_callback_ {nullptr};

rclcpp/include/rclcpp/subscription_base.hpp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -20,8 +20,8 @@
2020
#include <mutex>
2121
#include <string>
2222
#include <unordered_map>
23-
#include <vector>
2423
#include <utility>
24+
#include <vector>
2525

2626
#include "rcl/event_callback.h"
2727
#include "rcl/subscription.h"

rclcpp/include/rclcpp/waitable.hpp

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@
1818
#include <atomic>
1919
#include <functional>
2020
#include <memory>
21+
#include <vector>
2122

2223
#include "rclcpp/macros.hpp"
2324
#include "rclcpp/visibility_control.hpp"
@@ -27,6 +28,8 @@
2728
namespace rclcpp
2829
{
2930

31+
class TimerBase;
32+
3033
class Waitable
3134
{
3235
public:
@@ -258,6 +261,17 @@ class Waitable
258261
void
259262
clear_on_ready_callback() = 0;
260263

264+
/// Returns all timers used by this waitable
265+
/**
266+
* Must return all timers used within the waitable.
267+
* Note, it is not supported, that timers are added
268+
* or removed over the lifetime of the waitable.
269+
*/
270+
RCLCPP_PUBLIC
271+
virtual
272+
std::vector<std::shared_ptr<rclcpp::TimerBase>>
273+
get_timers() const = 0;
274+
261275
private:
262276
std::atomic<bool> in_use_by_wait_set_{false};
263277
}; // class Waitable

rclcpp/src/rclcpp/executors/executor_notify_waitable.cpp

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -189,5 +189,12 @@ ExecutorNotifyWaitable::get_number_of_ready_guard_conditions()
189189
return notify_guard_conditions_.size();
190190
}
191191

192+
std::vector<std::shared_ptr<rclcpp::TimerBase>>
193+
ExecutorNotifyWaitable::get_timers() const
194+
{
195+
return {};
196+
}
197+
198+
192199
} // namespace executors
193200
} // namespace rclcpp

rclcpp/src/rclcpp/experimental/executors/events_executor/events_executor.cpp

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -408,8 +408,16 @@ EventsExecutor::refresh_current_collection(
408408
[this](auto waitable) {
409409
waitable->set_on_ready_callback(
410410
this->create_waitable_callback(waitable.get()));
411+
for (const auto & t : waitable->get_timers()) {
412+
timers_manager_->add_timer(t);
413+
}
411414
},
412-
[](auto waitable) {waitable->clear_on_ready_callback();});
415+
[this](auto waitable) {
416+
waitable->clear_on_ready_callback();
417+
for (const auto & t : waitable->get_timers()) {
418+
timers_manager_->remove_timer(t);
419+
}
420+
});
413421
}
414422

415423
std::function<void(size_t)>

rclcpp/test/rclcpp/executors/test_events_executor.cpp

Lines changed: 119 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -497,3 +497,122 @@ TEST_F(TestEventsExecutor, test_default_incompatible_qos_callbacks)
497497

498498
rcutils_logging_set_output_handler(original_output_handler);
499499
}
500+
501+
class TestWaitableWithTimer : public rclcpp::Waitable
502+
{
503+
static constexpr int TimerCallbackType = 0;
504+
505+
public:
506+
explicit TestWaitableWithTimer(const rclcpp::Context::SharedPtr & context)
507+
{
508+
auto timer_callback = [this] () {
509+
timer_ready = true;
510+
if (ready_callback) {
511+
// inform executor that the waitable is ready to process a timer event
512+
ready_callback(1, TimerCallbackType);
513+
}
514+
};
515+
timer =
516+
std::make_shared<rclcpp::WallTimer<decltype (timer_callback)>>(std::chrono::milliseconds(10),
517+
std::move(timer_callback), context);
518+
}
519+
520+
void
521+
add_to_wait_set(rcl_wait_set_t & /*wait_set*/) override {}
522+
523+
bool
524+
is_ready(const rcl_wait_set_t & /*wait_set*/) override
525+
{
526+
return false;
527+
}
528+
529+
std::shared_ptr<void>
530+
take_data() override
531+
{
532+
return std::shared_ptr<void>(nullptr);
533+
}
534+
535+
std::shared_ptr<void>
536+
take_data_by_entity_id(size_t id) override
537+
{
538+
if (id != TimerCallbackType) {
539+
throw std::runtime_error("Internal error, got wrong ID for take data");
540+
}
541+
return nullptr;
542+
}
543+
544+
void
545+
execute(const std::shared_ptr<void> &) override
546+
{
547+
if (timer_ready) {
548+
timer_triggered_waitable_and_waitable_was_executed = true;
549+
}
550+
}
551+
552+
void
553+
set_on_ready_callback(std::function<void(size_t, int)> callback) override
554+
{
555+
ready_callback = callback;
556+
}
557+
558+
void
559+
clear_on_ready_callback() override
560+
{
561+
ready_callback = std::function<void(size_t, int)>();
562+
}
563+
564+
size_t
565+
get_number_of_ready_guard_conditions() override
566+
{
567+
return 0;
568+
}
569+
570+
std::vector<std::shared_ptr<rclcpp::TimerBase>> get_timers() const override
571+
{
572+
return {timer};
573+
}
574+
575+
bool timerTriggeredWaitable() const
576+
{
577+
return timer_triggered_waitable_and_waitable_was_executed;
578+
}
579+
580+
private:
581+
std::atomic_bool timer_triggered_waitable_and_waitable_was_executed = false;
582+
std::atomic_bool timer_ready = false;
583+
rclcpp::TimerBase::SharedPtr timer;
584+
std::function<void(size_t, int)> ready_callback;
585+
};
586+
587+
TEST_F(TestEventsExecutor, waitable_with_timer)
588+
{
589+
auto node = std::make_shared<rclcpp::Node>("node");
590+
591+
auto waitable =
592+
std::make_shared<TestWaitableWithTimer>(rclcpp::contexts::get_global_default_context());
593+
594+
EventsExecutor executor;
595+
executor.add_node(node);
596+
597+
node->get_node_waitables_interface()->add_waitable(waitable,
598+
node->get_node_base_interface()->get_default_callback_group());
599+
600+
std::thread spinner([&executor]() {executor.spin();});
601+
602+
size_t cnt = 0;
603+
while (!waitable->timerTriggeredWaitable()) {
604+
std::this_thread::sleep_for(10ms);
605+
cnt++;
606+
607+
// This should terminate after ~20 ms, so a timeout of 500ms should be plenty
608+
EXPECT_LT(cnt, 51);
609+
if (cnt > 50) {
610+
// timeout case
611+
break;
612+
}
613+
}
614+
executor.cancel();
615+
spinner.join();
616+
617+
EXPECT_TRUE(waitable->timerTriggeredWaitable());
618+
}

rclcpp/test/rclcpp/executors/test_waitable.hpp

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@
1818
#include <atomic>
1919
#include <functional>
2020
#include <memory>
21+
#include <vector>
2122

2223
#include "rclcpp/waitable.hpp"
2324
#include "rclcpp/guard_condition.hpp"
@@ -64,6 +65,11 @@ class TestWaitable : public rclcpp::Waitable
6465
size_t
6566
get_is_ready_call_count() const;
6667

68+
std::vector<std::shared_ptr<rclcpp::TimerBase>> get_timers() const override
69+
{
70+
return {};
71+
}
72+
6773
private:
6874
std::atomic<size_t> trigger_count_ = 0;
6975
std::atomic<size_t> is_ready_count_ = 0;

rclcpp/test/rclcpp/node_interfaces/test_node_waitables.cpp

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -38,6 +38,11 @@ class TestWaitable : public rclcpp::Waitable
3838
void clear_on_ready_callback() override {}
3939

4040
std::shared_ptr<void> take_data_by_entity_id(size_t) override {return nullptr;}
41+
42+
std::vector<std::shared_ptr<rclcpp::TimerBase>> get_timers() const override
43+
{
44+
return {};
45+
}
4146
};
4247

4348
class TestNodeWaitables : public ::testing::Test

0 commit comments

Comments
 (0)