Skip to content

Commit d50b888

Browse files
author
Janosch Machowinski
committed
chore: Made Executor fully overloadable
This commit makes every public funciton virtual, and adds virtual impl function for the existing template functions. The goal of this commit is to be able to fully control the everything from a derived class.
1 parent 03e5f9a commit d50b888

File tree

3 files changed

+113
-79
lines changed

3 files changed

+113
-79
lines changed

rclcpp/include/rclcpp/executor.hpp

Lines changed: 31 additions & 71 deletions
Original file line numberDiff line numberDiff line change
@@ -242,42 +242,30 @@ class Executor
242242
* spin_node_once to block indefinitely (the default behavior). A timeout of 0 causes this
243243
* function to be non-blocking.
244244
*/
245-
template<typename RepT = int64_t, typename T = std::milli>
246-
void
245+
RCLCPP_PUBLIC
246+
virtual void
247247
spin_node_once(
248-
rclcpp::node_interfaces::NodeBaseInterface::SharedPtr node,
249-
std::chrono::duration<RepT, T> timeout = std::chrono::duration<RepT, T>(-1))
250-
{
251-
return spin_node_once_nanoseconds(
252-
node,
253-
std::chrono::duration_cast<std::chrono::nanoseconds>(timeout)
254-
);
255-
}
248+
const rclcpp::node_interfaces::NodeBaseInterface::SharedPtr & node,
249+
std::chrono::nanoseconds timeout = std::chrono::nanoseconds(-1));
256250

257251
/// Convenience function which takes Node and forwards NodeBaseInterface.
258-
template<typename NodeT = rclcpp::Node, typename RepT = int64_t, typename T = std::milli>
259-
void
252+
RCLCPP_PUBLIC
253+
virtual void
260254
spin_node_once(
261-
std::shared_ptr<NodeT> node,
262-
std::chrono::duration<RepT, T> timeout = std::chrono::duration<RepT, T>(-1))
263-
{
264-
return spin_node_once_nanoseconds(
265-
node->get_node_base_interface(),
266-
std::chrono::duration_cast<std::chrono::nanoseconds>(timeout)
267-
);
268-
}
255+
const std::shared_ptr<rclcpp::Node> & node,
256+
std::chrono::nanoseconds timeout = std::chrono::nanoseconds(-1));
269257

270258
/// Add a node, complete all immediately available work, and remove the node.
271259
/**
272260
* \param[in] node Shared pointer to the node to add.
273261
*/
274262
RCLCPP_PUBLIC
275-
void
263+
virtual void
276264
spin_node_some(rclcpp::node_interfaces::NodeBaseInterface::SharedPtr node);
277265

278266
/// Convenience function which takes Node and forwards NodeBaseInterface.
279267
RCLCPP_PUBLIC
280-
void
268+
virtual void
281269
spin_node_some(std::shared_ptr<rclcpp::Node> node);
282270

283271
/// Collect work once and execute all available work, optionally within a max duration.
@@ -307,14 +295,14 @@ class Executor
307295
* \param[in] node Shared pointer to the node to add.
308296
*/
309297
RCLCPP_PUBLIC
310-
void
298+
virtual void
311299
spin_node_all(
312300
rclcpp::node_interfaces::NodeBaseInterface::SharedPtr node,
313301
std::chrono::nanoseconds max_duration);
314302

315303
/// Convenience function which takes Node and forwards NodeBaseInterface.
316304
RCLCPP_PUBLIC
317-
void
305+
virtual void
318306
spin_node_all(std::shared_ptr<rclcpp::Node> node, std::chrono::nanoseconds max_duration);
319307

