Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Utilize rclcpp::WaitSet as part of the executors #2142

Merged
merged 106 commits into from
Mar 29, 2024
Merged
Show file tree
Hide file tree
Changes from 61 commits
Commits
Show all changes
106 commits
Select commit Hold shift + click to select a range
2bf88de
Deprecate callback_group call taking context
mjcarroll Mar 29, 2023
9099635
Add base executor objects that can be used by implementors
mjcarroll Mar 29, 2023
2426056
Template common operations
mjcarroll Mar 29, 2023
173ffd6
Address reviewer feedback:
mjcarroll Mar 29, 2023
a524bf0
Lint
mjcarroll Mar 29, 2023
89f2106
Address reviewer feedback and fix templates
mjcarroll Mar 30, 2023
9695eaa
Merge branch 'rolling' into mjcarroll/executor_structures
mjcarroll Mar 30, 2023
e173e5a
Lint and docs
mjcarroll Mar 30, 2023
653d1a3
Make executor own the notify waitable
mjcarroll Mar 31, 2023
a6c4c1b
Add pending queue to collector, remove from waitable
mjcarroll Apr 3, 2023
9dd48ce
Change interrupt guard condition to shared_ptr
mjcarroll Apr 3, 2023
6267741
Lint and docs
mjcarroll Apr 3, 2023
974e845
Utilize rclcpp::WaitSet as part of the executors
mjcarroll Mar 29, 2023
1b1a915
Don't exchange atomic twice
mjcarroll Apr 3, 2023
0a9c9a6
Fix add_node and add more tests
mjcarroll Apr 3, 2023
0ae0bea
Make get_notify_guard_condition follow API tick-tock
mjcarroll Apr 3, 2023
87f41bf
Improve callback group tick-tocking
mjcarroll Apr 3, 2023
5809328
Don't lock twice
mjcarroll Apr 3, 2023
debe396
Address reviewer feedback
mjcarroll Apr 4, 2023
c4b6589
Add thread safety annotations and make locks consistent
mjcarroll Apr 4, 2023
8782fff
Merge branch 'mjcarroll/executor_structures' into mjcarroll/rclcpp_wa…
mjcarroll Apr 4, 2023
0c912b6
@wip
mjcarroll Apr 4, 2023
ae9a845
Reset callback groups for multithreaded executor
mjcarroll Apr 6, 2023
3db897a
Avoid many small function calls when building executables
mjcarroll Apr 6, 2023
20d3cca
Re-trigger guard condition if buffer has data
mjcarroll Apr 6, 2023
cd7aaba
Address reviewer feedback
mjcarroll Apr 11, 2023
d8ff831
Trace points
mjcarroll Apr 11, 2023
e52b242
Remove tracepoints
mjcarroll Apr 11, 2023
0c3c899
Reducing diff
mjcarroll Apr 11, 2023
d2d271b
Reduce diff
mjcarroll Apr 11, 2023
200f733
Uncrustify
mjcarroll Apr 11, 2023
985c1f4
Restore tests
mjcarroll Apr 11, 2023
03471fc
Back to weak_ptr and reduce test time
mjcarroll Apr 11, 2023
5c70cb6
reduce diff and lint
mjcarroll Apr 11, 2023
31d25fc
Restore static single threaded tests that weren't working before
mjcarroll Apr 11, 2023
38c80fd
Merge branch 'mjcarroll/executor_structures' into mjcarroll/rclcpp_wa…
mjcarroll Apr 11, 2023
7a81a8f
Restore more tests
mjcarroll Apr 11, 2023
38387e0
Fix multithreaded test
mjcarroll Apr 11, 2023
a2f3977
Fix assert
mjcarroll Apr 11, 2023
1ad6ad6
Fix constructor test
mjcarroll Apr 12, 2023
cd56124
Change ready_executables signature back
mjcarroll Apr 12, 2023
3a80b86
Don't enforce removing callback groups before nodes
mjcarroll Apr 12, 2023
6379f0c
Remove the "add_valid_node" API
mjcarroll Apr 12, 2023
4b2e280
Merge branch 'mjcarroll/executor_structures' into mjcarroll/rclcpp_wa…
mjcarroll Apr 12, 2023
855c64d
Only notify if the trigger condition is valid
mjcarroll Apr 12, 2023
d9a9206
Only trigger if valid and needed
mjcarroll Apr 13, 2023
43c8f45
Merge branch 'mjcarroll/executor_structures' into mjcarroll/rclcpp_wa…
mjcarroll Apr 13, 2023
fcc33e9
Fix spin_some/spin_all implementation
mjcarroll Apr 13, 2023
64cba3b
Restore single threaded executor
mjcarroll Apr 13, 2023
49962fd
Merge branch 'rolling' into mjcarroll/rclcpp_waitset_executor
mjcarroll Apr 13, 2023
838d1ae
Merge branch 'rolling' into mjcarroll/executor_structures
mjcarroll Apr 13, 2023
2c3a36c
Merge branch 'rolling' into mjcarroll/rclcpp_waitset_executor
mjcarroll Apr 13, 2023
ab3bbf4
Merge branch 'rolling' into mjcarroll/executor_structures
mjcarroll Apr 13, 2023
039d2b1
Merge branch 'mjcarroll/executor_structures' into mjcarroll/rclcpp_wa…
mjcarroll Apr 13, 2023
ffdb562
Merge branch 'rolling' into mjcarroll/rclcpp_waitset_executor
mjcarroll Apr 17, 2023
80077dd
Merge branch 'rolling' into mjcarroll/rclcpp_waitset_executor
mjcarroll Apr 17, 2023
ad5931b
Picking ABI-incompatible executor changes
mjcarroll Apr 17, 2023
e3f692b
Merge branch 'mjcarroll/rclcpp_waitset_executor_abi_only' into mjcarr…
mjcarroll Apr 17, 2023
acfc0e2
Add PIMPL
mjcarroll Apr 17, 2023
aff46a4
Merge branch 'mjcarroll/rclcpp_waitset_executor_abi_only' into mjcarr…
mjcarroll Apr 17, 2023
c6612ec
Additional waitset prune
mjcarroll Apr 18, 2023
43d6100
Merge branch 'rolling' into mjcarroll/rclcpp_waitset_executor
mjcarroll Apr 24, 2023
670843a
Fix bad merge
mjcarroll Apr 25, 2023
e364d89
Expand test timeout
mjcarroll Apr 27, 2023
d63d677
Introduce method to clear expired entities from a collection
mjcarroll May 2, 2023
8c2ed20
Merge branch 'rolling' into mjcarroll/rclcpp_waitset_executor
mjcarroll Jun 5, 2023
c8cc2c5
Merge branch 'rolling' into mjcarroll/rclcpp_waitset_executor
mjcarroll Jun 9, 2023
9ed1cc3
Merge branch 'rolling' into mjcarroll/rclcpp_waitset_executor
mjcarroll Jun 9, 2023
77d2810
Merge remote-tracking branch 'origin/mjcarroll/rclcpp_waitset_executo…
mjcarroll Jun 14, 2023
9ee5e26
Merge branch 'rolling' into rclcpp_waitset_executor
mjcarroll Jun 20, 2023
b9e87d2
Merge branch 'rolling' into mjcarroll/rclcpp_waitset_executor
mjcarroll Jul 10, 2023
2a4e932
Merge branch 'rolling' into mjcarroll/rclcpp_waitset_executor
mjcarroll Jul 18, 2023
66ba3b0
Merge branch 'rolling' into mjcarroll/rclcpp_waitset_executor
mjcarroll Oct 25, 2023
3a51869
Merge branch 'rolling' into mjcarroll/rclcpp_waitset_executor
mjcarroll Nov 14, 2023
fbae914
Merge branch 'rolling' into mjcarroll/rclcpp_waitset_executor
clalancette Nov 27, 2023
4fe9f68
Make sure to call remove_expired_entities().
clalancette Nov 29, 2023
eea79ad
Prune queued work when callback group is removed
mjcarroll Nov 30, 2023
37550f8
Prune subscriptions from dynamic storage
mjcarroll Nov 30, 2023
760c8fe
Styles fixes.
clalancette Nov 30, 2023
af43e2a
Re-trigger guard conditions
mjcarroll Dec 5, 2023
cfb7e79
Merge remote-tracking branch 'origin/mjcarroll/rclcpp_waitset_executo…
mjcarroll Dec 5, 2023
4ce1645
Condense to just use watiable.take_data
mjcarroll Dec 5, 2023
662f440
Lint
mjcarroll Dec 6, 2023
02d9cd6
Address reviewer comments (nits)
mjcarroll Dec 8, 2023
8ed094e
Lock mutex when copying
mjcarroll Dec 8, 2023
fe4333f
Merge branch 'rolling' into mjcarroll/rclcpp_waitset_executor
mjcarroll Jan 11, 2024
1376bd7
Refactors to static single threaded based on reviewers
mjcarroll Jan 11, 2024
6208333
More small refactoring
mjcarroll Jan 11, 2024
6b671aa
Lint
mjcarroll Jan 23, 2024
7349408
Merge branch 'rolling' into mjcarroll/rclcpp_waitset_executor
mjcarroll Jan 23, 2024
527d284
Lint
mjcarroll Jan 23, 2024
ed12cdb
Merge branch 'rolling' into mjcarroll/rclcpp_waitset_executor
mjcarroll Jan 26, 2024
92aff2c
Add ready executable accessors to WaitResult
mjcarroll Jan 26, 2024
bf224d6
Make use of accessors from wait_set
mjcarroll Jan 30, 2024
ddac821
Fix tests
mjcarroll Feb 12, 2024
c984469
Merge branch 'rolling' into mjcarroll/rclcpp_waitset_executor
mjcarroll Feb 12, 2024
1a9784d
Fix more tests
mjcarroll Feb 13, 2024
892f70a
Tidy up single threaded executor implementation
mjcarroll Feb 13, 2024
d254d0c
Don't null out timer, rely on call
mjcarroll Feb 27, 2024
62a3f55
Merge remote-tracking branch 'origin/rolling' into mjcarroll/rclcpp_w…
wjwwood Mar 4, 2024
5769359
change how timers are checked from wait result in executors
wjwwood Mar 19, 2024
15f321f
peak -> peek
wjwwood Mar 20, 2024
14c882d
Merge remote-tracking branch 'origin/rolling' into mjcarroll/rclcpp_w…
wjwwood Mar 22, 2024
836946e
fix bug in next_waitable logic
wjwwood Mar 26, 2024
2a88295
fix bug in StaticSTE that broke the add callback groups to executor t…
wjwwood Mar 26, 2024
245bb50
style
wjwwood Mar 26, 2024
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 0 additions & 1 deletion rclcpp/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -64,7 +64,6 @@ set(${PROJECT_NAME}_SRCS
src/rclcpp/executors/executor_notify_waitable.cpp
src/rclcpp/executors/multi_threaded_executor.cpp
src/rclcpp/executors/single_threaded_executor.cpp
src/rclcpp/executors/static_executor_entities_collector.cpp
src/rclcpp/executors/static_single_threaded_executor.cpp
src/rclcpp/expand_topic_or_service_name.cpp
src/rclcpp/experimental/executors/events_executor/events_executor.cpp
Expand Down
28 changes: 28 additions & 0 deletions rclcpp/include/rclcpp/callback_group.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -180,14 +180,42 @@ class CallbackGroup
return _find_ptrs_if_impl<rclcpp::Waitable, Function>(func, waitable_ptrs_);
}

