Skip to content

Commit 28419d2

Browse files
mjcarrollAlberto Soragna
authored and
Alberto Soragna
committed
Create common structures for executors to use (ros2#2143)
* Deprecate callback_group call taking context * Add base executor objects that can be used by implementors * Template common operations * Add callback to EntitiesCollector constructor * Make function to check automatically added callback groups take a list * Make executor own the notify waitable * Add pending queue to collector, remove from waitable Also change node's get_guard_condition to return shared_ptr * Change interrupt guard condition to shared_ptr Check if guard condition is valid before adding it to the waitable * Make get_notify_guard_condition follow API tick-tock * Improve callback group tick-tocking * Add thread safety annotations and make locks consistent * Remove the "add_valid_node" API * Only notify if the trigger condition is valid * Only trigger if valid and needed Signed-off-by: Michael Carroll <[email protected]>
1 parent fbc8a5e commit 28419d2

24 files changed

+1986
-45
lines changed

rclcpp/CMakeLists.txt

+3
Original file line numberDiff line numberDiff line change
@@ -57,6 +57,9 @@ set(${PROJECT_NAME}_SRCS
5757
src/rclcpp/executable_list.cpp
5858
src/rclcpp/executor.cpp
5959
src/rclcpp/executors.cpp
60+
src/rclcpp/executors/executor_entities_collection.cpp
61+
src/rclcpp/executors/executor_entities_collector.cpp
62+
src/rclcpp/executors/executor_notify_waitable.cpp
6063
src/rclcpp/executors/multi_threaded_executor.cpp
6164
src/rclcpp/executors/single_threaded_executor.cpp
6265
src/rclcpp/executors/static_executor_entities_collector.cpp

rclcpp/include/rclcpp/callback_group.hpp

+59-1
Original file line numberDiff line numberDiff line change
@@ -93,11 +93,54 @@ class CallbackGroup
9393
* determines whether a callback group is automatically added to an executor
9494
* with the node with which it is associated.
9595
*/
96+
[[deprecated("Use CallbackGroup constructor with context function argument")]]
9697
RCLCPP_PUBLIC
9798
explicit CallbackGroup(
9899
CallbackGroupType group_type,
99100
bool automatically_add_to_executor_with_node = true);
100101

102+
/// Constructor for CallbackGroup.
103+
/**
104+
* Callback Groups have a type, either 'Mutually Exclusive' or 'Reentrant'
105+
* and when creating one the type must be specified.
106+
*
107+
* Callbacks in Reentrant Callback Groups must be able to:
108+
* - run at the same time as themselves (reentrant)
109+
* - run at the same time as other callbacks in their group
110+
* - run at the same time as other callbacks in other groups
111+
*
112+
* Callbacks in Mutually Exclusive Callback Groups:
113+
* - will not be run multiple times simultaneously (non-reentrant)
114+
* - will not be run at the same time as other callbacks in their group
115+
* - but must run at the same time as callbacks in other groups
116+
*
117+
* Additionally, callback groups have a property which determines whether or
118+
* not they are added to an executor with their associated node automatically.
119+
* When creating a callback group the automatically_add_to_executor_with_node
120+
* argument determines this behavior, and if true it will cause the newly
121+
* created callback group to be added to an executor with the node when the
122+
* Executor::add_node method is used.
123+
* If false, this callback group will not be added automatically and would
124+
* have to be added to an executor manually using the
125+
* Executor::add_callback_group method.
126+
*
127+
* Whether the node was added to the executor before creating the callback
128+
* group, or after, is irrelevant; the callback group will be automatically
129+
* added to the executor in either case.
130+
*
131+
* \param[in] group_type The type of the callback group.
132+
* \param[in] get_node_context Lambda to retrieve the node context when
133+
* checking that the creating node is valid and using the guard condition.
134+
* \param[in] automatically_add_to_executor_with_node A boolean that
135+
* determines whether a callback group is automatically added to an executor
136+
* with the node with which it is associated.
137+
*/
138+
RCLCPP_PUBLIC
139+
explicit CallbackGroup(
140+
CallbackGroupType group_type,
141+
std::function<rclcpp::Context::SharedPtr(void)> get_node_context,
142+
bool automatically_add_to_executor_with_node = true);
143+
101144
/// Default destructor.
102145
RCLCPP_PUBLIC
103146
~CallbackGroup();
@@ -178,11 +221,24 @@ class CallbackGroup
178221
bool
179222
automatically_add_to_executor_with_node() const;
180223

181-
/// Defer creating the notify guard condition and return it.
224+
/// Retrieve the guard condition used to signal changes to this callback group.
225+
/**
226+
* \param[in] context_ptr context to use when creating the guard condition
227+
* \return guard condition if it is valid, otherwise nullptr.
228+
*/
229+
[[deprecated("Use get_notify_guard_condition() without arguments")]]
182230
RCLCPP_PUBLIC
183231
rclcpp::GuardCondition::SharedPtr
184232
get_notify_guard_condition(const rclcpp::Context::SharedPtr context_ptr);
185233

234+
/// Retrieve the guard condition used to signal changes to this callback group.
235+
/**
236+
* \return guard condition if it is valid, otherwise nullptr.
237+
*/
238+
RCLCPP_PUBLIC
239+
rclcpp::GuardCondition::SharedPtr
240+
get_notify_guard_condition();
241+
186242
/// Trigger the notify guard condition.
187243
RCLCPP_PUBLIC
188244
void
@@ -234,6 +290,8 @@ class CallbackGroup
234290
std::shared_ptr<rclcpp::GuardCondition> notify_guard_condition_ = nullptr;
235291
std::recursive_mutex notify_guard_condition_mutex_;
236292

293+
std::function<rclcpp::Context::SharedPtr(void)> get_context_;
294+
237295
private:
238296
template<typename TypeT, typename Function>
239297
typename TypeT::SharedPtr _find_ptrs_if_impl(

rclcpp/include/rclcpp/executor.hpp

+2-1
Original file line numberDiff line numberDiff line change
@@ -537,8 +537,9 @@ class Executor
537537
std::atomic_bool spinning;
538538

539539
/// Guard condition for signaling the rmw layer to wake up for special events.
540-
rclcpp::GuardCondition interrupt_guard_condition_;
540+
std::shared_ptr<rclcpp::GuardCondition> interrupt_guard_condition_;
541541

542+
/// Guard condition for signaling the rmw layer to wake up for system shutdown.
542543
std::shared_ptr<rclcpp::GuardCondition> shutdown_guard_condition_;
543544

544545
/// Wait set for managing entities that the rmw layer waits on.
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,212 @@
1+
// Copyright 2023 Open Source Robotics Foundation, Inc.
2+
//
3+
// Licensed under the Apache License, Version 2.0 (the "License");
4+
// you may not use this file except in compliance with the License.
5+
// You may obtain a copy of the License at
6+
//
7+
// http://www.apache.org/licenses/LICENSE-2.0
8+
//
9+
// Unless required by applicable law or agreed to in writing, software
10+
// distributed under the License is distributed on an "AS IS" BASIS,
11+
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12+
// See the License for the specific language governing permissions and
13+
// limitations under the License.
14+
15+
#ifndef RCLCPP__EXECUTORS__EXECUTOR_ENTITIES_COLLECTION_HPP_
16+
#define RCLCPP__EXECUTORS__EXECUTOR_ENTITIES_COLLECTION_HPP_
17+
18+
#include <deque>
19+
#include <functional>
20+
#include <unordered_map>
21+
#include <vector>
22+
23+
#include <rclcpp/any_executable.hpp>
24+
#include <rclcpp/node_interfaces/node_base.hpp>
25+
#include <rclcpp/callback_group.hpp>
26+
#include <rclcpp/executors/executor_notify_waitable.hpp>
27+
#include <rclcpp/visibility_control.hpp>
28+
#include <rclcpp/wait_result.hpp>
29+
#include <rclcpp/wait_set.hpp>
30+
31+
namespace rclcpp
32+
{
33+
namespace executors
34+
{
35+
36+
/// Structure to represent a single entity's entry in a collection
37+
template<typename EntityValueType>
38+
struct CollectionEntry
39+
{
40+
/// Weak pointer to entity type
41+
using EntityWeakPtr = typename EntityValueType::WeakPtr;
42+
/// Shared pointer to entity type
43+
using EntitySharedPtr = typename EntityValueType::SharedPtr;
44+
45+
/// The entity
46+
EntityWeakPtr entity;
47+
48+
/// If relevant, the entity's corresponding callback_group
49+
rclcpp::CallbackGroup::WeakPtr callback_group;
50+
};
51+
52+
/// Update a collection based on another collection
53+
/*
54+
* Iterates update_from and update_to to see which entities have been added/removed between
55+
* the two collections.
56+
*
57+
* For each new entry (in update_from, but not in update_to),
58+
* add the entity and fire the on_added callback
59+
* For each removed entry (in update_to, but not in update_from),
60+
* remove the entity and fire the on_removed callback.
61+
*
62+
* \param[in] update_from The collection representing the next iteration's state
63+
* \param[inout] update_to The collection representing the current iteration's state
64+
* \param[in] on_added Callback fired when a new entity is detected
65+
* \param[in] on_removed Callback fired when an entity is removed
66+
*/
67+
template<typename CollectionType>
68+
void update_entities(
69+
const CollectionType & update_from,
70+
CollectionType & update_to,
71+
std::function<void(const typename CollectionType::EntitySharedPtr &)> on_added,
72+
std::function<void(const typename CollectionType::EntitySharedPtr &)> on_removed
73+
)
74+
{
75+
for (auto it = update_to.begin(); it != update_to.end(); ) {
76+
if (update_from.count(it->first) == 0) {
77+
auto entity = it->second.entity.lock();
78+
if (entity) {
79+
on_removed(entity);
80+
}
81+
it = update_to.erase(it);
82+
} else {
83+
++it;
84+
}
85+
}
86+
for (auto it = update_from.begin(); it != update_from.end(); ++it) {
87+
if (update_to.count(it->first) == 0) {
88+
auto entity = it->second.entity.lock();
89+
if (entity) {
90+
on_added(entity);
91+
}
92+
update_to.insert(*it);
93+
}
94+
}
95+
}
96+
97+
/// A collection of entities, indexed by their corresponding handles
98+
template<typename EntityKeyType, typename EntityValueType>
99+
class EntityCollection
100+
: public std::unordered_map<const EntityKeyType *, CollectionEntry<EntityValueType>>
101+
{
102+
public:
103+
/// Key type of the map
104+
using Key = const EntityKeyType *;
105+
106+
/// Weak pointer to entity type
107+
using EntityWeakPtr = typename EntityValueType::WeakPtr;
108+
109+
/// Shared pointer to entity type
110+
using EntitySharedPtr = typename EntityValueType::SharedPtr;
111+
112+
/// Update this collection based on the contents of another collection
113+
/**
114+
* Update the internal state of this collection, firing callbacks when entities have been
115+
* added or removed.
116+
*
117+
* \param[in] other Collection to compare to
118+
* \param[in] on_added Callback for when entities have been added
119+
* \param[in] on_removed Callback for when entities have been removed
120+
*/
121+
void update(
122+
const EntityCollection<EntityKeyType, EntityValueType> & other,
123+
std::function<void(const EntitySharedPtr &)> on_added,
124+
std::function<void(const EntitySharedPtr &)> on_removed)
125+
{
126+
update_entities(other, *this, on_added, on_removed);
127+
}
128+
};
129+
130+
/// Represent the total set of entities for a single executor
131+
/**
132+
* This allows the entities to be stored from ExecutorEntitiesCollector.
133+
* The structure also makes in convenient to re-evaluate when entities have been added or removed.
134+
*/
135+
struct ExecutorEntitiesCollection
136+
{
137+
/// Collection type for timer entities
138+
using TimerCollection = EntityCollection<rcl_timer_t, rclcpp::TimerBase>;
139+
140+
/// Collection type for subscription entities
141+
using SubscriptionCollection = EntityCollection<rcl_subscription_t, rclcpp::SubscriptionBase>;
142+
143+
/// Collection type for client entities
144+
using ClientCollection = EntityCollection<rcl_client_t, rclcpp::ClientBase>;
145+
146+
/// Collection type for service entities
147+
using ServiceCollection = EntityCollection<rcl_service_t, rclcpp::ServiceBase>;
148+
149+
/// Collection type for waitable entities
150+
using WaitableCollection = EntityCollection<rclcpp::Waitable, rclcpp::Waitable>;
151+
152+
/// Collection type for guard condition entities
153+
using GuardConditionCollection = EntityCollection<rcl_guard_condition_t, rclcpp::GuardCondition>;
154+
155+
/// Collection of timers currently in use by the executor.
156+
TimerCollection timers;
157+
158+
/// Collection of subscriptions currently in use by the executor.
159+
SubscriptionCollection subscriptions;
160+
161+
/// Collection of clients currently in use by the executor.
162+
ClientCollection clients;
163+
164+
/// Collection of services currently in use by the executor.
165+
ServiceCollection services;
166+
167+
/// Collection of guard conditions currently in use by the executor.
168+
GuardConditionCollection guard_conditions;
169+
170+
/// Collection of waitables currently in use by the executor.
171+
WaitableCollection waitables;
172+
173+
/// Check if the entities collection is empty
174+
/**
175+
* \return true if all member collections are empty, false otherwise
176+
*/
177+
bool empty() const;
178+
179+
/// Clear the entities collection
180+
void clear();
181+
};
182+
183+
/// Build an entities collection from callback groups
184+
/**
185+
* Iterates a list of callback groups and adds entities from each valid group
186+
*
187+
* \param[in] callback_groups List of callback groups to check for entities
188+
* \param[inout] colletion Entities collection to populate with found entities
189+
*/
190+
void
191+
build_entities_collection(
192+
const std::vector<rclcpp::CallbackGroup::WeakPtr> & callback_groups,
193+
ExecutorEntitiesCollection & collection);
194+
195+
/// Build a queue of executables ready to be executed
196+
/**
197+
* Iterates a list of entities and adds them to a queue if they are ready.
198+
*
199+
* \param[in] collection Collection of entities corresponding to the current wait set.
200+
* \param[in] wait_result Result of rclcpp::WaitSet::wait corresponding to the collection.
201+
* \return A queue of executables that have been marked ready by the waitset.
202+
*/
203+
std::deque<rclcpp::AnyExecutable>
204+
ready_executables(
205+
const ExecutorEntitiesCollection & collection,
206+
rclcpp::WaitResult<rclcpp::WaitSet> & wait_result
207+
);
208+
209+
} // namespace executors
210+
} // namespace rclcpp
211+
212+
#endif // RCLCPP__EXECUTORS__EXECUTOR_ENTITIES_COLLECTION_HPP_

0 commit comments

Comments
 (0)