320308
/// Collect and execute work repeatedly within a duration or until no more work is available.
@@ -366,52 +354,11 @@ class Executor
366354
const FutureT & future,
367355
std::chrono::duration<TimeRepT, TimeT> timeout = std::chrono::duration<TimeRepT, TimeT>(-1))
368356
{
369-
// TODO(wjwwood): does not work recursively; can't call spin_node_until_future_complete
370-
// inside a callback executed by an executor.
371-
372-
// Check the future before entering the while loop.
373-
// If the future is already complete, don't try to spin.
374-
std::future_status status = future.wait_for(std::chrono::seconds(0));
375-
if (status == std::future_status::ready) {
376-
return FutureReturnCode::SUCCESS;
377-
}
378-
379-
auto end_time = std::chrono::steady_clock::now();
380-
std::chrono::nanoseconds timeout_ns = std::chrono::duration_cast<std::chrono::nanoseconds>(
381-
timeout);
382-
if (timeout_ns > std::chrono::nanoseconds::zero()) {
383-
end_time += timeout_ns;
384-
}
385-
std::chrono::nanoseconds timeout_left = timeout_ns;
386-
387-
if (spinning.exchange(true)) {
388-
throw std::runtime_error("spin_until_future_complete() called while already spinning");
357+
return spin_until_future_complete_impl(std::chrono::duration_cast<std::chrono::nanoseconds>(
358+
timeout), [&future] () {
359+
return future.wait_for(std::chrono::seconds(0));
389360
}
390-
RCPPUTILS_SCOPE_EXIT(this->spinning.store(false); );
391-
while (rclcpp::ok(this->context_) && spinning.load()) {
392-
// Do one item of work.
393-
spin_once_impl(timeout_left);
394-
395-
// Check if the future is set, return SUCCESS if it is.
396-
status = future.wait_for(std::chrono::seconds(0));
397-
if (status == std::future_status::ready) {
398-
return FutureReturnCode::SUCCESS;
399-
}
400-
// If the original timeout is < 0, then this is blocking, never TIMEOUT.
401-
if (timeout_ns < std::chrono::nanoseconds::zero()) {
402-
continue;
403-
}
404-
// Otherwise check if we still have time to wait, return TIMEOUT if not.
405-
auto now = std::chrono::steady_clock::now();
406-
if (now >= end_time) {
407-
return FutureReturnCode::TIMEOUT;
408-
}
409-
// Subtract the elapsed time from the original timeout.
410-
timeout_left = std::chrono::duration_cast<std::chrono::nanoseconds>(end_time - now);
411-
}
412-
413-
// The future did not complete before ok() returned false, return INTERRUPTED.
414-
return FutureReturnCode::INTERRUPTED;
361+
);
415362
}
416363

417364
/// Cancel any running spin* function, causing it to return.
@@ -420,7 +367,7 @@ class Executor
420367
* \throws std::runtime_error if there is an issue triggering the guard condition
421368
*/
422369
RCLCPP_PUBLIC
423-
void
370+
virtual void
424371
cancel();
425372

426373
/// Returns true if the executor is currently spinning.
@@ -429,10 +376,19 @@ class Executor
429376
* \return True if the executor is currently spinning.
430377
*/
431378
RCLCPP_PUBLIC
432-
bool
379+
virtual bool
433380
is_spinning();
434381

435382
protected:
383+
// constructor that will not setup any internals.
384+
/**
385+
* This constructor is intended to be used by any derived executor,
386+
* that explicitly does not want to use the default implementation provided
387+
* by this class. This constructor is guaranteed, to not modify the system
388+
* state.
389+
* */
390+
explicit Executor(const std::shared_ptr<rclcpp::Context> & context);
391+
436392
/// Add a node to executor, execute the next available unit of work, and remove the node.
437393
/**
438394
* Implementation of spin_node_once using std::chrono::nanoseconds
@@ -447,6 +403,10 @@ class Executor
447403
rclcpp::node_interfaces::NodeBaseInterface::SharedPtr node,
448404
std::chrono::nanoseconds timeout);
449405

406+
virtual FutureReturnCode spin_until_future_complete_impl(
407+
std::chrono::nanoseconds max_duration,
408+
const std::function<std::future_status ()> & get_future_status);
409+
450410
/// Collect work and execute available work, optionally within a duration.
451411
/**
452412
* Implementation of spin_some and spin_all.

rclcpp/src/rclcpp/executor.cpp

Lines changed: 82 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -50,6 +50,14 @@ static constexpr rclcpp::SubscriptionWaitSetMask kDefaultSubscriptionMask = {tru
5050

5151
class rclcpp::ExecutorImplementation {};
5252

53+
Executor::Executor(const std::shared_ptr<rclcpp::Context> & context)
54+
: spinning(false),
55+
entities_need_rebuild_(true),
56+
collector_(nullptr),
57+
wait_set_({}, {}, {}, {}, {}, {}, context)
58+
{
59+
}
60+
5361
Executor::Executor(const rclcpp::ExecutorOptions & options)
5462
: spinning(false),
5563
interrupt_guard_condition_(std::make_shared<rclcpp::GuardCondition>(options.context)),
@@ -229,6 +237,28 @@ Executor::remove_node(std::shared_ptr<rclcpp::Node> node_ptr, bool notify)
229237
this->remove_node(node_ptr->get_node_base_interface(), notify);
230238
}
231239

240+
void
241+
Executor::spin_node_once(
242+
const rclcpp::node_interfaces::NodeBaseInterface::SharedPtr & node,
243+
std::chrono::nanoseconds timeout)
244+
{
245+
return spin_node_once_nanoseconds(
246+
node,
247+
std::chrono::duration_cast<std::chrono::nanoseconds>(timeout)
248+
);
249+
}
250+
251+
void
252+
Executor::spin_node_once(
253+
const std::shared_ptr<rclcpp::Node> & node,
254+
std::chrono::nanoseconds timeout)
255+
{
256+
return spin_node_once_nanoseconds(
257+
node->get_node_base_interface(),
258+
timeout
259+
);
260+
}
261+
232262
void
233263
Executor::spin_node_once_nanoseconds(
234264
rclcpp::node_interfaces::NodeBaseInterface::SharedPtr node,
@@ -240,6 +270,58 @@ Executor::spin_node_once_nanoseconds(
240270
this->remove_node(node, false);
241271
}
242272

273+
rclcpp::FutureReturnCode Executor::spin_until_future_complete_impl(
274+
std::chrono::nanoseconds timeout,
275+
const std::function<std::future_status ()> & get_future_status)
276+
{
277+
// TODO(wjwwood): does not work recursively; can't call spin_node_until_future_complete
278+
// inside a callback executed by an executor.
279+
280+
// Check the future before entering the while loop.
281+
// If the future is already complete, don't try to spin.
282+
std::future_status status = get_future_status();
283+
if (status == std::future_status::ready) {
284+
return FutureReturnCode::SUCCESS;
285+
}
286+
287+
auto end_time = std::chrono::steady_clock::now();
288+
std::chrono::nanoseconds timeout_ns = std::chrono::duration_cast<std::chrono::nanoseconds>(
289+
timeout);
290+
if (timeout_ns > std::chrono::nanoseconds::zero()) {
291+
end_time += timeout_ns;
292+
}
293+
std::chrono::nanoseconds timeout_left = timeout_ns;
294+
295+
if (spinning.exchange(true)) {
296+
throw std::runtime_error("spin_until_future_complete() called while already spinning");
297+
}
298+
RCPPUTILS_SCOPE_EXIT(this->spinning.store(false); );
299+
while (rclcpp::ok(this->context_) && spinning.load()) {
300+
// Do one item of work.
301+
spin_once_impl(timeout_left);
302+
303+
// Check if the future is set, return SUCCESS if it is.
304+
status = get_future_status();
305+
if (status == std::future_status::ready) {
306+
return FutureReturnCode::SUCCESS;
307+
}
308+
// If the original timeout is < 0, then this is blocking, never TIMEOUT.
309+
if (timeout_ns < std::chrono::nanoseconds::zero()) {
310+
continue;
311+
}
312+
// Otherwise check if we still have time to wait, return TIMEOUT if not.
313+
auto now = std::chrono::steady_clock::now();
314+
if (now >= end_time) {
315+
return FutureReturnCode::TIMEOUT;
316+
}
317+
// Subtract the elapsed time from the original timeout.
318+
timeout_left = std::chrono::duration_cast<std::chrono::nanoseconds>(end_time - now);
319+
}
320+
321+
// The future did not complete before ok() returned false, return INTERRUPTED.
322+
return FutureReturnCode::INTERRUPTED;
323+
}
324+
243325
void
244326
Executor::spin_node_some(rclcpp::node_interfaces::NodeBaseInterface::SharedPtr node)
245327
{

rclcpp/test/rclcpp/test_clock.cpp

Lines changed: 0 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -26,14 +26,6 @@
2626

2727
using namespace std::chrono_literals;
2828

29-
enum class ClockType
30-
{
31-
RCL_STEADY_TIME,
32-
RCL_SYSTEM_TIME,
33-
RCL_ROS_TIME,
34-
};
35-
36-
3729
class TestClockWakeup : public ::testing::TestWithParam<rcl_clock_type_e>
3830
{
3931
public:

0 commit comments

Comments
 (0)