/// Get the total number of entities in this callback group.
/**
* \return the number of entities in the callback group.
*/
RCLCPP_PUBLIC
size_t size() const;

/// Return a reference to the 'can be taken' atomic boolean.
/**
* The resulting bool will be true in the case that no executor is currently
* using an executable entity from this group.
* The resulting bool will be false in the case that an executor is currently
* using an executable entity from this group, and the group policy doesn't
* allow a second take (eg mutual exclusion)
* \return a reference to the flag
*/
RCLCPP_PUBLIC
std::atomic_bool &
can_be_taken_from();

/// Get the group type.
/**
* \return the group type
*/
RCLCPP_PUBLIC
const CallbackGroupType &
type() const;

/// Collect all of the entity pointers contained in this callback group.
/**
* \param[in] sub_func Function to execute for each subscription
* \param[in] service_func Function to execute for each service
* \param[in] client_func Function to execute for each client
* \param[in] timer_func Function to execute for each timer
* \param[in] waitable_fuinc Function to execute for each waitable
*/
RCLCPP_PUBLIC
void collect_all_ptrs(
std::function<void(const rclcpp::SubscriptionBase::SharedPtr &)> sub_func,
Expand Down
185 changes: 35 additions & 150 deletions rclcpp/include/rclcpp/executor.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@
#include <cassert>
#include <chrono>
#include <cstdlib>
#include <deque>
#include <iostream>
#include <list>
#include <map>
Expand All @@ -29,28 +30,27 @@

#include "rcl/guard_condition.h"
#include "rcl/wait.h"
#include "rclcpp/executors/executor_notify_waitable.hpp"
#include "rcpputils/scope_exit.hpp"

#include "rclcpp/context.hpp"
#include "rclcpp/contexts/default_context.hpp"
#include "rclcpp/guard_condition.hpp"
#include "rclcpp/executor_options.hpp"
#include "rclcpp/executors/executor_entities_collection.hpp"
#include "rclcpp/executors/executor_entities_collector.hpp"
#include "rclcpp/future_return_code.hpp"
#include "rclcpp/memory_strategies.hpp"
#include "rclcpp/memory_strategy.hpp"
#include "rclcpp/node_interfaces/node_base_interface.hpp"
#include "rclcpp/utilities.hpp"
#include "rclcpp/visibility_control.hpp"
#include "rclcpp/wait_set.hpp"

namespace rclcpp
{

typedef std::map<rclcpp::CallbackGroup::WeakPtr,
rclcpp::node_interfaces::NodeBaseInterface::WeakPtr,
std::owner_less<rclcpp::CallbackGroup::WeakPtr>> WeakCallbackGroupsToNodesMap;

// Forward declaration is used in convenience method signature.
class Node;
class ExecutorImplementation;

/// Coordinate the order and timing of available communication tasks.
/**
Expand Down Expand Up @@ -402,17 +402,6 @@ class Executor
void
cancel();

/// Support dynamic switching of the memory strategy.
/**
* Switching the memory strategy while the executor is spinning in another threading could have
* unintended consequences.
* \param[in] memory_strategy Shared pointer to the memory strategy to set.
* \throws std::runtime_error if memory_strategy is null
*/
RCLCPP_PUBLIC
void
set_memory_strategy(memory_strategy::MemoryStrategy::SharedPtr memory_strategy);

/// Returns true if the executor is currently spinning.
/**
* This function can be called asynchronously from any thread.
Expand Down Expand Up @@ -497,6 +486,11 @@ class Executor
static void
execute_client(rclcpp::ClientBase::SharedPtr client);

/// Gather all of the waitable entities from associated nodes and callback groups.
RCLCPP_PUBLIC
void
collect_entities();

/// Block until more work becomes avilable or timeout is reached.
/**
* Builds a set of waitable entities, which are passed to the middleware.
Expand All @@ -508,62 +502,6 @@ class Executor
void
wait_for_work(std::chrono::nanoseconds timeout = std::chrono::nanoseconds(-1));

/// Find node associated with a callback group
/**
* \param[in] weak_groups_to_nodes map of callback groups to nodes
* \param[in] group callback group to find assocatiated node
* \return Pointer to associated node if found, else nullptr
*/
RCLCPP_PUBLIC
rclcpp::node_interfaces::NodeBaseInterface::SharedPtr
get_node_by_group(
const WeakCallbackGroupsToNodesMap & weak_groups_to_nodes,
rclcpp::CallbackGroup::SharedPtr group);

/// Return true if the node has been added to this executor.
/**
* \param[in] node_ptr a shared pointer that points to a node base interface
* \param[in] weak_groups_to_nodes map to nodes to lookup
* \return true if the node is associated with the executor, otherwise false
*/
RCLCPP_PUBLIC
bool
has_node(
const rclcpp::node_interfaces::NodeBaseInterface::SharedPtr node_ptr,
const WeakCallbackGroupsToNodesMap & weak_groups_to_nodes) const;

/// Find the callback group associated with a timer
/**
* \param[in] timer Timer to find associated callback group
* \return Pointer to callback group node if found, else nullptr
*/
RCLCPP_PUBLIC
rclcpp::CallbackGroup::SharedPtr
get_group_by_timer(rclcpp::TimerBase::SharedPtr timer);

/// Add a callback group to an executor
/**
* \see rclcpp::Executor::add_callback_group
*/
RCLCPP_PUBLIC
virtual void
add_callback_group_to_map(
rclcpp::CallbackGroup::SharedPtr group_ptr,
rclcpp::node_interfaces::NodeBaseInterface::SharedPtr node_ptr,
WeakCallbackGroupsToNodesMap & weak_groups_to_nodes,
bool notify = true) RCPPUTILS_TSA_REQUIRES(mutex_);

/// Remove a callback group from the executor.
/**
* \see rclcpp::Executor::remove_callback_group
*/
RCLCPP_PUBLIC
virtual void
remove_callback_group_from_map(
rclcpp::CallbackGroup::SharedPtr group_ptr,
WeakCallbackGroupsToNodesMap & weak_groups_to_nodes,
bool notify = true) RCPPUTILS_TSA_REQUIRES(mutex_);

/// Check for executable in ready state and populate union structure.
/**
* \param[out] any_executable populated union structure of ready executable
Expand All @@ -574,33 +512,6 @@ class Executor
bool
get_next_ready_executable(AnyExecutable & any_executable);

/// Check for executable in ready state and populate union structure.
/**
* This is the implementation of get_next_ready_executable that takes into
* account the current state of callback groups' association with nodes and
* executors.
*
* This checks in a particular order for available work:
* * Timers
* * Subscriptions
* * Services
* * Clients
* * Waitable
*
* If the next executable is not associated with this executor/node pair,
* then this method will return false.
*
* \param[out] any_executable populated union structure of ready executable
* \param[in] weak_groups_to_nodes mapping of callback groups to nodes
* \return true if an executable was ready and any_executable was populated,
* otherwise false
*/
RCLCPP_PUBLIC
bool
get_next_ready_executable_from_map(
AnyExecutable & any_executable,
const WeakCallbackGroupsToNodesMap & weak_groups_to_nodes);

/// Wait for executable in ready state and populate union structure.
/**
* If an executable is ready, it will return immediately, otherwise
Expand All @@ -618,21 +529,6 @@ class Executor
AnyExecutable & any_executable,
std::chrono::nanoseconds timeout = std::chrono::nanoseconds(-1));

/// Add all callback groups that can be automatically added from associated nodes.
/**
* The executor, before collecting entities, verifies if any callback group from
* nodes associated with the executor, which is not already associated to an executor,
* can be automatically added to this executor.
* This takes care of any callback group that has been added to a node but not explicitly added
* to the executor.
* It is important to note that in order for the callback groups to be automatically added to an
* executor through this function, the node of the callback groups needs to have been added
* through the `add_node` method.
*/
RCLCPP_PUBLIC
virtual void
add_callback_groups_from_nodes_associated_to_executor() RCPPUTILS_TSA_REQUIRES(mutex_);

/// Spinning state, used to prevent multi threaded calls to spin and to cancel blocking spins.
std::atomic_bool spinning;

Expand All @@ -642,16 +538,8 @@ class Executor
/// Guard condition for signaling the rmw layer to wake up for system shutdown.
std::shared_ptr<rclcpp::GuardCondition> shutdown_guard_condition_;

/// Wait set for managing entities that the rmw layer waits on.
rcl_wait_set_t wait_set_ = rcl_get_zero_initialized_wait_set();

// Mutex to protect the subsequent memory_strategy_.
mutable std::mutex mutex_;

/// The memory strategy: an interface for handling user-defined memory allocation strategies.
memory_strategy::MemoryStrategy::SharedPtr
memory_strategy_ RCPPUTILS_TSA_PT_GUARDED_BY(mutex_);

/// The context associated with this executor.
std::shared_ptr<rclcpp::Context> context_;

Expand All @@ -661,42 +549,39 @@ class Executor
virtual void
spin_once_impl(std::chrono::nanoseconds timeout);

typedef std::map<rclcpp::node_interfaces::NodeBaseInterface::WeakPtr,
const rclcpp::GuardCondition *,
std::owner_less<rclcpp::node_interfaces::NodeBaseInterface::WeakPtr>>
WeakNodesToGuardConditionsMap;

typedef std::map<rclcpp::CallbackGroup::WeakPtr,
const rclcpp::GuardCondition *,
std::owner_less<rclcpp::CallbackGroup::WeakPtr>>
WeakCallbackGroupsToGuardConditionsMap;
/// Waitable containing guard conditions controlling the executor flow.
/**
* This waitable contains the interrupt and shutdown guard condition, as well
* as the guard condition associated with each node and callback group.
* By default, if any change is detected in the monitored entities, the notify
* waitable will awake the executor and rebuild the collections.
mjcarroll marked this conversation as resolved.
Show resolved Hide resolved
*/
std::shared_ptr<rclcpp::executors::ExecutorNotifyWaitable> notify_waitable_;

/// maps nodes to guard conditions
WeakNodesToGuardConditionsMap
weak_nodes_to_guard_conditions_ RCPPUTILS_TSA_GUARDED_BY(mutex_);
std::atomic_bool entities_need_rebuild_;

/// maps callback groups to guard conditions
WeakCallbackGroupsToGuardConditionsMap
weak_groups_to_guard_conditions_ RCPPUTILS_TSA_GUARDED_BY(mutex_);
/// Collector used to associate executable entities from nodes and guard conditions
rclcpp::executors::ExecutorEntitiesCollector collector_;

/// maps callback groups associated to nodes
WeakCallbackGroupsToNodesMap
weak_groups_associated_with_executor_to_nodes_ RCPPUTILS_TSA_GUARDED_BY(mutex_);
/// Waitset to be waited on.
mjcarroll marked this conversation as resolved.
Show resolved Hide resolved
rclcpp::WaitSet wait_set_ RCPPUTILS_TSA_GUARDED_BY(mutex_);

/// maps callback groups to nodes associated with executor
WeakCallbackGroupsToNodesMap
weak_groups_to_nodes_associated_with_executor_ RCPPUTILS_TSA_GUARDED_BY(mutex_);
/// Hold the current state of the collection being waited on by the waitset
rclcpp::executors::ExecutorEntitiesCollection current_collection_ RCPPUTILS_TSA_GUARDED_BY(
mutex_);

/// maps all callback groups to nodes
WeakCallbackGroupsToNodesMap
weak_groups_to_nodes_ RCPPUTILS_TSA_GUARDED_BY(mutex_);
/// Hold the current state of the notify waitable being waited on by the waitset
std::shared_ptr<rclcpp::executors::ExecutorNotifyWaitable> current_notify_waitable_
RCPPUTILS_TSA_GUARDED_BY(mutex_);

/// nodes that are associated with the executor
std::list<rclcpp::node_interfaces::NodeBaseInterface::WeakPtr>
weak_nodes_ RCPPUTILS_TSA_GUARDED_BY(mutex_);
/// Hold the list of executables currently available to be executed.
std::deque<rclcpp::AnyExecutable> ready_executables_ RCPPUTILS_TSA_GUARDED_BY(mutex_);

/// shutdown callback handle registered to Context
rclcpp::OnShutdownCallbackHandle shutdown_callback_handle_;

/// Pointer to implementation
std::unique_ptr<ExecutorImplementation> impl_;
};

} // namespace rclcpp
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -198,14 +198,15 @@ build_entities_collection(
*
* \param[in] collection Collection of entities corresponding to the current wait set.
* \param[in] wait_result Result of rclcpp::WaitSet::wait corresponding to the collection.
* \return A queue of executables that have been marked ready by the waitset.
* \param[inout] queue of executables to append new ready executables to
* \return number of new ready executables
*/
std::deque<rclcpp::AnyExecutable>
size_t
ready_executables(
const ExecutorEntitiesCollection & collection,
rclcpp::WaitResult<rclcpp::WaitSet> & wait_result
rclcpp::WaitResult<rclcpp::WaitSet> & wait_result,
std::deque<rclcpp::AnyExecutable> & executables
);

} // namespace executors
} // namespace rclcpp

Expand Down
Loading