Skip to content

Commit ad08ee9

Browse files
Josh Finkenapojomovsky
authored andcommitted
Initial commit adding the upstream events-executor here
The commit also includes a few test fixes and temporary comments allowing the unit-tests, and the full package, to build.
1 parent 9177002 commit ad08ee9

File tree

17 files changed

+3195
-222
lines changed

17 files changed

+3195
-222
lines changed

rclcpp/CMakeLists.txt

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -66,6 +66,8 @@ set(${PROJECT_NAME}_SRCS
6666
src/rclcpp/executors/static_executor_entities_collector.cpp
6767
src/rclcpp/executors/static_single_threaded_executor.cpp
6868
src/rclcpp/expand_topic_or_service_name.cpp
69+
src/rclcpp/experimental/executors/events_executor/events_executor.cpp
70+
src/rclcpp/experimental/timers_manager.cpp
6971
src/rclcpp/future_return_code.cpp
7072
src/rclcpp/generic_publisher.cpp
7173
src/rclcpp/generic_subscription.cpp
Lines changed: 294 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,294 @@
1+
// Copyright 2023 iRobot Corporation.
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__EXPERIMENTAL__EXECUTORS__EVENTS_EXECUTOR__EVENTS_EXECUTOR_HPP_
16+
#define RCLCPP__EXPERIMENTAL__EXECUTORS__EVENTS_EXECUTOR__EVENTS_EXECUTOR_HPP_
17+
18+
#include <atomic>
19+
#include <chrono>
20+
#include <memory>
21+
#include <vector>
22+
23+
#include "rclcpp/executor.hpp"
24+
#include "rclcpp/executors/executor_entities_collection.hpp"
25+
#include "rclcpp/executors/executor_entities_collector.hpp"
26+
#include "rclcpp/experimental/executors/events_executor/events_executor_event_types.hpp"
27+
#include "rclcpp/experimental/executors/events_executor/events_queue.hpp"
28+
#include "rclcpp/experimental/executors/events_executor/simple_events_queue.hpp"
29+
#include "rclcpp/experimental/timers_manager.hpp"
30+
#include "rclcpp/node.hpp"
31+
32+
namespace rclcpp
33+
{
34+
namespace experimental
35+
{
36+
namespace executors
37+
{
38+
39+
/// Events executor implementation
40+
/**
41+
* This executor uses an events queue and a timers manager to execute entities from its
42+
* associated nodes and callback groups.
43+
* ROS 2 entities allow to set callback functions that are invoked when the entity is triggered
44+
* or has work to do. The events-executor sets these callbacks such that they push an
45+
* event into its queue.
46+
*
47+
* This executor tries to reduce as much as possible the amount of maintenance operations.
48+
* This allows to use customized `EventsQueue` classes to achieve different goals such
49+
* as very low CPU usage, bounded memory requirement, determinism, etc.
50+
*
51+
* The executor uses a weak ownership model and it locks entities only while executing
52+
* their related events.
53+
*
54+
* To run this executor:
55+
* rclcpp::experimental::executors::EventsExecutor executor;
56+
* executor.add_node(node);
57+
* executor.spin();
58+
* executor.remove_node(node);
59+
*/
60+
class EventsExecutor : public rclcpp::Executor
61+
{
62+
friend class EventsExecutorEntitiesCollector;
63+
64+
public:
65+
RCLCPP_SMART_PTR_DEFINITIONS(EventsExecutor)
66+
67+
/// Default constructor. See the default constructor for Executor.
68+
/**
69+
* \param[in] events_queue The queue used to store events.
70+
* \param[in] execute_timers_separate_thread If true, timers are executed in a separate
71+
* thread. If false, timers are executed in the same thread as all other entities.
72+
* \param[in] options Options used to configure the executor.
73+
*/
74+
RCLCPP_PUBLIC
75+
explicit EventsExecutor(
76+
rclcpp::experimental::executors::EventsQueue::UniquePtr events_queue = std::make_unique<
77+
rclcpp::experimental::executors::SimpleEventsQueue>(),
78+
bool execute_timers_separate_thread = false,
79+
const rclcpp::ExecutorOptions & options = rclcpp::ExecutorOptions());
80+
81+
/// Default destructor.
82+
RCLCPP_PUBLIC
83+
virtual ~EventsExecutor();
84+
85+
/// Events executor implementation of spin.
86+
/**
87+
* This function will block until work comes in, execute it, and keep blocking.
88+
* It will only be interrupted by a CTRL-C (managed by the global signal handler).
89+
* \throws std::runtime_error when spin() called while already spinning
90+
*/
91+
RCLCPP_PUBLIC
92+
void
93+
spin() override;
94+
95+
/// Events executor implementation of spin some
96+
/**
97+
* This non-blocking function will execute the timers and events
98+
* that were ready when this API was called, until timeout or no
99+
* more work available. New ready-timers/events arrived while
100+
* executing work, won't be taken into account here.
101+
*
102+
* Example:
103+
* while(condition) {
104+
* spin_some();
105+
* sleep(); // User should have some sync work or
106+
* // sleep to avoid a 100% CPU usage
107+
* }
108+
*/
109+
RCLCPP_PUBLIC
110+
void
111+
spin_some(std::chrono::nanoseconds max_duration = std::chrono::nanoseconds(0)) override;
112+
113+
/// Events executor implementation of spin all
114+
/**
115+
* This non-blocking function will execute timers and events
116+
* until timeout or no more work available. If new ready-timers/events
117+
* arrive while executing work available, they will be executed
118+
* as long as the timeout hasn't expired.
119+
*
120+
* Example:
121+
* while(condition) {
122+
* spin_all();
123+
* sleep(); // User should have some sync work or
124+
* // sleep to avoid a 100% CPU usage
125+
* }
126+
*/
127+
RCLCPP_PUBLIC
128+
void
129+
spin_all(std::chrono::nanoseconds max_duration) override;
130+
131+
/// Add a node to the executor.
132+
/**
133+
* \sa rclcpp::Executor::add_node
134+
*/
135+
RCLCPP_PUBLIC
136+
void
137+
add_node(
138+
rclcpp::node_interfaces::NodeBaseInterface::SharedPtr node_ptr,
139+
bool notify = true) override;
140+
141+
/// Convenience function which takes Node and forwards NodeBaseInterface.
142+
/**
143+
* \sa rclcpp::EventsExecutor::add_node
144+
*/
145+
RCLCPP_PUBLIC
146+
void
147+
add_node(std::shared_ptr<rclcpp::Node> node_ptr, bool notify = true) override;
148+
149+
/// Remove a node from the executor.
150+
/**
151+
* \sa rclcpp::Executor::remove_node
152+
*/
153+
RCLCPP_PUBLIC
154+
void
155+
remove_node(
156+
rclcpp::node_interfaces::NodeBaseInterface::SharedPtr node_ptr,
157+
bool notify = true) override;
158+
159+
/// Convenience function which takes Node and forwards NodeBaseInterface.
160+
/**
161+
* \sa rclcpp::Executor::remove_node
162+
*/
163+
RCLCPP_PUBLIC
164+
void
165+
remove_node(std::shared_ptr<rclcpp::Node> node_ptr, bool notify = true) override;
166+
167+
/// Add a callback group to an executor.
168+
/**
169+
* \sa rclcpp::Executor::add_callback_group
170+
*/
171+
RCLCPP_PUBLIC
172+
void
173+
add_callback_group(
174+
rclcpp::CallbackGroup::SharedPtr group_ptr,
175+
rclcpp::node_interfaces::NodeBaseInterface::SharedPtr node_ptr,
176+
bool notify = true) override;
177+
178+
/// Remove callback group from the executor
179+
/**
180+
* \sa rclcpp::Executor::remove_callback_group
181+
*/
182+
RCLCPP_PUBLIC
183+
void
184+
remove_callback_group(
185+
rclcpp::CallbackGroup::SharedPtr group_ptr,
186+
bool notify = true) override;
187+
188+
/// Get callback groups that belong to executor.
189+
/**
190+
* \sa rclcpp::Executor::get_all_callback_groups()
191+
*/
192+
RCLCPP_PUBLIC
193+
std::vector<rclcpp::CallbackGroup::WeakPtr>
194+
get_all_callback_groups() override;
195+
196+
/// Get callback groups that belong to executor.
197+
/**
198+
* \sa rclcpp::Executor::get_manually_added_callback_groups()
199+
*/
200+
RCLCPP_PUBLIC
201+
std::vector<rclcpp::CallbackGroup::WeakPtr>
202+
get_manually_added_callback_groups() override;
203+
204+
/// Get callback groups that belong to executor.
205+
/**
206+
* \sa rclcpp::Executor::get_automatically_added_callback_groups_from_nodes()
207+
*/
208+
RCLCPP_PUBLIC
209+
std::vector<rclcpp::CallbackGroup::WeakPtr>
210+
get_automatically_added_callback_groups_from_nodes() override;
211+
212+
protected:
213+
/// Internal implementation of spin_once
214+
RCLCPP_PUBLIC
215+
void
216+
spin_once_impl(std::chrono::nanoseconds timeout) override;
217+
218+
/// Internal implementation of spin_some
219+
RCLCPP_PUBLIC
220+
void
221+
spin_some_impl(std::chrono::nanoseconds max_duration, bool exhaustive);
222+
223+
private:
224+
RCLCPP_DISABLE_COPY(EventsExecutor)
225+
226+
/// Execute a provided executor event if its associated entities are available
227+
void
228+
execute_event(const ExecutorEvent & event);
229+
230+
/// Collect entities from callback groups and refresh the current collection with them
231+
void
232+
refresh_current_collection_from_callback_groups();
233+
234+
/// Refresh the current collection using the provided new_collection
235+
void
236+
refresh_current_collection(const rclcpp::executors::ExecutorEntitiesCollection & new_collection);
237+
238+
/// Create a listener callback function for the provided entity
239+
std::function<void(size_t)>
240+
create_entity_callback(void * entity_key, ExecutorEventType type);
241+
242+
/// Create a listener callback function for the provided waitable entity
243+
std::function<void(size_t, int)>
244+
create_waitable_callback(const rclcpp::Waitable * waitable_id);
245+
246+
/// Utility to add the notify waitable to an entities collection
247+
void
248+
add_notify_waitable_to_collection(
249+
rclcpp::executors::ExecutorEntitiesCollection::WaitableCollection & collection);
250+
251+
/// Searches for the provided entity_id in the collection and returns the entity if valid
252+
template<typename CollectionType>
253+
typename CollectionType::EntitySharedPtr
254+
retrieve_entity(typename CollectionType::Key entity_id, CollectionType & collection)
255+
{
256+
// Check if the entity_id is in the collection
257+
auto it = collection.find(entity_id);
258+
if (it == collection.end()) {
259+
return nullptr;
260+
}
261+
262+
// Check if the entity associated with the entity_id is valid
263+
// and remove it from the collection if it isn't
264+
auto entity = it->second.entity.lock();
265+
if (!entity) {
266+
collection.erase(it);
267+
}
268+
269+
// Return the retrieved entity (this can be a nullptr if the entity was not valid)
270+
return entity;
271+
}
272+
273+
/// Queue where entities can push events
274+
rclcpp::experimental::executors::EventsQueue::UniquePtr events_queue_;
275+
276+
std::shared_ptr<rclcpp::executors::ExecutorEntitiesCollector> entities_collector_;
277+
std::shared_ptr<rclcpp::executors::ExecutorNotifyWaitable> notify_waitable_;
278+
279+
/// Mutex to protect the current_entities_collection_
280+
std::recursive_mutex collection_mutex_;
281+
std::shared_ptr<rclcpp::executors::ExecutorEntitiesCollection> current_entities_collection_;
282+
283+
/// Flag used to reduce the number of unnecessary waitable events
284+
std::atomic<bool> notify_waitable_event_pushed_ {false};
285+
286+
/// Timers manager used to track and/or execute associated timers
287+
std::shared_ptr<rclcpp::experimental::TimersManager> timers_manager_;
288+
};
289+
290+
} // namespace executors
291+
} // namespace experimental
292+
} // namespace rclcpp
293+
294+
#endif // RCLCPP__EXPERIMENTAL__EXECUTORS__EVENTS_EXECUTOR__EVENTS_EXECUTOR_HPP_
Lines changed: 46 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,46 @@
1+
// Copyright 2023 iRobot Corporation.
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__EXPERIMENTAL__EXECUTORS__EVENTS_EXECUTOR__EVENTS_EXECUTOR_EVENT_TYPES_HPP_
16+
#define RCLCPP__EXPERIMENTAL__EXECUTORS__EVENTS_EXECUTOR__EVENTS_EXECUTOR_EVENT_TYPES_HPP_
17+
18+
namespace rclcpp
19+
{
20+
namespace experimental
21+
{
22+
namespace executors
23+
{
24+
25+
enum ExecutorEventType
26+
{
27+
CLIENT_EVENT,
28+
SUBSCRIPTION_EVENT,
29+
SERVICE_EVENT,
30+
TIMER_EVENT,
31+
WAITABLE_EVENT
32+
};
33+
34+
struct ExecutorEvent
35+
{
36+
const void * entity_key;
37+
int waitable_data;
38+
ExecutorEventType type;
39+
size_t num_events;
40+
};
41+
42+
} // namespace executors
43+
} // namespace experimental
44+
} // namespace rclcpp
45+
46+
#endif // RCLCPP__EXPERIMENTAL__EXECUTORS__EVENTS_EXECUTOR__EVENTS_EXECUTOR_EVENT_TYPES_HPP_

0 commit comments

Comments
 (0)