Skip to content

Commit 48416d3

Browse files
committed
Add EventsExecutor
Signed-off-by: Alberto Soragna <[email protected]>
1 parent 66a2398 commit 48416d3

23 files changed

+3977
-19
lines changed

rclcpp/CMakeLists.txt

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -51,10 +51,13 @@ set(${PROJECT_NAME}_SRCS
5151
src/rclcpp/executor.cpp
5252
src/rclcpp/executors.cpp
5353
src/rclcpp/expand_topic_or_service_name.cpp
54+
src/rclcpp/executors/events_executor.cpp
55+
src/rclcpp/executors/events_executor_entities_collector.cpp
5456
src/rclcpp/executors/multi_threaded_executor.cpp
5557
src/rclcpp/executors/single_threaded_executor.cpp
5658
src/rclcpp/executors/static_executor_entities_collector.cpp
5759
src/rclcpp/executors/static_single_threaded_executor.cpp
60+
src/rclcpp/executors/timers_manager.cpp
5861
src/rclcpp/future_return_code.cpp
5962
src/rclcpp/graph_listener.cpp
6063
src/rclcpp/guard_condition.cpp

rclcpp/include/rclcpp/any_subscription_callback.hpp

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -188,6 +188,12 @@ class AnySubscriptionCallback
188188
ConstMessageSharedPtr message, const rclcpp::MessageInfo & message_info)
189189
{
190190
TRACEPOINT(callback_start, static_cast<const void *>(this), true);
191+
192+
// If the message is not valid, return.
193+
if (!message) {
194+
return;
195+
}
196+
191197
if (const_shared_ptr_callback_) {
192198
const_shared_ptr_callback_(message);
193199
} else if (const_shared_ptr_with_info_callback_) {
@@ -211,6 +217,12 @@ class AnySubscriptionCallback
211217
MessageUniquePtr message, const rclcpp::MessageInfo & message_info)
212218
{
213219
TRACEPOINT(callback_start, static_cast<const void *>(this), true);
220+
221+
// If the message is not valid, return.
222+
if (!message) {
223+
return;
224+
}
225+
214226
if (shared_ptr_callback_) {
215227
typename std::shared_ptr<MessageT> shared_message = std::move(message);
216228
shared_ptr_callback_(shared_message);

rclcpp/include/rclcpp/executor.hpp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -544,7 +544,7 @@ class Executor
544544
RCLCPP_DISABLE_COPY(Executor)
545545

546546
RCLCPP_PUBLIC
547-
void
547+
virtual void
548548
spin_once_impl(std::chrono::nanoseconds timeout);
549549

550550
typedef std::map<rclcpp::node_interfaces::NodeBaseInterface::WeakPtr,

rclcpp/include/rclcpp/executors.hpp

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@
1818
#include <future>
1919
#include <memory>
2020

21+
#include "rclcpp/executors/events_executor.hpp"
2122
#include "rclcpp/executors/multi_threaded_executor.hpp"
2223
#include "rclcpp/executors/single_threaded_executor.hpp"
2324
#include "rclcpp/executors/static_single_threaded_executor.hpp"
Lines changed: 68 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,68 @@
1+
// Copyright 2020 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__EVENT_WAITABLE_HPP_
16+
#define RCLCPP__EXECUTORS__EVENT_WAITABLE_HPP_
17+
18+
#include "rclcpp/waitable.hpp"
19+
20+
namespace rclcpp
21+
{
22+
namespace executors
23+
{
24+
25+
/**
26+
* @brief This class provides a wrapper around the waitable object, that is
27+
* meant to be used with the EventsExecutor.
28+
* The waitset related methods are stubbed out as they should not be called.
29+
* This class is abstract as the execute method of rclcpp::Waitable is not implemented.
30+
* Nodes who want to implement a custom EventWaitable, can derive from this class and override
31+
* the execute method.
32+
*/
33+
class EventWaitable : public rclcpp::Waitable
34+
{
35+
public:
36+
// Constructor
37+
RCLCPP_PUBLIC
38+
EventWaitable() = default;
39+
40+
// Destructor
41+
RCLCPP_PUBLIC
42+
virtual ~EventWaitable() = default;
43+
44+
// Stub API: not used by EventsExecutor
45+
RCLCPP_PUBLIC
46+
bool
47+
is_ready(rcl_wait_set_t * wait_set) final
48+
{
49+
(void)wait_set;
50+
throw std::runtime_error("EventWaitable can't be checked if it's ready");
51+
return false;
52+
}
53+
54+
// Stub API: not used by EventsExecutor
55+
RCLCPP_PUBLIC
56+
bool
57+
add_to_wait_set(rcl_wait_set_t * wait_set) final
58+
{
59+
(void)wait_set;
60+
throw std::runtime_error("EventWaitable can't be added to a wait_set");
61+
return false;
62+
}
63+
};
64+
65+
} // namespace executors
66+
} // namespace rclcpp
67+
68+
#endif // RCLCPP__EXECUTORS__EVENT_WAITABLE_HPP_
Lines changed: 259 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,259 @@
1+
// Copyright 2020 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__EVENTS_EXECUTOR_HPP_
16+
#define RCLCPP__EXECUTORS__EVENTS_EXECUTOR_HPP_
17+
18+
#include <chrono>
19+
#include <memory>
20+
#include <queue>
21+
#include <vector>
22+
23+
#include "rclcpp/executor.hpp"
24+
#include "rclcpp/executors/events_executor_entities_collector.hpp"
25+
#include "rclcpp/executors/events_executor_event_types.hpp"
26+
#include "rclcpp/executors/events_executor_notify_waitable.hpp"
27+
#include "rclcpp/executors/timers_manager.hpp"
28+
#include "rclcpp/experimental/buffers/events_queue.hpp"
29+
#include "rclcpp/experimental/buffers/simple_events_queue.hpp"
30+
#include "rclcpp/node.hpp"
31+
32+
#include "rmw/listener_callback_type.h"
33+
34+
namespace rclcpp
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+
* The RMW listener APIs are used to collect new events.
44+
*
45+
* This executor tries to reduce as much as possible the amount of maintenance operations.
46+
* This allows to use customized `EventsQueue` classes to achieve different goals such
47+
* as very low CPU usage, bounded memory requirement, determinism, etc.
48+
*
49+
* The executor uses a weak ownership model and it locks entities only while executing
50+
* their related events.
51+
*
52+
* To run this executor:
53+
* rclcpp::executors::EventsExecutor executor;
54+
* executor.add_node(node);
55+
* executor.spin();
56+
* executor.remove_node(node);
57+
*/
58+
class EventsExecutor : public rclcpp::Executor
59+
{
60+
friend class EventsExecutorEntitiesCollector;
61+
62+
public:
63+
RCLCPP_SMART_PTR_DEFINITIONS(EventsExecutor)
64+
65+
/// Default constructor. See the default constructor for Executor.
66+
/**
67+
* \param[in] events_queue The queue used to store events.
68+
* \param[in] options Options used to configure the executor.
69+
*/
70+
RCLCPP_PUBLIC
71+
explicit EventsExecutor(
72+
rclcpp::experimental::buffers::EventsQueue::UniquePtr events_queue = std::make_unique<
73+
rclcpp::experimental::buffers::SimpleEventsQueue>(),
74+
const rclcpp::ExecutorOptions & options = rclcpp::ExecutorOptions());
75+
76+
/// Default destrcutor.
77+
RCLCPP_PUBLIC
78+
virtual ~EventsExecutor() = default;
79+
80+
/// Events executor implementation of spin.
81+
/**
82+
* This function will block until work comes in, execute it, and keep blocking.
83+
* It will only be interrupted by a CTRL-C (managed by the global signal handler).
84+
* \throws std::runtime_error when spin() called while already spinning
85+
*/
86+
RCLCPP_PUBLIC
87+
void
88+
spin() override;
89+
90+
/// Events executor implementation of spin some
91+
/**
92+
* This non-blocking function will execute the timers and events
93+
* that were ready when this API was called, until timeout or no
94+
* more work available. New ready-timers/events arrived while
95+
* executing work, won't be taken into account here.
96+
*
97+
* Example:
98+
* while(condition) {
99+
* spin_some();
100+
* sleep(); // User should have some sync work or
101+
* // sleep to avoid a 100% CPU usage
102+
* }
103+
*/
104+
RCLCPP_PUBLIC
105+
void
106+
spin_some(std::chrono::nanoseconds max_duration = std::chrono::nanoseconds(0)) override;
107+
108+
/// Events executor implementation of spin all
109+
/**
110+
* This non-blocking function will execute timers and events
111+
* until timeout or no more work available. If new ready-timers/events
112+
* arrive while executing work available, they will be executed
113+
* as long as the timeout hasn't expired.
114+
*
115+
* Example:
116+
* while(condition) {
117+
* spin_all();
118+
* sleep(); // User should have some sync work or
119+
* // sleep to avoid a 100% CPU usage
120+
* }
121+
*/
122+
RCLCPP_PUBLIC
123+
void
124+
spin_all(std::chrono::nanoseconds max_duration) override;
125+
126+
/// Add a node to the executor.
127+
/**
128+
* \sa rclcpp::Executor::add_node
129+
*/
130+
RCLCPP_PUBLIC
131+
void
132+
add_node(
133+
rclcpp::node_interfaces::NodeBaseInterface::SharedPtr node_ptr,
134+
bool notify = true) override;
135+
136+
/// Convenience function which takes Node and forwards NodeBaseInterface.
137+
/**
138+
* \sa rclcpp::EventsExecutor::add_node
139+
*/
140+
RCLCPP_PUBLIC
141+
void
142+
add_node(std::shared_ptr<rclcpp::Node> node_ptr, bool notify = true) override;
143+
144+
/// Remove a node from the executor.
145+
/**
146+
* \sa rclcpp::Executor::remove_node
147+
*/
148+
RCLCPP_PUBLIC
149+
void
150+
remove_node(
151+
rclcpp::node_interfaces::NodeBaseInterface::SharedPtr node_ptr,
152+
bool notify = true) override;
153+
154+
/// Convenience function which takes Node and forwards NodeBaseInterface.
155+
/**
156+
* \sa rclcpp::Executor::remove_node
157+
*/
158+
RCLCPP_PUBLIC
159+
void
160+
remove_node(std::shared_ptr<rclcpp::Node> node_ptr, bool notify = true) override;
161+
162+
/// Add a callback group to an executor.
163+
/**
164+
* \sa rclcpp::Executor::add_callback_group
165+
*/
166+
void
167+
add_callback_group(
168+
rclcpp::CallbackGroup::SharedPtr group_ptr,
169+
rclcpp::node_interfaces::NodeBaseInterface::SharedPtr node_ptr,
170+
bool notify = true) override;
171+
172+
/// Remove callback group from the executor
173+
/**
174+
* \sa rclcpp::Executor::remove_callback_group
175+
*/
176+
RCLCPP_PUBLIC
177+
void
178+
remove_callback_group(
179+
rclcpp::CallbackGroup::SharedPtr group_ptr,
180+
bool notify = true) override;
181+
182+
RCLCPP_PUBLIC
183+
std::vector<rclcpp::CallbackGroup::WeakPtr>
184+
get_all_callback_groups() override;
185+
186+
/// Get callback groups that belong to executor.
187+
/**
188+
* \sa rclcpp::Executor::get_manually_added_callback_groups()
189+
*/
190+
RCLCPP_PUBLIC
191+
std::vector<rclcpp::CallbackGroup::WeakPtr>
192+
get_manually_added_callback_groups() override;
193+
194+
/// Get callback groups that belong to executor.
195+
/**
196+
* \sa rclcpp::Executor::get_automatically_added_callback_groups_from_nodes()
197+
*/
198+
RCLCPP_PUBLIC
199+
std::vector<rclcpp::CallbackGroup::WeakPtr>
200+
get_automatically_added_callback_groups_from_nodes() override;
201+
202+
protected:
203+
RCLCPP_PUBLIC
204+
void
205+
spin_once_impl(std::chrono::nanoseconds timeout) override;
206+
207+
RCLCPP_PUBLIC
208+
void
209+
spin_some_impl(std::chrono::nanoseconds max_duration, bool exhaustive);
210+
211+
private:
212+
RCLCPP_DISABLE_COPY(EventsExecutor)
213+
214+
// Executor callback: Push new events into the queue and trigger cv.
215+
// This function is called by the DDS entities when an event happened,
216+
// like a subscription receiving a message.
217+
static void
218+
push_event(const void * event_data)
219+
{
220+
if (!event_data) {
221+
throw std::runtime_error("Executor event data not valid.");
222+
}
223+
224+
auto data = static_cast<const executors::EventsExecutorCallbackData *>(event_data);
225+
226+
executors::EventsExecutor * this_executor = data->executor;
227+
228+
// Event queue mutex scope
229+
{
230+
std::unique_lock<std::mutex> lock(this_executor->push_mutex_);
231+
this_executor->events_queue_->push(data->event);
232+
}
233+
// Notify that the event queue has some events in it.
234+
this_executor->events_queue_cv_.notify_one();
235+
}
236+
237+
// Execute a single event
238+
RCLCPP_PUBLIC
239+
void
240+
execute_event(const ExecutorEvent & event);
241+
242+
// Queue where entities can push events
243+
rclcpp::experimental::buffers::EventsQueue::SharedPtr events_queue_;
244+
245+
EventsExecutorEntitiesCollector::SharedPtr entities_collector_;
246+
EventsExecutorNotifyWaitable::SharedPtr executor_notifier_;
247+
248+
// Mutex to protect the insertion of events in the queue
249+
std::mutex push_mutex_;
250+
// Variable used to notify when an event is added to the queue
251+
std::condition_variable events_queue_cv_;
252+
// Timers manager
253+
std::shared_ptr<TimersManager> timers_manager_;
254+
};
255+
256+
} // namespace executors
257+
} // namespace rclcpp
258+
259+
#endif // RCLCPP__EXECUTORS__EVENTS_EXECUTOR_HPP_

0 commit comments

Comments
 (0)