diff --git a/rclcpp/include/rclcpp/executor.hpp b/rclcpp/include/rclcpp/executor.hpp index 0d0497168f..c9994a49a1 100644 --- a/rclcpp/include/rclcpp/executor.hpp +++ b/rclcpp/include/rclcpp/executor.hpp @@ -385,7 +385,7 @@ class Executor * \throws std::runtime_error if there is an issue triggering the guard condition */ RCLCPP_PUBLIC - virtual void + void cancel(); /// Support dynamic switching of the memory strategy. diff --git a/rclcpp/include/rclcpp/executors/event_waitable.hpp b/rclcpp/include/rclcpp/executors/event_waitable.hpp index 895bf13f50..8f4b4d27ca 100644 --- a/rclcpp/include/rclcpp/executors/event_waitable.hpp +++ b/rclcpp/include/rclcpp/executors/event_waitable.hpp @@ -26,8 +26,9 @@ namespace executors * @brief This class provides a wrapper around the waitable object, that is * meant to be used with the EventsExecutor. * The waitset related methods are stubbed out as they should not be called. + * This class is abstract as the execute method of rclcpp::Waitable is not implemented. * Nodes who want to implement a custom EventWaitable, can derive from this class and override - * the execute function. + * the execute method. */ class EventWaitable : public rclcpp::Waitable { @@ -42,12 +43,6 @@ class EventWaitable : public rclcpp::Waitable RCLCPP_PUBLIC virtual ~EventWaitable() = default; - // Executing an EventWaitable is a no-op. - // Derive from this class to implement execute function. - RCLCPP_PUBLIC - virtual void - execute() = 0; - // Stub API: not used by EventsExecutor RCLCPP_PUBLIC bool diff --git a/rclcpp/include/rclcpp/executors/events_executor.hpp b/rclcpp/include/rclcpp/executors/events_executor.hpp index ead774011b..fd6ec0d50d 100644 --- a/rclcpp/include/rclcpp/executors/events_executor.hpp +++ b/rclcpp/include/rclcpp/executors/events_executor.hpp @@ -45,6 +45,8 @@ namespace executors */ class EventsExecutor : public rclcpp::Executor { + friend class EventsExecutorEntitiesCollector; + public: RCLCPP_SMART_PTR_DEFINITIONS(EventsExecutor) @@ -121,15 +123,6 @@ class EventsExecutor : public rclcpp::Executor void remove_node(std::shared_ptr node_ptr, bool notify = true) override; - /// Cancel any running spin* function, causing it to return. - /** - * This function can be called asynchonously from any thread. - * \throws std::runtime_error if there is an issue triggering the guard condition - */ - RCLCPP_PUBLIC - void - cancel() override; - // Executor callback: Push new events into the queue and trigger cv. // This function is called by the DDS entities when an event happened, // like a subscription receiving a message. @@ -184,11 +177,15 @@ class EventsExecutor : public rclcpp::Executor } } + /// Add a callback group to an executor. + /** + * \sa rclcpp::Executor::add_callback_group + */ void add_callback_group( rclcpp::CallbackGroup::SharedPtr group_ptr, rclcpp::node_interfaces::NodeBaseInterface::SharedPtr node_ptr, - bool notify) override; + bool notify = true) override; /// Remove callback group from the executor /** @@ -198,7 +195,7 @@ class EventsExecutor : public rclcpp::Executor void remove_callback_group( rclcpp::CallbackGroup::SharedPtr group_ptr, - bool notify) override; + bool notify = true) override; RCLCPP_PUBLIC std::vector diff --git a/rclcpp/include/rclcpp/executors/events_executor_entities_collector.hpp b/rclcpp/include/rclcpp/executors/events_executor_entities_collector.hpp index a3ffe6237c..46960edeed 100644 --- a/rclcpp/include/rclcpp/executors/events_executor_entities_collector.hpp +++ b/rclcpp/include/rclcpp/executors/events_executor_entities_collector.hpp @@ -56,8 +56,7 @@ class EventsExecutorEntitiesCollector final : public EventWaitable // Constructor RCLCPP_PUBLIC EventsExecutorEntitiesCollector( - EventsExecutor * executor_context, - std::shared_ptr timers_manager); + EventsExecutor * executor); // Destructor RCLCPP_PUBLIC diff --git a/rclcpp/src/rclcpp/executors/events_executor.cpp b/rclcpp/src/rclcpp/executors/events_executor.cpp index 4afab2395a..fbe5fdc65f 100644 --- a/rclcpp/src/rclcpp/executors/events_executor.cpp +++ b/rclcpp/src/rclcpp/executors/events_executor.cpp @@ -30,7 +30,7 @@ EventsExecutor::EventsExecutor( : rclcpp::Executor(options) { timers_manager_ = std::make_shared(context_); - entities_collector_ = std::make_shared(this, timers_manager_); + entities_collector_ = std::make_shared(this); // This API uses the wait_set only as a token to identify different executors. auto context_interrupt_gc = options.context->get_interrupt_guard_condition(&wait_set_); @@ -251,20 +251,6 @@ EventsExecutor::remove_node(std::shared_ptr node_ptr, bool notify) this->remove_node(node_ptr->get_node_base_interface(), notify); } -void -EventsExecutor::cancel() -{ - spinning.store(false); - rcl_ret_t ret = rcl_trigger_guard_condition(&interrupt_guard_condition_); - if (ret != RCL_RET_OK) { - rclcpp::exceptions::throw_from_rcl_error(ret, "Failed to trigger guard condition in cancel"); - } - - // This makes sure that the timers manager is stopped when we return from this function - // otherwise applications may call rclcpp::shutdown() while that thread is still running. - timers_manager_->stop(); -} - void EventsExecutor::consume_all_events(EventQueue & event_queue) { diff --git a/rclcpp/src/rclcpp/executors/events_executor_entities_collector.cpp b/rclcpp/src/rclcpp/executors/events_executor_entities_collector.cpp index 41d2befa0d..d7bef87097 100644 --- a/rclcpp/src/rclcpp/executors/events_executor_entities_collector.cpp +++ b/rclcpp/src/rclcpp/executors/events_executor_entities_collector.cpp @@ -22,11 +22,14 @@ using rclcpp::executors::EventsExecutorEntitiesCollector; EventsExecutorEntitiesCollector::EventsExecutorEntitiesCollector( - EventsExecutor * executor_context, - TimersManager::SharedPtr timers_manager) + EventsExecutor * executor) { - associated_executor_ = executor_context; - timers_manager_ = timers_manager; + if (executor == nullptr) { + throw std::runtime_error("Received NULL executor in EventsExecutorEntitiesCollector."); + } + + associated_executor_ = executor; + timers_manager_ = associated_executor_->timers_manager_; } EventsExecutorEntitiesCollector::~EventsExecutorEntitiesCollector() @@ -80,7 +83,6 @@ EventsExecutorEntitiesCollector::add_node( { // Check if the node already has an executor and if not, set this to true std::atomic_bool & has_executor = node_ptr->get_associated_with_executor_atomic(); - if (has_executor.exchange(true)) { throw std::runtime_error("Node has already been added to an executor."); } @@ -99,7 +101,6 @@ EventsExecutorEntitiesCollector::add_node( weak_nodes_.push_back(node_ptr); } - void EventsExecutorEntitiesCollector::add_callback_group( rclcpp::CallbackGroup::SharedPtr group_ptr, @@ -346,35 +347,36 @@ EventsExecutorEntitiesCollector::remove_callback_group_from_map( // Look for the group to remove in the map auto iter = weak_groups_to_nodes.find(weak_group_ptr); - if (iter != weak_groups_to_nodes.end()) { - // Group found, get its associated node. - node_ptr = iter->second.lock(); - if (node_ptr == nullptr) { - throw std::runtime_error("Node must not be deleted before its callback group(s)."); - } - // Remove group from map - weak_groups_to_nodes.erase(iter); - - // For all the entities in the group, unset their callbacks - unset_callback_group_entities_callbacks(group_ptr); - - // Check if this node still has other callback groups associated with the executor - bool node_has_associated_callback_groups = - !has_node(node_ptr, weak_groups_associated_with_executor_to_nodes_) && - !has_node(node_ptr, weak_groups_to_nodes_associated_with_executor_); - - if (!node_has_associated_callback_groups) { - // Node doesn't have more callback groups associated to the executor. - // Unset the event callback for the node's notify guard condition, to stop - // receiving events if entities are added or removed to this node. - unset_guard_condition_callback(node_ptr->get_notify_guard_condition()); - - // Remove guard condition from list - rclcpp::node_interfaces::NodeBaseInterface::WeakPtr weak_node_ptr(node_ptr); - weak_nodes_to_guard_conditions_.erase(weak_node_ptr); - } - } else { - throw std::runtime_error("Callback group needs to be associated with executor."); + if (iter == weak_groups_to_nodes.end()) { + // Group not found. + throw std::runtime_error("Callback group needs to be associated with this executor."); + } + + // Group found, get its associated node. + node_ptr = iter->second.lock(); + if (node_ptr == nullptr) { + throw std::runtime_error("Node must not be deleted before its callback group(s)."); + } + // Remove group from map + weak_groups_to_nodes.erase(iter); + + // For all the entities in the group, unset their callbacks + unset_callback_group_entities_callbacks(group_ptr); + + // Check if this node still has other callback groups associated with the executor + bool node_has_associated_callback_groups = + has_node(node_ptr, weak_groups_associated_with_executor_to_nodes_) || + has_node(node_ptr, weak_groups_to_nodes_associated_with_executor_); + + if (!node_has_associated_callback_groups) { + // Node doesn't have more callback groups associated to the executor. + // Unset the event callback for the node's notify guard condition, to stop + // receiving events if entities are added or removed to this node. + unset_guard_condition_callback(node_ptr->get_notify_guard_condition()); + + // Remove guard condition from list + rclcpp::node_interfaces::NodeBaseInterface::WeakPtr weak_node_ptr(node_ptr); + weak_nodes_to_guard_conditions_.erase(weak_node_ptr); } } @@ -383,26 +385,27 @@ EventsExecutorEntitiesCollector::remove_node( rclcpp::node_interfaces::NodeBaseInterface::SharedPtr node_ptr) { if (!node_ptr->get_associated_with_executor_atomic().load()) { - throw std::runtime_error("Node needs to be associated with this executor."); + throw std::runtime_error("Node needs to be associated with an executor."); return; } // Check if this node is currently stored here auto node_it = weak_nodes_.begin(); while (node_it != weak_nodes_.end()) { - if (node_it->lock() == node_ptr) { + bool matched = (node_it->lock() == node_ptr); + if (matched) { weak_nodes_.erase(node_it); break; } + ++node_it; } if (node_it == weak_nodes_.end()) { - // The node is not stored here, so nothing to do - throw std::runtime_error("Tried to remove node not stored in executor."); + // The node is not stored here + throw std::runtime_error("Tried to remove node not stored in this executor."); return; } // Find callback groups belonging to the node to remove std::vector found_group_ptrs; - std::for_each( weak_groups_to_nodes_associated_with_executor_.begin(), weak_groups_to_nodes_associated_with_executor_.end(), @@ -415,7 +418,6 @@ EventsExecutorEntitiesCollector::remove_node( found_group_ptrs.push_back(group_ptr); } }); - // Remove those callback groups std::for_each( found_group_ptrs.begin(), found_group_ptrs.end(), [this] diff --git a/rclcpp/test/CMakeLists.txt b/rclcpp/test/CMakeLists.txt index 939077ace7..5bd1c9db10 100644 --- a/rclcpp/test/CMakeLists.txt +++ b/rclcpp/test/CMakeLists.txt @@ -557,6 +557,15 @@ if(TARGET test_events_executor) target_link_libraries(test_events_executor ${PROJECT_NAME} mimick) endif() +ament_add_gtest(test_events_executor_entities_collector rclcpp/executors/test_events_executor_entities_collector.cpp + APPEND_LIBRARY_DIRS "${append_library_dirs}") +if(TARGET test_events_executor_entities_collector) + ament_target_dependencies(test_events_executor_entities_collector + "rcl" + "test_msgs") + target_link_libraries(test_events_executor_entities_collector ${PROJECT_NAME} mimick) +endif() + ament_add_gtest(test_static_single_threaded_executor rclcpp/executors/test_static_single_threaded_executor.cpp APPEND_LIBRARY_DIRS "${append_library_dirs}") if(TARGET test_static_single_threaded_executor) diff --git a/rclcpp/test/rclcpp/executors/test_events_executor.cpp b/rclcpp/test/rclcpp/executors/test_events_executor.cpp index 7250186e28..e9c3fbd5a1 100644 --- a/rclcpp/test/rclcpp/executors/test_events_executor.cpp +++ b/rclcpp/test/rclcpp/executors/test_events_executor.cpp @@ -22,9 +22,12 @@ #include "test_msgs/srv/empty.hpp" #include "test_msgs/msg/empty.hpp" +#include "../../mocking_utils/patch.hpp" + using namespace std::chrono_literals; using rclcpp::executors::EventsExecutor; +using rclcpp::executors::EventsExecutorNotifyWaitable; class TestEventsExecutor : public ::testing::Test { @@ -40,6 +43,29 @@ class TestEventsExecutor : public ::testing::Test } }; +TEST_F(TestEventsExecutor, notify_waitable) +{ + auto notifier = std::make_shared(); + + // Waitset methods can't be used on EventsWaitable + rcl_wait_set_t wait_set = rcl_get_zero_initialized_wait_set(); + EXPECT_THROW(notifier->add_to_wait_set(&wait_set), std::runtime_error); + EXPECT_THROW(notifier->is_ready(&wait_set), std::runtime_error); + + EventsExecutor executor; + rcl_guard_condition_t gc = rcl_get_zero_initialized_guard_condition(); + notifier->add_guard_condition(&gc); + { + auto mock = mocking_utils::patch_and_return( + "lib:rclcpp", rcl_guard_condition_set_events_executor_callback, RCL_RET_ERROR); + EXPECT_THROW( + notifier->set_events_executor_callback( + &executor, + &EventsExecutor::push_event), + std::runtime_error); + } +} + TEST_F(TestEventsExecutor, run_clients_servers) { auto node = std::make_shared("node"); diff --git a/rclcpp/test/rclcpp/executors/test_events_executor_entities_collector.cpp b/rclcpp/test/rclcpp/executors/test_events_executor_entities_collector.cpp new file mode 100644 index 0000000000..47f0cc017d --- /dev/null +++ b/rclcpp/test/rclcpp/executors/test_events_executor_entities_collector.cpp @@ -0,0 +1,178 @@ +// Copyright 2020 Open Source Robotics Foundation, Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include + +#include + +#include "rclcpp/rclcpp.hpp" + +#include "../../mocking_utils/patch.hpp" + +class TestEventsExecutorEntitiesCollector : public ::testing::Test +{ +public: + void SetUp() + { + rclcpp::init(0, nullptr); + dummy_executor_ = std::make_shared(); + entities_collector_ = + std::make_shared(dummy_executor_.get()); + } + + void TearDown() + { + rclcpp::shutdown(); + } + + rclcpp::executors::EventsExecutorEntitiesCollector::SharedPtr entities_collector_; + +private: + rclcpp::executors::EventsExecutor::SharedPtr dummy_executor_; +}; + +TEST_F(TestEventsExecutorEntitiesCollector, bad_init) +{ + EXPECT_THROW( + std::make_shared(nullptr), + std::runtime_error); +} + +TEST_F(TestEventsExecutorEntitiesCollector, add_remove_node) +{ + auto node1 = std::make_shared("node1", "ns"); + EXPECT_NO_THROW(entities_collector_->add_node(node1->get_node_base_interface())); + + // Check adding second time + EXPECT_THROW( + entities_collector_->add_node(node1->get_node_base_interface()), + std::runtime_error); + + auto node2 = std::make_shared("node2", "ns"); + EXPECT_THROW( + entities_collector_->remove_node(node2->get_node_base_interface()), + std::runtime_error); + EXPECT_NO_THROW(entities_collector_->add_node(node2->get_node_base_interface())); + + EXPECT_NO_THROW(entities_collector_->remove_node(node1->get_node_base_interface())); + EXPECT_THROW( + entities_collector_->remove_node(node1->get_node_base_interface()), + std::runtime_error); + EXPECT_NO_THROW(entities_collector_->remove_node(node2->get_node_base_interface())); + + auto node3 = std::make_shared("node3", "ns"); + node3->get_node_base_interface()->get_associated_with_executor_atomic().exchange(true); + EXPECT_THROW( + entities_collector_->remove_node(node3->get_node_base_interface()), + std::runtime_error); +} + +TEST_F(TestEventsExecutorEntitiesCollector, add_callback_group) +{ + auto node = std::make_shared("node1", "ns"); + rclcpp::CallbackGroup::SharedPtr cb_group = node->create_callback_group( + rclcpp::CallbackGroupType::MutuallyExclusive); + + entities_collector_->add_callback_group(cb_group, node->get_node_base_interface()); + ASSERT_EQ(entities_collector_->get_all_callback_groups().size(), 1u); +} + +TEST_F(TestEventsExecutorEntitiesCollector, add_callback_group_after_add_node) +{ + auto node = std::make_shared("node1", "ns"); + rclcpp::CallbackGroup::SharedPtr cb_group = node->create_callback_group( + rclcpp::CallbackGroupType::MutuallyExclusive); + + entities_collector_->add_node(node->get_node_base_interface()); + EXPECT_THROW( + entities_collector_->add_callback_group(cb_group, node->get_node_base_interface()), + std::runtime_error); +} + +TEST_F(TestEventsExecutorEntitiesCollector, add_callback_group_twice) +{ + auto node = std::make_shared("node1", "ns"); + rclcpp::CallbackGroup::SharedPtr cb_group = node->create_callback_group( + rclcpp::CallbackGroupType::MutuallyExclusive); + + entities_collector_->add_callback_group(cb_group, node->get_node_base_interface()); + ASSERT_EQ(entities_collector_->get_all_callback_groups().size(), 1u); + cb_group->get_associated_with_executor_atomic().exchange(false); + EXPECT_THROW( + entities_collector_->add_callback_group(cb_group, node->get_node_base_interface()), + std::runtime_error); +} + +TEST_F(TestEventsExecutorEntitiesCollector, remove_callback_group_after_node) +{ + auto node = std::make_shared("node1", "ns"); + rclcpp::CallbackGroup::SharedPtr cb_group = node->create_callback_group( + rclcpp::CallbackGroupType::MutuallyExclusive); + + entities_collector_->add_callback_group(cb_group, node->get_node_base_interface()); + ASSERT_EQ(entities_collector_->get_all_callback_groups().size(), 1u); + + node.reset(); + + EXPECT_THROW( + entities_collector_->remove_callback_group(cb_group), + std::runtime_error); +} + +TEST_F(TestEventsExecutorEntitiesCollector, remove_callback_group_twice) +{ + auto node = std::make_shared("node1", "ns"); + rclcpp::CallbackGroup::SharedPtr cb_group = node->create_callback_group( + rclcpp::CallbackGroupType::MutuallyExclusive); + + entities_collector_->add_callback_group(cb_group, node->get_node_base_interface()); + ASSERT_EQ(entities_collector_->get_all_callback_groups().size(), 1u); + + entities_collector_->remove_callback_group(cb_group); + + EXPECT_THROW( + entities_collector_->remove_callback_group(cb_group), + std::runtime_error); +} + +TEST_F(TestEventsExecutorEntitiesCollector, remove_node_opposite_order) +{ + auto node1 = std::make_shared("node1", "ns"); + EXPECT_NO_THROW(entities_collector_->add_node(node1->get_node_base_interface())); + + auto node2 = std::make_shared("node2", "ns"); + EXPECT_NO_THROW(entities_collector_->add_node(node2->get_node_base_interface())); + + EXPECT_NO_THROW(entities_collector_->remove_node(node2->get_node_base_interface())); +} + +TEST_F(TestEventsExecutorEntitiesCollector, test_fancy_name) +{ + auto node1 = std::make_shared("node1", "ns"); + auto node2 = std::make_shared("node2", "ns"); + entities_collector_->add_node(node1->get_node_base_interface()); + + { + auto mock = mocking_utils::patch_and_return( + "lib:rclcpp", rcl_guard_condition_set_events_executor_callback, RCL_RET_ERROR); + + EXPECT_THROW( + entities_collector_->add_node(node2->get_node_base_interface()), + std::runtime_error); + + EXPECT_THROW( + entities_collector_->remove_node(node1->get_node_base_interface()), + std::runtime_error); + } +} diff --git a/rclcpp/test/rclcpp/executors/test_executors.cpp b/rclcpp/test/rclcpp/executors/test_executors.cpp index 809f2ea2a7..6c4fa69c62 100644 --- a/rclcpp/test/rclcpp/executors/test_executors.cpp +++ b/rclcpp/test/rclcpp/executors/test_executors.cpp @@ -128,8 +128,9 @@ TYPED_TEST_CASE(TestExecutors, ExecutorTypes, ExecutorTypeNames); // https://github.com/ros2/rclcpp/issues/1219 using StandardExecutors = ::testing::Types< - rclcpp::executors::EventsExecutor, - rclcpp::executors::SingleThreadedExecutor>; + rclcpp::executors::SingleThreadedExecutor, + rclcpp::executors::MultiThreadedExecutor, + rclcpp::executors::EventsExecutor>; TYPED_TEST_CASE(TestExecutorsStable, StandardExecutors, ExecutorTypeNames); // Make sure that executors detach from nodes when destructing @@ -148,7 +149,6 @@ TYPED_TEST(TestExecutors, detachOnDestruction) { // Make sure that the executor can automatically remove expired nodes correctly // Currently fails for StaticSingleThreadedExecutor so it is being skipped, see: // https://github.com/ros2/rclcpp/issues/1231 -// This test is also flaky for the MultiThreadedExecutor TYPED_TEST(TestExecutorsStable, addTemporaryNode) { using ExecutorType = TypeParam; ExecutorType executor; @@ -162,7 +162,7 @@ TYPED_TEST(TestExecutorsStable, addTemporaryNode) { // Sleep for a short time to verify executor.spin() is going, and didn't throw. std::thread spinner([&]() {EXPECT_NO_THROW(executor.spin());}); - std::this_thread::sleep_for(50ms); + std::this_thread::sleep_for(200ms); executor.cancel(); spinner.join(); } diff --git a/rclcpp/test/rclcpp/test_add_callback_groups_to_executor.cpp b/rclcpp/test/rclcpp/test_add_callback_groups_to_executor.cpp index deea28d915..c153a91187 100644 --- a/rclcpp/test/rclcpp/test_add_callback_groups_to_executor.cpp +++ b/rclcpp/test/rclcpp/test_add_callback_groups_to_executor.cpp @@ -50,7 +50,8 @@ using ExecutorTypes = ::testing::Types< rclcpp::executors::SingleThreadedExecutor, rclcpp::executors::MultiThreadedExecutor, - rclcpp::executors::StaticSingleThreadedExecutor>; + rclcpp::executors::StaticSingleThreadedExecutor, + rclcpp::executors::EventsExecutor>; class ExecutorTypeNames { @@ -71,6 +72,10 @@ class ExecutorTypeNames return "StaticSingleThreadedExecutor"; } + if (std::is_same()) { + return "EventsExecutor"; + } + return ""; } }; @@ -158,7 +163,8 @@ TYPED_TEST(TestAddCallbackGroupsToExecutor, remove_callback_groups) { */ TYPED_TEST(TestAddCallbackGroupsToExecutor, add_duplicate_callback_groups) { - rclcpp::executors::MultiThreadedExecutor executor; + using ExecutorType = TypeParam; + ExecutorType executor; auto node = std::make_shared("my_node", "/ns"); auto timer_callback = []() {}; rclcpp::CallbackGroup::SharedPtr cb_grp = node->create_callback_group( @@ -176,7 +182,7 @@ TYPED_TEST(TestAddCallbackGroupsToExecutor, add_duplicate_callback_groups) */ TYPED_TEST(TestAddCallbackGroupsToExecutor, add_callback_groups_after_add_node_to_executor) { - rclcpp::executors::MultiThreadedExecutor executor; + rclcpp::executors::EventsExecutor executor; auto node = std::make_shared("my_node", "/ns"); executor.add_node(node->get_node_base_interface()); ASSERT_EQ(executor.get_all_callback_groups().size(), 1u); @@ -210,13 +216,14 @@ TYPED_TEST(TestAddCallbackGroupsToExecutor, add_callback_groups_after_add_node_t */ TYPED_TEST(TestAddCallbackGroupsToExecutor, add_unallowable_callback_groups) { + using ExecutorType = TypeParam; + ExecutorType executor; auto node = std::make_shared("my_node", "/ns"); auto timer_callback = []() {}; rclcpp::CallbackGroup::SharedPtr cb_grp = node->create_callback_group( rclcpp::CallbackGroupType::MutuallyExclusive, false); rclcpp::TimerBase::SharedPtr timer_ = node->create_wall_timer( 2s, timer_callback, cb_grp); - rclcpp::executors::MultiThreadedExecutor executor; executor.add_callback_group(cb_grp, node->get_node_base_interface()); ASSERT_EQ(executor.get_all_callback_groups().size(), 1u); @@ -245,14 +252,15 @@ TYPED_TEST(TestAddCallbackGroupsToExecutor, add_unallowable_callback_groups) */ TYPED_TEST(TestAddCallbackGroupsToExecutor, one_node_many_callback_groups_many_executors) { + using ExecutorType = TypeParam; auto node = std::make_shared("my_node", "/ns"); auto timer_callback = []() {}; rclcpp::CallbackGroup::SharedPtr cb_grp = node->create_callback_group( rclcpp::CallbackGroupType::MutuallyExclusive, false); rclcpp::TimerBase::SharedPtr timer_ = node->create_wall_timer( 2s, timer_callback, cb_grp); - rclcpp::executors::MultiThreadedExecutor timer_executor; - rclcpp::executors::MultiThreadedExecutor sub_executor; + ExecutorType timer_executor; + ExecutorType sub_executor; timer_executor.add_callback_group(cb_grp, node->get_node_base_interface()); const rclcpp::QoS qos(10); auto options = rclcpp::SubscriptionOptions(); @@ -281,7 +289,8 @@ TYPED_TEST(TestAddCallbackGroupsToExecutor, one_node_many_callback_groups_many_e */ TYPED_TEST(TestAddCallbackGroupsToExecutor, remove_callback_group) { - rclcpp::executors::MultiThreadedExecutor executor; + using ExecutorType = TypeParam; + ExecutorType executor; auto node = std::make_shared("my_node", "/ns"); auto timer_callback = []() {}; rclcpp::CallbackGroup::SharedPtr cb_grp = node->create_callback_group(