Compare commits
61 Commits
native_buf
...
mjcarroll/
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
f03f2c2828 | ||
|
|
aff46a4abf | ||
|
|
acfc0e29fb | ||
|
|
e3f692bf51 | ||
|
|
ad5931b129 | ||
|
|
80077ddb82 | ||
|
|
ffdb562927 | ||
|
|
039d2b19b5 | ||
|
|
ab3bbf4e16 | ||
|
|
2c3a36cdcc | ||
|
|
838d1ae214 | ||
|
|
49962fd9e2 | ||
|
|
64cba3b781 | ||
|
|
fcc33e9692 | ||
|
|
43c8f45407 | ||
|
|
d9a92061c5 | ||
|
|
855c64dc3f | ||
|
|
4b2e280e9e | ||
|
|
6379f0cfa0 | ||
|
|
3a80b86164 | ||
|
|
cd56124c14 | ||
|
|
1ad6ad66cf | ||
|
|
a2f397715e | ||
|
|
38387e0a29 | ||
|
|
7a81a8fb8a | ||
|
|
38c80fd352 | ||
|
|
31d25fc0f7 | ||
|
|
5c70cb6808 | ||
|
|
03471fc97a | ||
|
|
985c1f4e81 | ||
|
|
200f733a8f | ||
|
|
d2d271b8a0 | ||
|
|
0c3c8999a6 | ||
|
|
e52b2420d6 | ||
|
|
d8ff831e8f | ||
|
|
cd7aaba5ca | ||
|
|
20d3ccaf57 | ||
|
|
3db897ad2f | ||
|
|
ae9a845620 | ||
|
|
0c912b6a6a | ||
|
|
8782fffaf7 | ||
|
|
c4b658935f | ||
|
|
debe396b71 | ||
|
|
58093288f8 | ||
|
|
87f41bff1d | ||
|
|
0ae0bea1fa | ||
|
|
0a9c9a6403 | ||
|
|
1b1a9154d5 | ||
|
|
974e845582 | ||
|
|
6267741212 | ||
|
|
9dd48ce6c2 | ||
|
|
a6c4c1b435 | ||
|
|
653d1a3868 | ||
|
|
e173e5a62a | ||
|
|
9695eaaee7 | ||
|
|
89f210687d | ||
|
|
a524bf016a | ||
|
|
173ffd686f | ||
|
|
2426056b9c | ||
|
|
9099635103 | ||
|
|
2bf88de912 |
@@ -64,7 +64,6 @@ set(${PROJECT_NAME}_SRCS
|
|||||||
src/rclcpp/executors/executor_notify_waitable.cpp
|
src/rclcpp/executors/executor_notify_waitable.cpp
|
||||||
src/rclcpp/executors/multi_threaded_executor.cpp
|
src/rclcpp/executors/multi_threaded_executor.cpp
|
||||||
src/rclcpp/executors/single_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/executors/static_single_threaded_executor.cpp
|
||||||
src/rclcpp/expand_topic_or_service_name.cpp
|
src/rclcpp/expand_topic_or_service_name.cpp
|
||||||
src/rclcpp/experimental/executors/events_executor/events_executor.cpp
|
src/rclcpp/experimental/executors/events_executor/events_executor.cpp
|
||||||
|
|||||||
@@ -180,14 +180,42 @@ public:
|
|||||||
return _find_ptrs_if_impl<rclcpp::Waitable, Function>(func, waitable_ptrs_);
|
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
|
RCLCPP_PUBLIC
|
||||||
std::atomic_bool &
|
std::atomic_bool &
|
||||||
can_be_taken_from();
|
can_be_taken_from();
|
||||||
|
|
||||||
|
/// Get the group type.
|
||||||
|
/**
|
||||||
|
* \return the group type
|
||||||
|
*/
|
||||||
RCLCPP_PUBLIC
|
RCLCPP_PUBLIC
|
||||||
const CallbackGroupType &
|
const CallbackGroupType &
|
||||||
type() const;
|
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
|
RCLCPP_PUBLIC
|
||||||
void collect_all_ptrs(
|
void collect_all_ptrs(
|
||||||
std::function<void(const rclcpp::SubscriptionBase::SharedPtr &)> sub_func,
|
std::function<void(const rclcpp::SubscriptionBase::SharedPtr &)> sub_func,
|
||||||
|
|||||||
@@ -19,6 +19,7 @@
|
|||||||
#include <cassert>
|
#include <cassert>
|
||||||
#include <chrono>
|
#include <chrono>
|
||||||
#include <cstdlib>
|
#include <cstdlib>
|
||||||
|
#include <deque>
|
||||||
#include <iostream>
|
#include <iostream>
|
||||||
#include <list>
|
#include <list>
|
||||||
#include <map>
|
#include <map>
|
||||||
@@ -29,28 +30,27 @@
|
|||||||
|
|
||||||
#include "rcl/guard_condition.h"
|
#include "rcl/guard_condition.h"
|
||||||
#include "rcl/wait.h"
|
#include "rcl/wait.h"
|
||||||
|
#include "rclcpp/executors/executor_notify_waitable.hpp"
|
||||||
#include "rcpputils/scope_exit.hpp"
|
#include "rcpputils/scope_exit.hpp"
|
||||||
|
|
||||||
#include "rclcpp/context.hpp"
|
#include "rclcpp/context.hpp"
|
||||||
#include "rclcpp/contexts/default_context.hpp"
|
#include "rclcpp/contexts/default_context.hpp"
|
||||||
#include "rclcpp/guard_condition.hpp"
|
#include "rclcpp/guard_condition.hpp"
|
||||||
#include "rclcpp/executor_options.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/future_return_code.hpp"
|
||||||
#include "rclcpp/memory_strategies.hpp"
|
|
||||||
#include "rclcpp/memory_strategy.hpp"
|
|
||||||
#include "rclcpp/node_interfaces/node_base_interface.hpp"
|
#include "rclcpp/node_interfaces/node_base_interface.hpp"
|
||||||
#include "rclcpp/utilities.hpp"
|
#include "rclcpp/utilities.hpp"
|
||||||
#include "rclcpp/visibility_control.hpp"
|
#include "rclcpp/visibility_control.hpp"
|
||||||
|
#include "rclcpp/wait_set.hpp"
|
||||||
|
|
||||||
namespace rclcpp
|
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.
|
// Forward declaration is used in convenience method signature.
|
||||||
class Node;
|
class Node;
|
||||||
|
class ExecutorImplementation;
|
||||||
|
|
||||||
/// Coordinate the order and timing of available communication tasks.
|
/// Coordinate the order and timing of available communication tasks.
|
||||||
/**
|
/**
|
||||||
@@ -402,17 +402,6 @@ public:
|
|||||||
void
|
void
|
||||||
cancel();
|
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.
|
/// Returns true if the executor is currently spinning.
|
||||||
/**
|
/**
|
||||||
* This function can be called asynchronously from any thread.
|
* This function can be called asynchronously from any thread.
|
||||||
@@ -497,6 +486,11 @@ protected:
|
|||||||
static void
|
static void
|
||||||
execute_client(rclcpp::ClientBase::SharedPtr client);
|
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.
|
/// Block until more work becomes avilable or timeout is reached.
|
||||||
/**
|
/**
|
||||||
* Builds a set of waitable entities, which are passed to the middleware.
|
* Builds a set of waitable entities, which are passed to the middleware.
|
||||||
@@ -508,62 +502,6 @@ protected:
|
|||||||
void
|
void
|
||||||
wait_for_work(std::chrono::nanoseconds timeout = std::chrono::nanoseconds(-1));
|
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.
|
/// Check for executable in ready state and populate union structure.
|
||||||
/**
|
/**
|
||||||
* \param[out] any_executable populated union structure of ready executable
|
* \param[out] any_executable populated union structure of ready executable
|
||||||
@@ -574,33 +512,6 @@ protected:
|
|||||||
bool
|
bool
|
||||||
get_next_ready_executable(AnyExecutable & any_executable);
|
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.
|
/// Wait for executable in ready state and populate union structure.
|
||||||
/**
|
/**
|
||||||
* If an executable is ready, it will return immediately, otherwise
|
* If an executable is ready, it will return immediately, otherwise
|
||||||
@@ -618,21 +529,6 @@ protected:
|
|||||||
AnyExecutable & any_executable,
|
AnyExecutable & any_executable,
|
||||||
std::chrono::nanoseconds timeout = std::chrono::nanoseconds(-1));
|
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.
|
/// Spinning state, used to prevent multi threaded calls to spin and to cancel blocking spins.
|
||||||
std::atomic_bool spinning;
|
std::atomic_bool spinning;
|
||||||
|
|
||||||
@@ -642,16 +538,8 @@ protected:
|
|||||||
/// Guard condition for signaling the rmw layer to wake up for system shutdown.
|
/// Guard condition for signaling the rmw layer to wake up for system shutdown.
|
||||||
std::shared_ptr<rclcpp::GuardCondition> shutdown_guard_condition_;
|
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_;
|
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.
|
/// The context associated with this executor.
|
||||||
std::shared_ptr<rclcpp::Context> context_;
|
std::shared_ptr<rclcpp::Context> context_;
|
||||||
|
|
||||||
@@ -661,42 +549,39 @@ protected:
|
|||||||
virtual void
|
virtual void
|
||||||
spin_once_impl(std::chrono::nanoseconds timeout);
|
spin_once_impl(std::chrono::nanoseconds timeout);
|
||||||
|
|
||||||
typedef std::map<rclcpp::node_interfaces::NodeBaseInterface::WeakPtr,
|
/// Waitable containing guard conditions controlling the executor flow.
|
||||||
const rclcpp::GuardCondition *,
|
/**
|
||||||
std::owner_less<rclcpp::node_interfaces::NodeBaseInterface::WeakPtr>>
|
* This waitable contains the interrupt and shutdown guard condition, as well
|
||||||
WeakNodesToGuardConditionsMap;
|
* 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.
|
||||||
|
*/
|
||||||
|
std::shared_ptr<rclcpp::executors::ExecutorNotifyWaitable> notify_waitable_;
|
||||||
|
|
||||||
typedef std::map<rclcpp::CallbackGroup::WeakPtr,
|
std::atomic_bool entities_need_rebuild_;
|
||||||
const rclcpp::GuardCondition *,
|
|
||||||
std::owner_less<rclcpp::CallbackGroup::WeakPtr>>
|
|
||||||
WeakCallbackGroupsToGuardConditionsMap;
|
|
||||||
|
|
||||||
/// maps nodes to guard conditions
|
/// Collector used to associate executable entities from nodes and guard conditions
|
||||||
WeakNodesToGuardConditionsMap
|
rclcpp::executors::ExecutorEntitiesCollector collector_;
|
||||||
weak_nodes_to_guard_conditions_ RCPPUTILS_TSA_GUARDED_BY(mutex_);
|
|
||||||
|
|
||||||
/// maps callback groups to guard conditions
|
/// Waitset to be waited on.
|
||||||
WeakCallbackGroupsToGuardConditionsMap
|
rclcpp::WaitSet wait_set_ RCPPUTILS_TSA_GUARDED_BY(mutex_);
|
||||||
weak_groups_to_guard_conditions_ RCPPUTILS_TSA_GUARDED_BY(mutex_);
|
|
||||||
|
|
||||||
/// maps callback groups associated to nodes
|
/// Hold the current state of the collection being waited on by the waitset
|
||||||
WeakCallbackGroupsToNodesMap
|
rclcpp::executors::ExecutorEntitiesCollection current_collection_ RCPPUTILS_TSA_GUARDED_BY(
|
||||||
weak_groups_associated_with_executor_to_nodes_ RCPPUTILS_TSA_GUARDED_BY(mutex_);
|
mutex_);
|
||||||
|
|
||||||
/// maps callback groups to nodes associated with executor
|
/// Hold the current state of the notify waitable being waited on by the waitset
|
||||||
WeakCallbackGroupsToNodesMap
|
std::shared_ptr<rclcpp::executors::ExecutorNotifyWaitable> current_notify_waitable_
|
||||||
weak_groups_to_nodes_associated_with_executor_ RCPPUTILS_TSA_GUARDED_BY(mutex_);
|
RCPPUTILS_TSA_GUARDED_BY(mutex_);
|
||||||
|
|
||||||
/// maps all callback groups to nodes
|
/// Hold the list of executables currently available to be executed.
|
||||||
WeakCallbackGroupsToNodesMap
|
std::deque<rclcpp::AnyExecutable> ready_executables_ RCPPUTILS_TSA_GUARDED_BY(mutex_);
|
||||||
weak_groups_to_nodes_ 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_);
|
|
||||||
|
|
||||||
/// shutdown callback handle registered to Context
|
/// shutdown callback handle registered to Context
|
||||||
rclcpp::OnShutdownCallbackHandle shutdown_callback_handle_;
|
rclcpp::OnShutdownCallbackHandle shutdown_callback_handle_;
|
||||||
|
|
||||||
|
/// Pointer to implementation
|
||||||
|
std::unique_ptr<ExecutorImplementation> impl_;
|
||||||
};
|
};
|
||||||
|
|
||||||
} // namespace rclcpp
|
} // namespace rclcpp
|
||||||
|
|||||||
@@ -198,14 +198,15 @@ build_entities_collection(
|
|||||||
*
|
*
|
||||||
* \param[in] collection Collection of entities corresponding to the current wait set.
|
* \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.
|
* \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(
|
ready_executables(
|
||||||
const ExecutorEntitiesCollection & collection,
|
const ExecutorEntitiesCollection & collection,
|
||||||
rclcpp::WaitResult<rclcpp::WaitSet> & wait_result
|
rclcpp::WaitResult<rclcpp::WaitSet> & wait_result,
|
||||||
|
std::deque<rclcpp::AnyExecutable> & executables
|
||||||
);
|
);
|
||||||
|
|
||||||
} // namespace executors
|
} // namespace executors
|
||||||
} // namespace rclcpp
|
} // namespace rclcpp
|
||||||
|
|
||||||
|
|||||||
@@ -1,357 +0,0 @@
|
|||||||
// Copyright 2020 Open Source Robotics Foundation, Inc.
|
|
||||||
//
|
|
||||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
|
||||||
// you may not use this file except in compliance with the License.
|
|
||||||
// You may obtain a copy of the License at
|
|
||||||
//
|
|
||||||
// http://www.apache.org/licenses/LICENSE-2.0
|
|
||||||
//
|
|
||||||
// Unless required by applicable law or agreed to in writing, software
|
|
||||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
|
||||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
||||||
// See the License for the specific language governing permissions and
|
|
||||||
// limitations under the License.
|
|
||||||
|
|
||||||
#ifndef RCLCPP__EXECUTORS__STATIC_EXECUTOR_ENTITIES_COLLECTOR_HPP_
|
|
||||||
#define RCLCPP__EXECUTORS__STATIC_EXECUTOR_ENTITIES_COLLECTOR_HPP_
|
|
||||||
|
|
||||||
#include <chrono>
|
|
||||||
#include <list>
|
|
||||||
#include <map>
|
|
||||||
#include <memory>
|
|
||||||
#include <vector>
|
|
||||||
|
|
||||||
#include "rcl/guard_condition.h"
|
|
||||||
#include "rcl/wait.h"
|
|
||||||
|
|
||||||
#include "rclcpp/experimental/executable_list.hpp"
|
|
||||||
#include "rclcpp/macros.hpp"
|
|
||||||
#include "rclcpp/memory_strategy.hpp"
|
|
||||||
#include "rclcpp/visibility_control.hpp"
|
|
||||||
#include "rclcpp/waitable.hpp"
|
|
||||||
|
|
||||||
namespace rclcpp
|
|
||||||
{
|
|
||||||
namespace executors
|
|
||||||
{
|
|
||||||
typedef std::map<rclcpp::CallbackGroup::WeakPtr,
|
|
||||||
rclcpp::node_interfaces::NodeBaseInterface::WeakPtr,
|
|
||||||
std::owner_less<rclcpp::CallbackGroup::WeakPtr>> WeakCallbackGroupsToNodesMap;
|
|
||||||
|
|
||||||
class StaticExecutorEntitiesCollector final
|
|
||||||
: public rclcpp::Waitable,
|
|
||||||
public std::enable_shared_from_this<StaticExecutorEntitiesCollector>
|
|
||||||
{
|
|
||||||
public:
|
|
||||||
RCLCPP_SMART_PTR_DEFINITIONS(StaticExecutorEntitiesCollector)
|
|
||||||
|
|
||||||
// Constructor
|
|
||||||
RCLCPP_PUBLIC
|
|
||||||
StaticExecutorEntitiesCollector() = default;
|
|
||||||
|
|
||||||
// Destructor
|
|
||||||
RCLCPP_PUBLIC
|
|
||||||
~StaticExecutorEntitiesCollector();
|
|
||||||
|
|
||||||
/// Initialize StaticExecutorEntitiesCollector
|
|
||||||
/**
|
|
||||||
* \param p_wait_set A reference to the wait set to be used in the executor
|
|
||||||
* \param memory_strategy Shared pointer to the memory strategy to set.
|
|
||||||
* \throws std::runtime_error if memory strategy is null
|
|
||||||
*/
|
|
||||||
RCLCPP_PUBLIC
|
|
||||||
void
|
|
||||||
init(
|
|
||||||
rcl_wait_set_t * p_wait_set,
|
|
||||||
rclcpp::memory_strategy::MemoryStrategy::SharedPtr memory_strategy);
|
|
||||||
|
|
||||||
/// Finalize StaticExecutorEntitiesCollector to clear resources
|
|
||||||
RCLCPP_PUBLIC
|
|
||||||
bool
|
|
||||||
is_init() {return initialized_;}
|
|
||||||
|
|
||||||
RCLCPP_PUBLIC
|
|
||||||
void
|
|
||||||
fini();
|
|
||||||
|
|
||||||
/// Execute the waitable.
|
|
||||||
RCLCPP_PUBLIC
|
|
||||||
void
|
|
||||||
execute(std::shared_ptr<void> & data) override;
|
|
||||||
|
|
||||||
/// Take the data so that it can be consumed with `execute`.
|
|
||||||
/**
|
|
||||||
* For `StaticExecutorEntitiesCollector`, this always return `nullptr`.
|
|
||||||
* \sa rclcpp::Waitable::take_data()
|
|
||||||
*/
|
|
||||||
RCLCPP_PUBLIC
|
|
||||||
std::shared_ptr<void>
|
|
||||||
take_data() override;
|
|
||||||
|
|
||||||
/// Function to add_handles_to_wait_set and wait for work and
|
|
||||||
/**
|
|
||||||
* block until the wait set is ready or until the timeout has been exceeded.
|
|
||||||
* \throws std::runtime_error if wait set couldn't be cleared or filled.
|
|
||||||
* \throws any rcl errors from rcl_wait, \see rclcpp::exceptions::throw_from_rcl_error()
|
|
||||||
*/
|
|
||||||
RCLCPP_PUBLIC
|
|
||||||
void
|
|
||||||
refresh_wait_set(std::chrono::nanoseconds timeout = std::chrono::nanoseconds(-1));
|
|
||||||
|
|
||||||
/**
|
|
||||||
* \throws std::runtime_error if it couldn't add guard condition to wait set
|
|
||||||
*/
|
|
||||||
RCLCPP_PUBLIC
|
|
||||||
void
|
|
||||||
add_to_wait_set(rcl_wait_set_t * wait_set) override;
|
|
||||||
|
|
||||||
RCLCPP_PUBLIC
|
|
||||||
size_t
|
|
||||||
get_number_of_ready_guard_conditions() override;
|
|
||||||
|
|
||||||
/// Add a callback group to an executor.
|
|
||||||
/**
|
|
||||||
* \see rclcpp::Executor::add_callback_group
|
|
||||||
*/
|
|
||||||
RCLCPP_PUBLIC
|
|
||||||
bool
|
|
||||||
add_callback_group(
|
|
||||||
rclcpp::CallbackGroup::SharedPtr group_ptr,
|
|
||||||
rclcpp::node_interfaces::NodeBaseInterface::SharedPtr node_ptr);
|
|
||||||
|
|
||||||
/// Add a callback group to an executor.
|
|
||||||
/**
|
|
||||||
* \see rclcpp::Executor::add_callback_group
|
|
||||||
* \return boolean whether the node from the callback group is new
|
|
||||||
*/
|
|
||||||
RCLCPP_PUBLIC
|
|
||||||
bool
|
|
||||||
add_callback_group(
|
|
||||||
rclcpp::CallbackGroup::SharedPtr group_ptr,
|
|
||||||
rclcpp::node_interfaces::NodeBaseInterface::SharedPtr node_ptr,
|
|
||||||
WeakCallbackGroupsToNodesMap & weak_groups_to_nodes);
|
|
||||||
|
|
||||||
/// Remove a callback group from the executor.
|
|
||||||
/**
|
|
||||||
* \see rclcpp::Executor::remove_callback_group
|
|
||||||
*/
|
|
||||||
RCLCPP_PUBLIC
|
|
||||||
bool
|
|
||||||
remove_callback_group(
|
|
||||||
rclcpp::CallbackGroup::SharedPtr group_ptr);
|
|
||||||
|
|
||||||
/// Remove a callback group from the executor.
|
|
||||||
/**
|
|
||||||
* \see rclcpp::Executor::remove_callback_group_from_map
|
|
||||||
*/
|
|
||||||
RCLCPP_PUBLIC
|
|
||||||
bool
|
|
||||||
remove_callback_group_from_map(
|
|
||||||
rclcpp::CallbackGroup::SharedPtr group_ptr,
|
|
||||||
WeakCallbackGroupsToNodesMap & weak_groups_to_nodes);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* \see rclcpp::Executor::add_node()
|
|
||||||
* \throw std::runtime_error if node was already added
|
|
||||||
*/
|
|
||||||
RCLCPP_PUBLIC
|
|
||||||
bool
|
|
||||||
add_node(
|
|
||||||
rclcpp::node_interfaces::NodeBaseInterface::SharedPtr node_ptr);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* \see rclcpp::Executor::remove_node()
|
|
||||||
* \throw std::runtime_error if no guard condition is associated with node.
|
|
||||||
*/
|
|
||||||
RCLCPP_PUBLIC
|
|
||||||
bool
|
|
||||||
remove_node(
|
|
||||||
rclcpp::node_interfaces::NodeBaseInterface::SharedPtr node_ptr);
|
|
||||||
|
|
||||||
RCLCPP_PUBLIC
|
|
||||||
std::vector<rclcpp::CallbackGroup::WeakPtr>
|
|
||||||
get_all_callback_groups();
|
|
||||||
|
|
||||||
/// Get callback groups that belong to executor.
|
|
||||||
/**
|
|
||||||
* \see rclcpp::Executor::get_manually_added_callback_groups()
|
|
||||||
*/
|
|
||||||
RCLCPP_PUBLIC
|
|
||||||
std::vector<rclcpp::CallbackGroup::WeakPtr>
|
|
||||||
get_manually_added_callback_groups();
|
|
||||||
|
|
||||||
/// Get callback groups that belong to executor.
|
|
||||||
/**
|
|
||||||
* \see rclcpp::Executor::get_automatically_added_callback_groups_from_nodes()
|
|
||||||
*/
|
|
||||||
RCLCPP_PUBLIC
|
|
||||||
std::vector<rclcpp::CallbackGroup::WeakPtr>
|
|
||||||
get_automatically_added_callback_groups_from_nodes();
|
|
||||||
|
|
||||||
/// Complete all available queued work without blocking.
|
|
||||||
/**
|
|
||||||
* This function checks if after the guard condition was triggered
|
|
||||||
* (or a spurious wakeup happened) we are really ready to execute
|
|
||||||
* i.e. re-collect entities
|
|
||||||
*/
|
|
||||||
RCLCPP_PUBLIC
|
|
||||||
bool
|
|
||||||
is_ready(rcl_wait_set_t * wait_set) override;
|
|
||||||
|
|
||||||
/// Return number of timers
|
|
||||||
/**
|
|
||||||
* \return number of timers
|
|
||||||
*/
|
|
||||||
RCLCPP_PUBLIC
|
|
||||||
size_t
|
|
||||||
get_number_of_timers() {return exec_list_.number_of_timers;}
|
|
||||||
|
|
||||||
/// Return number of subscriptions
|
|
||||||
/**
|
|
||||||
* \return number of subscriptions
|
|
||||||
*/
|
|
||||||
RCLCPP_PUBLIC
|
|
||||||
size_t
|
|
||||||
get_number_of_subscriptions() {return exec_list_.number_of_subscriptions;}
|
|
||||||
|
|
||||||
/// Return number of services
|
|
||||||
/**
|
|
||||||
* \return number of services
|
|
||||||
*/
|
|
||||||
RCLCPP_PUBLIC
|
|
||||||
size_t
|
|
||||||
get_number_of_services() {return exec_list_.number_of_services;}
|
|
||||||
|
|
||||||
/// Return number of clients
|
|
||||||
/**
|
|
||||||
* \return number of clients
|
|
||||||
*/
|
|
||||||
RCLCPP_PUBLIC
|
|
||||||
size_t
|
|
||||||
get_number_of_clients() {return exec_list_.number_of_clients;}
|
|
||||||
|
|
||||||
/// Return number of waitables
|
|
||||||
/**
|
|
||||||
* \return number of waitables
|
|
||||||
*/
|
|
||||||
RCLCPP_PUBLIC
|
|
||||||
size_t
|
|
||||||
get_number_of_waitables() {return exec_list_.number_of_waitables;}
|
|
||||||
|
|
||||||
/** Return a SubscritionBase Sharedptr by index.
|
|
||||||
* \param[in] i The index of the SubscritionBase
|
|
||||||
* \return a SubscritionBase shared pointer
|
|
||||||
* \throws std::out_of_range if the argument is higher than the size of the structrue.
|
|
||||||
*/
|
|
||||||
RCLCPP_PUBLIC
|
|
||||||
rclcpp::SubscriptionBase::SharedPtr
|
|
||||||
get_subscription(size_t i) {return exec_list_.subscription[i];}
|
|
||||||
|
|
||||||
/** Return a TimerBase Sharedptr by index.
|
|
||||||
* \param[in] i The index of the TimerBase
|
|
||||||
* \return a TimerBase shared pointer
|
|
||||||
* \throws std::out_of_range if the argument is higher than the size.
|
|
||||||
*/
|
|
||||||
RCLCPP_PUBLIC
|
|
||||||
rclcpp::TimerBase::SharedPtr
|
|
||||||
get_timer(size_t i) {return exec_list_.timer[i];}
|
|
||||||
|
|
||||||
/** Return a ServiceBase Sharedptr by index.
|
|
||||||
* \param[in] i The index of the ServiceBase
|
|
||||||
* \return a ServiceBase shared pointer
|
|
||||||
* \throws std::out_of_range if the argument is higher than the size.
|
|
||||||
*/
|
|
||||||
RCLCPP_PUBLIC
|
|
||||||
rclcpp::ServiceBase::SharedPtr
|
|
||||||
get_service(size_t i) {return exec_list_.service[i];}
|
|
||||||
|
|
||||||
/** Return a ClientBase Sharedptr by index
|
|
||||||
* \param[in] i The index of the ClientBase
|
|
||||||
* \return a ClientBase shared pointer
|
|
||||||
* \throws std::out_of_range if the argument is higher than the size.
|
|
||||||
*/
|
|
||||||
RCLCPP_PUBLIC
|
|
||||||
rclcpp::ClientBase::SharedPtr
|
|
||||||
get_client(size_t i) {return exec_list_.client[i];}
|
|
||||||
|
|
||||||
/** Return a Waitable Sharedptr by index
|
|
||||||
* \param[in] i The index of the Waitable
|
|
||||||
* \return a Waitable shared pointer
|
|
||||||
* \throws std::out_of_range if the argument is higher than the size.
|
|
||||||
*/
|
|
||||||
RCLCPP_PUBLIC
|
|
||||||
rclcpp::Waitable::SharedPtr
|
|
||||||
get_waitable(size_t i) {return exec_list_.waitable[i];}
|
|
||||||
|
|
||||||
private:
|
|
||||||
/// Function to reallocate space for entities in the wait set.
|
|
||||||
/**
|
|
||||||
* \throws std::runtime_error if wait set couldn't be cleared or resized.
|
|
||||||
*/
|
|
||||||
void
|
|
||||||
prepare_wait_set();
|
|
||||||
|
|
||||||
void
|
|
||||||
fill_executable_list();
|
|
||||||
|
|
||||||
void
|
|
||||||
fill_memory_strategy();
|
|
||||||
|
|
||||||
/// Return true if the node belongs to the collector
|
|
||||||
/**
|
|
||||||
* \param[in] node_ptr a node base interface shared pointer
|
|
||||||
* \param[in] weak_groups_to_nodes map to nodes to lookup
|
|
||||||
* \return boolean whether a node belongs the collector
|
|
||||||
*/
|
|
||||||
bool
|
|
||||||
has_node(
|
|
||||||
const rclcpp::node_interfaces::NodeBaseInterface::SharedPtr node_ptr,
|
|
||||||
const WeakCallbackGroupsToNodesMap & weak_groups_to_nodes) const;
|
|
||||||
|
|
||||||
/// Add all callback groups that can be automatically added by any executor
|
|
||||||
/// and is not already associated with an executor from nodes
|
|
||||||
/// that are associated with executor
|
|
||||||
/**
|
|
||||||
* \see rclcpp::Executor::add_callback_groups_from_nodes_associated_to_executor()
|
|
||||||
*/
|
|
||||||
void
|
|
||||||
add_callback_groups_from_nodes_associated_to_executor();
|
|
||||||
|
|
||||||
void
|
|
||||||
fill_executable_list_from_map(const WeakCallbackGroupsToNodesMap & weak_groups_to_nodes);
|
|
||||||
|
|
||||||
/// Memory strategy: an interface for handling user-defined memory allocation strategies.
|
|
||||||
rclcpp::memory_strategy::MemoryStrategy::SharedPtr memory_strategy_;
|
|
||||||
|
|
||||||
// maps callback groups to nodes.
|
|
||||||
WeakCallbackGroupsToNodesMap weak_groups_associated_with_executor_to_nodes_;
|
|
||||||
// maps callback groups to nodes.
|
|
||||||
WeakCallbackGroupsToNodesMap weak_groups_to_nodes_associated_with_executor_;
|
|
||||||
|
|
||||||
typedef std::map<rclcpp::node_interfaces::NodeBaseInterface::WeakPtr,
|
|
||||||
const rclcpp::GuardCondition *,
|
|
||||||
std::owner_less<rclcpp::node_interfaces::NodeBaseInterface::WeakPtr>>
|
|
||||||
WeakNodesToGuardConditionsMap;
|
|
||||||
WeakNodesToGuardConditionsMap weak_nodes_to_guard_conditions_;
|
|
||||||
|
|
||||||
/// List of weak nodes registered in the static executor
|
|
||||||
std::list<rclcpp::node_interfaces::NodeBaseInterface::WeakPtr> weak_nodes_;
|
|
||||||
|
|
||||||
// Mutex to protect vector of new nodes.
|
|
||||||
std::mutex new_nodes_mutex_;
|
|
||||||
std::vector<rclcpp::node_interfaces::NodeBaseInterface::WeakPtr> new_nodes_;
|
|
||||||
|
|
||||||
/// Wait set for managing entities that the rmw layer waits on.
|
|
||||||
rcl_wait_set_t * p_wait_set_ = nullptr;
|
|
||||||
|
|
||||||
/// Executable list: timers, subscribers, clients, services and waitables
|
|
||||||
rclcpp::experimental::ExecutableList exec_list_;
|
|
||||||
|
|
||||||
/// Bool to check if the entities collector has been initialized
|
|
||||||
bool initialized_ = false;
|
|
||||||
};
|
|
||||||
|
|
||||||
} // namespace executors
|
|
||||||
} // namespace rclcpp
|
|
||||||
|
|
||||||
#endif // RCLCPP__EXECUTORS__STATIC_EXECUTOR_ENTITIES_COLLECTOR_HPP_
|
|
||||||
@@ -15,24 +15,13 @@
|
|||||||
#ifndef RCLCPP__EXECUTORS__STATIC_SINGLE_THREADED_EXECUTOR_HPP_
|
#ifndef RCLCPP__EXECUTORS__STATIC_SINGLE_THREADED_EXECUTOR_HPP_
|
||||||
#define RCLCPP__EXECUTORS__STATIC_SINGLE_THREADED_EXECUTOR_HPP_
|
#define RCLCPP__EXECUTORS__STATIC_SINGLE_THREADED_EXECUTOR_HPP_
|
||||||
|
|
||||||
|
#include <atomic>
|
||||||
#include <chrono>
|
#include <chrono>
|
||||||
#include <cassert>
|
|
||||||
#include <cstdlib>
|
|
||||||
#include <memory>
|
#include <memory>
|
||||||
#include <vector>
|
|
||||||
#include <string>
|
|
||||||
|
|
||||||
#include "rmw/rmw.h"
|
|
||||||
|
|
||||||
#include "rclcpp/executor.hpp"
|
#include "rclcpp/executor.hpp"
|
||||||
#include "rclcpp/executors/static_executor_entities_collector.hpp"
|
#include "rclcpp/executors/executor_entities_collection.hpp"
|
||||||
#include "rclcpp/experimental/executable_list.hpp"
|
#include "rclcpp/executors/single_threaded_executor.hpp"
|
||||||
#include "rclcpp/macros.hpp"
|
|
||||||
#include "rclcpp/memory_strategies.hpp"
|
|
||||||
#include "rclcpp/node.hpp"
|
|
||||||
#include "rclcpp/rate.hpp"
|
|
||||||
#include "rclcpp/utilities.hpp"
|
|
||||||
#include "rclcpp/visibility_control.hpp"
|
|
||||||
|
|
||||||
namespace rclcpp
|
namespace rclcpp
|
||||||
{
|
{
|
||||||
@@ -65,7 +54,7 @@ public:
|
|||||||
explicit StaticSingleThreadedExecutor(
|
explicit StaticSingleThreadedExecutor(
|
||||||
const rclcpp::ExecutorOptions & options = rclcpp::ExecutorOptions());
|
const rclcpp::ExecutorOptions & options = rclcpp::ExecutorOptions());
|
||||||
|
|
||||||
/// Default destrcutor.
|
/// Default destructor.
|
||||||
RCLCPP_PUBLIC
|
RCLCPP_PUBLIC
|
||||||
virtual ~StaticSingleThreadedExecutor();
|
virtual ~StaticSingleThreadedExecutor();
|
||||||
|
|
||||||
@@ -116,92 +105,20 @@ public:
|
|||||||
void
|
void
|
||||||
spin_all(std::chrono::nanoseconds max_duration) override;
|
spin_all(std::chrono::nanoseconds max_duration) override;
|
||||||
|
|
||||||
/// Add a callback group to an executor.
|
|
||||||
/**
|
|
||||||
* \sa rclcpp::Executor::add_callback_group
|
|
||||||
*/
|
|
||||||
RCLCPP_PUBLIC
|
|
||||||
void
|
|
||||||
add_callback_group(
|
|
||||||
rclcpp::CallbackGroup::SharedPtr group_ptr,
|
|
||||||
rclcpp::node_interfaces::NodeBaseInterface::SharedPtr node_ptr,
|
|
||||||
bool notify = true) override;
|
|
||||||
|
|
||||||
/// Remove callback group from the executor
|
|
||||||
/**
|
|
||||||
* \sa rclcpp::Executor::remove_callback_group
|
|
||||||
*/
|
|
||||||
RCLCPP_PUBLIC
|
|
||||||
void
|
|
||||||
remove_callback_group(
|
|
||||||
rclcpp::CallbackGroup::SharedPtr group_ptr,
|
|
||||||
bool notify = true) override;
|
|
||||||
|
|
||||||
/// Add a node to the executor.
|
|
||||||
/**
|
|
||||||
* \sa rclcpp::Executor::add_node
|
|
||||||
*/
|
|
||||||
RCLCPP_PUBLIC
|
|
||||||
void
|
|
||||||
add_node(
|
|
||||||
rclcpp::node_interfaces::NodeBaseInterface::SharedPtr node_ptr,
|
|
||||||
bool notify = true) override;
|
|
||||||
|
|
||||||
/// Convenience function which takes Node and forwards NodeBaseInterface.
|
|
||||||
/**
|
|
||||||
* \sa rclcpp::StaticSingleThreadedExecutor::add_node
|
|
||||||
*/
|
|
||||||
RCLCPP_PUBLIC
|
|
||||||
void
|
|
||||||
add_node(std::shared_ptr<rclcpp::Node> node_ptr, bool notify = true) override;
|
|
||||||
|
|
||||||
/// Remove a node from the executor.
|
|
||||||
/**
|
|
||||||
* \sa rclcpp::Executor::remove_node
|
|
||||||
*/
|
|
||||||
RCLCPP_PUBLIC
|
|
||||||
void
|
|
||||||
remove_node(
|
|
||||||
rclcpp::node_interfaces::NodeBaseInterface::SharedPtr node_ptr,
|
|
||||||
bool notify = true) override;
|
|
||||||
|
|
||||||
/// Convenience function which takes Node and forwards NodeBaseInterface.
|
|
||||||
/**
|
|
||||||
* \sa rclcpp::Executor::remove_node
|
|
||||||
*/
|
|
||||||
RCLCPP_PUBLIC
|
|
||||||
void
|
|
||||||
remove_node(std::shared_ptr<rclcpp::Node> node_ptr, bool notify = true) override;
|
|
||||||
|
|
||||||
RCLCPP_PUBLIC
|
|
||||||
std::vector<rclcpp::CallbackGroup::WeakPtr>
|
|
||||||
get_all_callback_groups() override;
|
|
||||||
|
|
||||||
/// Get callback groups that belong to executor.
|
|
||||||
/**
|
|
||||||
* \sa rclcpp::Executor::get_manually_added_callback_groups()
|
|
||||||
*/
|
|
||||||
RCLCPP_PUBLIC
|
|
||||||
std::vector<rclcpp::CallbackGroup::WeakPtr>
|
|
||||||
get_manually_added_callback_groups() override;
|
|
||||||
|
|
||||||
/// Get callback groups that belong to executor.
|
|
||||||
/**
|
|
||||||
* \sa rclcpp::Executor::get_automatically_added_callback_groups_from_nodes()
|
|
||||||
*/
|
|
||||||
RCLCPP_PUBLIC
|
|
||||||
std::vector<rclcpp::CallbackGroup::WeakPtr>
|
|
||||||
get_automatically_added_callback_groups_from_nodes() override;
|
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
/**
|
/**
|
||||||
* @brief Executes ready executables from wait set.
|
* @brief Executes ready executables from wait set.
|
||||||
|
* @param collection entities to evaluate for ready executables.
|
||||||
|
* @param wait_result result to check for ready executables.
|
||||||
* @param spin_once if true executes only the first ready executable.
|
* @param spin_once if true executes only the first ready executable.
|
||||||
* @return true if any executable was ready.
|
* @return true if any executable was ready.
|
||||||
*/
|
*/
|
||||||
RCLCPP_PUBLIC
|
RCLCPP_PUBLIC
|
||||||
bool
|
bool
|
||||||
execute_ready_executables(bool spin_once = false);
|
execute_ready_executables(
|
||||||
|
const rclcpp::executors::ExecutorEntitiesCollection & collection,
|
||||||
|
rclcpp::WaitResult<rclcpp::WaitSet> & wait_result,
|
||||||
|
bool spin_once);
|
||||||
|
|
||||||
RCLCPP_PUBLIC
|
RCLCPP_PUBLIC
|
||||||
void
|
void
|
||||||
@@ -213,8 +130,6 @@ protected:
|
|||||||
|
|
||||||
private:
|
private:
|
||||||
RCLCPP_DISABLE_COPY(StaticSingleThreadedExecutor)
|
RCLCPP_DISABLE_COPY(StaticSingleThreadedExecutor)
|
||||||
|
|
||||||
StaticExecutorEntitiesCollector::SharedPtr entities_collector_;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
} // namespace executors
|
} // namespace executors
|
||||||
|
|||||||
@@ -104,7 +104,7 @@ protected:
|
|||||||
// TODO(wjwwood): support custom allocator, maybe restrict to polymorphic allocator
|
// TODO(wjwwood): support custom allocator, maybe restrict to polymorphic allocator
|
||||||
rcl_get_default_allocator());
|
rcl_get_default_allocator());
|
||||||
if (RCL_RET_OK != ret) {
|
if (RCL_RET_OK != ret) {
|
||||||
rclcpp::exceptions::throw_from_rcl_error(ret);
|
rclcpp::exceptions::throw_from_rcl_error(ret, "Failed to create wait set");
|
||||||
}
|
}
|
||||||
|
|
||||||
// (Re)build the wait set for the first time.
|
// (Re)build the wait set for the first time.
|
||||||
@@ -192,8 +192,7 @@ protected:
|
|||||||
size_t services_from_waitables = 0;
|
size_t services_from_waitables = 0;
|
||||||
size_t events_from_waitables = 0;
|
size_t events_from_waitables = 0;
|
||||||
for (const auto & waitable_entry : waitables) {
|
for (const auto & waitable_entry : waitables) {
|
||||||
auto waitable_ptr_pair = get_raw_pointer_from_smart_pointer(waitable_entry.waitable);
|
if (!waitable_entry.waitable) {
|
||||||
if (nullptr == waitable_ptr_pair.second) {
|
|
||||||
// In this case it was probably stored as a weak_ptr, but is now locking to nullptr.
|
// In this case it was probably stored as a weak_ptr, but is now locking to nullptr.
|
||||||
if (HasStrongOwnership) {
|
if (HasStrongOwnership) {
|
||||||
// This will not happen in fixed sized storage, as it holds
|
// This will not happen in fixed sized storage, as it holds
|
||||||
@@ -204,13 +203,13 @@ protected:
|
|||||||
needs_pruning_ = true;
|
needs_pruning_ = true;
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
rclcpp::Waitable & waitable = *waitable_ptr_pair.second;
|
const auto & waitable = waitable_entry.waitable;
|
||||||
subscriptions_from_waitables += waitable.get_number_of_ready_subscriptions();
|
subscriptions_from_waitables += waitable->get_number_of_ready_subscriptions();
|
||||||
guard_conditions_from_waitables += waitable.get_number_of_ready_guard_conditions();
|
guard_conditions_from_waitables += waitable->get_number_of_ready_guard_conditions();
|
||||||
timers_from_waitables += waitable.get_number_of_ready_timers();
|
timers_from_waitables += waitable->get_number_of_ready_timers();
|
||||||
clients_from_waitables += waitable.get_number_of_ready_clients();
|
clients_from_waitables += waitable->get_number_of_ready_clients();
|
||||||
services_from_waitables += waitable.get_number_of_ready_services();
|
services_from_waitables += waitable->get_number_of_ready_services();
|
||||||
events_from_waitables += waitable.get_number_of_ready_events();
|
events_from_waitables += waitable->get_number_of_ready_events();
|
||||||
}
|
}
|
||||||
rcl_ret_t ret = rcl_wait_set_resize(
|
rcl_ret_t ret = rcl_wait_set_resize(
|
||||||
&rcl_wait_set_,
|
&rcl_wait_set_,
|
||||||
@@ -222,7 +221,7 @@ protected:
|
|||||||
events_from_waitables
|
events_from_waitables
|
||||||
);
|
);
|
||||||
if (RCL_RET_OK != ret) {
|
if (RCL_RET_OK != ret) {
|
||||||
rclcpp::exceptions::throw_from_rcl_error(ret);
|
rclcpp::exceptions::throw_from_rcl_error(ret, "Couldn't resize the wait set");
|
||||||
}
|
}
|
||||||
was_resized = true;
|
was_resized = true;
|
||||||
// Assumption: the calling code ensures this function is not called
|
// Assumption: the calling code ensures this function is not called
|
||||||
@@ -238,15 +237,13 @@ protected:
|
|||||||
if (!was_resized) {
|
if (!was_resized) {
|
||||||
rcl_ret_t ret = rcl_wait_set_clear(&rcl_wait_set_);
|
rcl_ret_t ret = rcl_wait_set_clear(&rcl_wait_set_);
|
||||||
if (RCL_RET_OK != ret) {
|
if (RCL_RET_OK != ret) {
|
||||||
rclcpp::exceptions::throw_from_rcl_error(ret);
|
rclcpp::exceptions::throw_from_rcl_error(ret, "Couldn't clear the wait set");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Add subscriptions.
|
// Add subscriptions.
|
||||||
for (const auto & subscription_entry : subscriptions) {
|
for (const auto & subscription_entry : subscriptions) {
|
||||||
auto subscription_ptr_pair =
|
if (!subscription_entry.subscription) {
|
||||||
get_raw_pointer_from_smart_pointer(subscription_entry.subscription);
|
|
||||||
if (nullptr == subscription_ptr_pair.second) {
|
|
||||||
// In this case it was probably stored as a weak_ptr, but is now locking to nullptr.
|
// In this case it was probably stored as a weak_ptr, but is now locking to nullptr.
|
||||||
if (HasStrongOwnership) {
|
if (HasStrongOwnership) {
|
||||||
// This will not happen in fixed sized storage, as it holds
|
// This will not happen in fixed sized storage, as it holds
|
||||||
@@ -257,12 +254,13 @@ protected:
|
|||||||
needs_pruning_ = true;
|
needs_pruning_ = true;
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
rcl_ret_t ret = rcl_wait_set_add_subscription(
|
rcl_ret_t ret = rcl_wait_set_add_subscription(
|
||||||
&rcl_wait_set_,
|
&rcl_wait_set_,
|
||||||
subscription_ptr_pair.second->get_subscription_handle().get(),
|
subscription_entry.subscription->get_subscription_handle().get(),
|
||||||
nullptr);
|
nullptr);
|
||||||
if (RCL_RET_OK != ret) {
|
if (RCL_RET_OK != ret) {
|
||||||
rclcpp::exceptions::throw_from_rcl_error(ret);
|
rclcpp::exceptions::throw_from_rcl_error(ret, "Couldn't fill wait set");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -271,8 +269,7 @@ protected:
|
|||||||
[this](const auto & inner_guard_conditions)
|
[this](const auto & inner_guard_conditions)
|
||||||
{
|
{
|
||||||
for (const auto & guard_condition : inner_guard_conditions) {
|
for (const auto & guard_condition : inner_guard_conditions) {
|
||||||
auto guard_condition_ptr_pair = get_raw_pointer_from_smart_pointer(guard_condition);
|
if (!guard_condition) {
|
||||||
if (nullptr == guard_condition_ptr_pair.second) {
|
|
||||||
// In this case it was probably stored as a weak_ptr, but is now locking to nullptr.
|
// In this case it was probably stored as a weak_ptr, but is now locking to nullptr.
|
||||||
if (HasStrongOwnership) {
|
if (HasStrongOwnership) {
|
||||||
// This will not happen in fixed sized storage, as it holds
|
// This will not happen in fixed sized storage, as it holds
|
||||||
@@ -285,10 +282,10 @@ protected:
|
|||||||
}
|
}
|
||||||
rcl_ret_t ret = rcl_wait_set_add_guard_condition(
|
rcl_ret_t ret = rcl_wait_set_add_guard_condition(
|
||||||
&rcl_wait_set_,
|
&rcl_wait_set_,
|
||||||
&guard_condition_ptr_pair.second->get_rcl_guard_condition(),
|
&guard_condition->get_rcl_guard_condition(),
|
||||||
nullptr);
|
nullptr);
|
||||||
if (RCL_RET_OK != ret) {
|
if (RCL_RET_OK != ret) {
|
||||||
rclcpp::exceptions::throw_from_rcl_error(ret);
|
rclcpp::exceptions::throw_from_rcl_error(ret, "Couldn't fill wait set");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
@@ -301,8 +298,7 @@ protected:
|
|||||||
|
|
||||||
// Add timers.
|
// Add timers.
|
||||||
for (const auto & timer : timers) {
|
for (const auto & timer : timers) {
|
||||||
auto timer_ptr_pair = get_raw_pointer_from_smart_pointer(timer);
|
if (!timer) {
|
||||||
if (nullptr == timer_ptr_pair.second) {
|
|
||||||
// In this case it was probably stored as a weak_ptr, but is now locking to nullptr.
|
// In this case it was probably stored as a weak_ptr, but is now locking to nullptr.
|
||||||
if (HasStrongOwnership) {
|
if (HasStrongOwnership) {
|
||||||
// This will not happen in fixed sized storage, as it holds
|
// This will not happen in fixed sized storage, as it holds
|
||||||
@@ -315,17 +311,16 @@ protected:
|
|||||||
}
|
}
|
||||||
rcl_ret_t ret = rcl_wait_set_add_timer(
|
rcl_ret_t ret = rcl_wait_set_add_timer(
|
||||||
&rcl_wait_set_,
|
&rcl_wait_set_,
|
||||||
timer_ptr_pair.second->get_timer_handle().get(),
|
timer->get_timer_handle().get(),
|
||||||
nullptr);
|
nullptr);
|
||||||
if (RCL_RET_OK != ret) {
|
if (RCL_RET_OK != ret) {
|
||||||
rclcpp::exceptions::throw_from_rcl_error(ret);
|
rclcpp::exceptions::throw_from_rcl_error(ret, "Couldn't fill wait set");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Add clients.
|
// Add clients.
|
||||||
for (const auto & client : clients) {
|
for (const auto & client : clients) {
|
||||||
auto client_ptr_pair = get_raw_pointer_from_smart_pointer(client);
|
if (!client) {
|
||||||
if (nullptr == client_ptr_pair.second) {
|
|
||||||
// In this case it was probably stored as a weak_ptr, but is now locking to nullptr.
|
// In this case it was probably stored as a weak_ptr, but is now locking to nullptr.
|
||||||
if (HasStrongOwnership) {
|
if (HasStrongOwnership) {
|
||||||
// This will not happen in fixed sized storage, as it holds
|
// This will not happen in fixed sized storage, as it holds
|
||||||
@@ -338,17 +333,17 @@ protected:
|
|||||||
}
|
}
|
||||||
rcl_ret_t ret = rcl_wait_set_add_client(
|
rcl_ret_t ret = rcl_wait_set_add_client(
|
||||||
&rcl_wait_set_,
|
&rcl_wait_set_,
|
||||||
client_ptr_pair.second->get_client_handle().get(),
|
client->get_client_handle().get(),
|
||||||
nullptr);
|
nullptr);
|
||||||
if (RCL_RET_OK != ret) {
|
if (RCL_RET_OK != ret) {
|
||||||
rclcpp::exceptions::throw_from_rcl_error(ret);
|
rclcpp::exceptions::throw_from_rcl_error(ret);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
// Add services.
|
// Add services.
|
||||||
for (const auto & service : services) {
|
for (const auto & service : services) {
|
||||||
auto service_ptr_pair = get_raw_pointer_from_smart_pointer(service);
|
if (!service) {
|
||||||
if (nullptr == service_ptr_pair.second) {
|
|
||||||
// In this case it was probably stored as a weak_ptr, but is now locking to nullptr.
|
// In this case it was probably stored as a weak_ptr, but is now locking to nullptr.
|
||||||
if (HasStrongOwnership) {
|
if (HasStrongOwnership) {
|
||||||
// This will not happen in fixed sized storage, as it holds
|
// This will not happen in fixed sized storage, as it holds
|
||||||
@@ -361,17 +356,16 @@ protected:
|
|||||||
}
|
}
|
||||||
rcl_ret_t ret = rcl_wait_set_add_service(
|
rcl_ret_t ret = rcl_wait_set_add_service(
|
||||||
&rcl_wait_set_,
|
&rcl_wait_set_,
|
||||||
service_ptr_pair.second->get_service_handle().get(),
|
service->get_service_handle().get(),
|
||||||
nullptr);
|
nullptr);
|
||||||
if (RCL_RET_OK != ret) {
|
if (RCL_RET_OK != ret) {
|
||||||
rclcpp::exceptions::throw_from_rcl_error(ret);
|
rclcpp::exceptions::throw_from_rcl_error(ret, "Couldn't fill wait set");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Add waitables.
|
// Add waitables.
|
||||||
for (auto & waitable_entry : waitables) {
|
for (auto & waitable_entry : waitables) {
|
||||||
auto waitable_ptr_pair = get_raw_pointer_from_smart_pointer(waitable_entry.waitable);
|
if (!waitable_entry.waitable) {
|
||||||
if (nullptr == waitable_ptr_pair.second) {
|
|
||||||
// In this case it was probably stored as a weak_ptr, but is now locking to nullptr.
|
// In this case it was probably stored as a weak_ptr, but is now locking to nullptr.
|
||||||
if (HasStrongOwnership) {
|
if (HasStrongOwnership) {
|
||||||
// This will not happen in fixed sized storage, as it holds
|
// This will not happen in fixed sized storage, as it holds
|
||||||
@@ -382,8 +376,7 @@ protected:
|
|||||||
needs_pruning_ = true;
|
needs_pruning_ = true;
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
rclcpp::Waitable & waitable = *waitable_ptr_pair.second;
|
waitable_entry.waitable->add_to_wait_set(&rcl_wait_set_);
|
||||||
waitable.add_to_wait_set(&rcl_wait_set_);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -204,15 +204,19 @@ public:
|
|||||||
void
|
void
|
||||||
storage_rebuild_rcl_wait_set(const ArrayOfExtraGuardConditions & extra_guard_conditions)
|
storage_rebuild_rcl_wait_set(const ArrayOfExtraGuardConditions & extra_guard_conditions)
|
||||||
{
|
{
|
||||||
|
this->storage_acquire_ownerships();
|
||||||
|
|
||||||
this->storage_rebuild_rcl_wait_set_with_sets(
|
this->storage_rebuild_rcl_wait_set_with_sets(
|
||||||
subscriptions_,
|
shared_subscriptions_,
|
||||||
guard_conditions_,
|
shared_guard_conditions_,
|
||||||
extra_guard_conditions,
|
extra_guard_conditions,
|
||||||
timers_,
|
shared_timers_,
|
||||||
clients_,
|
shared_clients_,
|
||||||
services_,
|
shared_services_,
|
||||||
waitables_
|
shared_waitables_
|
||||||
);
|
);
|
||||||
|
|
||||||
|
this->storage_release_ownerships();
|
||||||
}
|
}
|
||||||
|
|
||||||
template<class EntityT, class SequenceOfEntitiesT>
|
template<class EntityT, class SequenceOfEntitiesT>
|
||||||
@@ -407,6 +411,7 @@ public:
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
// Lock all the weak pointers and hold them until released.
|
// Lock all the weak pointers and hold them until released.
|
||||||
|
lock_all(subscriptions_, shared_subscriptions_);
|
||||||
lock_all(guard_conditions_, shared_guard_conditions_);
|
lock_all(guard_conditions_, shared_guard_conditions_);
|
||||||
lock_all(timers_, shared_timers_);
|
lock_all(timers_, shared_timers_);
|
||||||
lock_all(clients_, shared_clients_);
|
lock_all(clients_, shared_clients_);
|
||||||
@@ -438,6 +443,7 @@ public:
|
|||||||
shared_ptr.reset();
|
shared_ptr.reset();
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
reset_all(shared_subscriptions_);
|
||||||
reset_all(shared_guard_conditions_);
|
reset_all(shared_guard_conditions_);
|
||||||
reset_all(shared_timers_);
|
reset_all(shared_timers_);
|
||||||
reset_all(shared_clients_);
|
reset_all(shared_clients_);
|
||||||
|
|||||||
@@ -290,7 +290,7 @@ protected:
|
|||||||
return create_wait_result(WaitResultKind::Empty);
|
return create_wait_result(WaitResultKind::Empty);
|
||||||
} else {
|
} else {
|
||||||
// Some other error case, throw.
|
// Some other error case, throw.
|
||||||
rclcpp::exceptions::throw_from_rcl_error(ret);
|
rclcpp::exceptions::throw_from_rcl_error(ret, "rcl_wait() failed");
|
||||||
}
|
}
|
||||||
} while (should_loop());
|
} while (should_loop());
|
||||||
|
|
||||||
|
|||||||
@@ -16,13 +16,16 @@
|
|||||||
|
|
||||||
using rclcpp::AnyExecutable;
|
using rclcpp::AnyExecutable;
|
||||||
|
|
||||||
|
RCLCPP_PUBLIC
|
||||||
AnyExecutable::AnyExecutable()
|
AnyExecutable::AnyExecutable()
|
||||||
: subscription(nullptr),
|
: subscription(nullptr),
|
||||||
timer(nullptr),
|
timer(nullptr),
|
||||||
service(nullptr),
|
service(nullptr),
|
||||||
client(nullptr),
|
client(nullptr),
|
||||||
|
waitable(nullptr),
|
||||||
callback_group(nullptr),
|
callback_group(nullptr),
|
||||||
node_base(nullptr)
|
node_base(nullptr),
|
||||||
|
data(nullptr)
|
||||||
{}
|
{}
|
||||||
|
|
||||||
AnyExecutable::~AnyExecutable()
|
AnyExecutable::~AnyExecutable()
|
||||||
|
|||||||
@@ -56,6 +56,17 @@ CallbackGroup::type() const
|
|||||||
return type_;
|
return type_;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
size_t
|
||||||
|
CallbackGroup::size() const
|
||||||
|
{
|
||||||
|
return
|
||||||
|
subscription_ptrs_.size() +
|
||||||
|
service_ptrs_.size() +
|
||||||
|
client_ptrs_.size() +
|
||||||
|
timer_ptrs_.size() +
|
||||||
|
waitable_ptrs_.size();
|
||||||
|
}
|
||||||
|
|
||||||
void CallbackGroup::collect_all_ptrs(
|
void CallbackGroup::collect_all_ptrs(
|
||||||
std::function<void(const rclcpp::SubscriptionBase::SharedPtr &)> sub_func,
|
std::function<void(const rclcpp::SubscriptionBase::SharedPtr &)> sub_func,
|
||||||
std::function<void(const rclcpp::ServiceBase::SharedPtr &)> service_func,
|
std::function<void(const rclcpp::ServiceBase::SharedPtr &)> service_func,
|
||||||
|
|||||||
@@ -13,6 +13,8 @@
|
|||||||
// limitations under the License.
|
// limitations under the License.
|
||||||
|
|
||||||
#include <algorithm>
|
#include <algorithm>
|
||||||
|
#include <chrono>
|
||||||
|
#include <iterator>
|
||||||
#include <memory>
|
#include <memory>
|
||||||
#include <map>
|
#include <map>
|
||||||
#include <string>
|
#include <string>
|
||||||
@@ -22,13 +24,14 @@
|
|||||||
|
|
||||||
#include "rcl/allocator.h"
|
#include "rcl/allocator.h"
|
||||||
#include "rcl/error_handling.h"
|
#include "rcl/error_handling.h"
|
||||||
|
#include "rclcpp/executors/executor_notify_waitable.hpp"
|
||||||
|
#include "rclcpp/subscription_wait_set_mask.hpp"
|
||||||
#include "rcpputils/scope_exit.hpp"
|
#include "rcpputils/scope_exit.hpp"
|
||||||
|
|
||||||
#include "rclcpp/dynamic_typesupport/dynamic_message.hpp"
|
#include "rclcpp/dynamic_typesupport/dynamic_message.hpp"
|
||||||
#include "rclcpp/exceptions.hpp"
|
#include "rclcpp/exceptions.hpp"
|
||||||
#include "rclcpp/executor.hpp"
|
#include "rclcpp/executor.hpp"
|
||||||
#include "rclcpp/guard_condition.hpp"
|
#include "rclcpp/guard_condition.hpp"
|
||||||
#include "rclcpp/memory_strategy.hpp"
|
|
||||||
#include "rclcpp/node.hpp"
|
#include "rclcpp/node.hpp"
|
||||||
#include "rclcpp/utilities.hpp"
|
#include "rclcpp/utilities.hpp"
|
||||||
|
|
||||||
@@ -38,14 +41,25 @@
|
|||||||
|
|
||||||
using namespace std::chrono_literals;
|
using namespace std::chrono_literals;
|
||||||
|
|
||||||
using rclcpp::exceptions::throw_from_rcl_error;
|
|
||||||
using rclcpp::Executor;
|
using rclcpp::Executor;
|
||||||
|
class rclcpp::ExecutorImplementation {};
|
||||||
|
|
||||||
|
/// Mask to indicate to the waitset to only add the subscription.
|
||||||
|
/// The events and intraprocess waitable are already added via the callback group.
|
||||||
|
static constexpr rclcpp::SubscriptionWaitSetMask kDefaultSubscriptionMask = {true, false, false};
|
||||||
|
|
||||||
Executor::Executor(const rclcpp::ExecutorOptions & options)
|
Executor::Executor(const rclcpp::ExecutorOptions & options)
|
||||||
: spinning(false),
|
: spinning(false),
|
||||||
interrupt_guard_condition_(std::make_shared<rclcpp::GuardCondition>(options.context)),
|
interrupt_guard_condition_(std::make_shared<rclcpp::GuardCondition>(options.context)),
|
||||||
shutdown_guard_condition_(std::make_shared<rclcpp::GuardCondition>(options.context)),
|
shutdown_guard_condition_(std::make_shared<rclcpp::GuardCondition>(options.context)),
|
||||||
memory_strategy_(options.memory_strategy)
|
notify_waitable_(std::make_shared<rclcpp::executors::ExecutorNotifyWaitable>(
|
||||||
|
[this]() {
|
||||||
|
this->entities_need_rebuild_.store(true);
|
||||||
|
})),
|
||||||
|
collector_(notify_waitable_),
|
||||||
|
wait_set_({}, {}, {}, {}, {}, {}, options.context),
|
||||||
|
current_notify_waitable_(notify_waitable_),
|
||||||
|
impl_(std::make_unique<rclcpp::ExecutorImplementation>())
|
||||||
{
|
{
|
||||||
// Store the context for later use.
|
// Store the context for later use.
|
||||||
context_ = options.context;
|
context_ = options.context;
|
||||||
@@ -58,74 +72,56 @@ Executor::Executor(const rclcpp::ExecutorOptions & options)
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
// The number of guard conditions is always at least 2: 1 for the ctrl-c guard cond,
|
notify_waitable_->add_guard_condition(interrupt_guard_condition_);
|
||||||
// and one for the executor's guard cond (interrupt_guard_condition_)
|
notify_waitable_->add_guard_condition(shutdown_guard_condition_);
|
||||||
memory_strategy_->add_guard_condition(*shutdown_guard_condition_.get());
|
|
||||||
|
|
||||||
// Put the executor's guard condition in
|
|
||||||
memory_strategy_->add_guard_condition(*interrupt_guard_condition_.get());
|
|
||||||
rcl_allocator_t allocator = memory_strategy_->get_allocator();
|
|
||||||
|
|
||||||
rcl_ret_t ret = rcl_wait_set_init(
|
|
||||||
&wait_set_,
|
|
||||||
0, 2, 0, 0, 0, 0,
|
|
||||||
context_->get_rcl_context().get(),
|
|
||||||
allocator);
|
|
||||||
if (RCL_RET_OK != ret) {
|
|
||||||
RCUTILS_LOG_ERROR_NAMED(
|
|
||||||
"rclcpp",
|
|
||||||
"failed to create wait set: %s", rcl_get_error_string().str);
|
|
||||||
rcl_reset_error();
|
|
||||||
throw_from_rcl_error(ret, "Failed to create wait set in Executor constructor");
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
Executor::~Executor()
|
Executor::~Executor()
|
||||||
{
|
{
|
||||||
// Disassociate all callback groups.
|
std::lock_guard<std::mutex> guard(mutex_);
|
||||||
for (auto & pair : weak_groups_to_nodes_) {
|
|
||||||
auto group = pair.first.lock();
|
notify_waitable_->remove_guard_condition(interrupt_guard_condition_);
|
||||||
if (group) {
|
notify_waitable_->remove_guard_condition(shutdown_guard_condition_);
|
||||||
std::atomic_bool & has_executor = group->get_associated_with_executor_atomic();
|
|
||||||
has_executor.store(false);
|
std::cout << "Executor::~Executor" << std::endl;
|
||||||
}
|
|
||||||
}
|
current_collection_.timers.update(
|
||||||
// Disassociate all nodes.
|
{}, {},
|
||||||
std::for_each(
|
[this](auto timer) {
|
||||||
weak_nodes_.begin(), weak_nodes_.end(), []
|
std::cout << "remove_timer(" << timer.get() << ")" << std::endl;
|
||||||
(rclcpp::node_interfaces::NodeBaseInterface::WeakPtr weak_node_ptr) {
|
wait_set_.remove_timer(timer);
|
||||||
auto shared_node_ptr = weak_node_ptr.lock();
|
|
||||||
if (shared_node_ptr) {
|
|
||||||
std::atomic_bool & has_executor = shared_node_ptr->get_associated_with_executor_atomic();
|
|
||||||
has_executor.store(false);
|
|
||||||
}
|
|
||||||
});
|
});
|
||||||
weak_nodes_.clear();
|
|
||||||
weak_groups_associated_with_executor_to_nodes_.clear();
|
|
||||||
weak_groups_to_nodes_associated_with_executor_.clear();
|
|
||||||
weak_groups_to_nodes_.clear();
|
|
||||||
for (const auto & pair : weak_groups_to_guard_conditions_) {
|
|
||||||
auto guard_condition = pair.second;
|
|
||||||
memory_strategy_->remove_guard_condition(guard_condition);
|
|
||||||
}
|
|
||||||
weak_groups_to_guard_conditions_.clear();
|
|
||||||
|
|
||||||
for (const auto & pair : weak_nodes_to_guard_conditions_) {
|
current_collection_.subscriptions.update(
|
||||||
auto guard_condition = pair.second;
|
{}, {},
|
||||||
memory_strategy_->remove_guard_condition(guard_condition);
|
[this](auto subscription) {
|
||||||
}
|
std::cout << "remove_subscription(" << subscription.get() << ")" << std::endl;
|
||||||
weak_nodes_to_guard_conditions_.clear();
|
wait_set_.remove_subscription(subscription, kDefaultSubscriptionMask);
|
||||||
|
});
|
||||||
|
|
||||||
// Finalize the wait set.
|
current_collection_.clients.update(
|
||||||
if (rcl_wait_set_fini(&wait_set_) != RCL_RET_OK) {
|
{}, {},
|
||||||
RCUTILS_LOG_ERROR_NAMED(
|
[this](auto client) {
|
||||||
"rclcpp",
|
std::cout << "remove_client(" << client.get() << ")" << std::endl;
|
||||||
"failed to destroy wait set: %s", rcl_get_error_string().str);
|
wait_set_.remove_client(client);});
|
||||||
rcl_reset_error();
|
|
||||||
}
|
current_collection_.services.update(
|
||||||
// Remove and release the sigint guard condition
|
{}, {},
|
||||||
memory_strategy_->remove_guard_condition(shutdown_guard_condition_.get());
|
[this](auto service) {
|
||||||
memory_strategy_->remove_guard_condition(interrupt_guard_condition_.get());
|
std::cout << "remove_service(" << service.get() << ")" << std::endl;
|
||||||
|
wait_set_.remove_service(service);});
|
||||||
|
|
||||||
|
current_collection_.guard_conditions.update(
|
||||||
|
{}, {},
|
||||||
|
[this](auto guard_condition) {
|
||||||
|
std::cout << "remove_guard_condition(" << guard_condition.get() << ")" << std::endl;
|
||||||
|
wait_set_.remove_guard_condition(guard_condition);});
|
||||||
|
|
||||||
|
current_collection_.waitables.update(
|
||||||
|
{}, {},
|
||||||
|
[this](auto waitable) {
|
||||||
|
std::cout << "remove_waitable(" << waitable.get() << ")" << std::endl;
|
||||||
|
wait_set_.remove_waitable(waitable);});
|
||||||
|
|
||||||
// Remove shutdown callback handle registered to Context
|
// Remove shutdown callback handle registered to Context
|
||||||
if (!context_->remove_on_shutdown_callback(shutdown_callback_handle_)) {
|
if (!context_->remove_on_shutdown_callback(shutdown_callback_handle_)) {
|
||||||
@@ -139,95 +135,39 @@ Executor::~Executor()
|
|||||||
std::vector<rclcpp::CallbackGroup::WeakPtr>
|
std::vector<rclcpp::CallbackGroup::WeakPtr>
|
||||||
Executor::get_all_callback_groups()
|
Executor::get_all_callback_groups()
|
||||||
{
|
{
|
||||||
std::vector<rclcpp::CallbackGroup::WeakPtr> groups;
|
this->collector_.update_collections();
|
||||||
std::lock_guard<std::mutex> guard{mutex_};
|
return this->collector_.get_all_callback_groups();
|
||||||
for (const auto & group_node_ptr : weak_groups_associated_with_executor_to_nodes_) {
|
|
||||||
groups.push_back(group_node_ptr.first);
|
|
||||||
}
|
|
||||||
for (auto const & group_node_ptr : weak_groups_to_nodes_associated_with_executor_) {
|
|
||||||
groups.push_back(group_node_ptr.first);
|
|
||||||
}
|
|
||||||
return groups;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
std::vector<rclcpp::CallbackGroup::WeakPtr>
|
std::vector<rclcpp::CallbackGroup::WeakPtr>
|
||||||
Executor::get_manually_added_callback_groups()
|
Executor::get_manually_added_callback_groups()
|
||||||
{
|
{
|
||||||
std::vector<rclcpp::CallbackGroup::WeakPtr> groups;
|
this->collector_.update_collections();
|
||||||
std::lock_guard<std::mutex> guard{mutex_};
|
return this->collector_.get_manually_added_callback_groups();
|
||||||
for (auto const & group_node_ptr : weak_groups_associated_with_executor_to_nodes_) {
|
|
||||||
groups.push_back(group_node_ptr.first);
|
|
||||||
}
|
|
||||||
return groups;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
std::vector<rclcpp::CallbackGroup::WeakPtr>
|
std::vector<rclcpp::CallbackGroup::WeakPtr>
|
||||||
Executor::get_automatically_added_callback_groups_from_nodes()
|
Executor::get_automatically_added_callback_groups_from_nodes()
|
||||||
{
|
{
|
||||||
std::vector<rclcpp::CallbackGroup::WeakPtr> groups;
|
this->collector_.update_collections();
|
||||||
std::lock_guard<std::mutex> guard{mutex_};
|
return this->collector_.get_automatically_added_callback_groups();
|
||||||
for (auto const & group_node_ptr : weak_groups_to_nodes_associated_with_executor_) {
|
|
||||||
groups.push_back(group_node_ptr.first);
|
|
||||||
}
|
|
||||||
return groups;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void
|
void
|
||||||
Executor::add_callback_groups_from_nodes_associated_to_executor()
|
Executor::add_callback_group(
|
||||||
{
|
|
||||||
for (auto & weak_node : weak_nodes_) {
|
|
||||||
auto node = weak_node.lock();
|
|
||||||
if (node) {
|
|
||||||
node->for_each_callback_group(
|
|
||||||
[this, node](rclcpp::CallbackGroup::SharedPtr shared_group_ptr)
|
|
||||||
{
|
|
||||||
if (
|
|
||||||
shared_group_ptr->automatically_add_to_executor_with_node() &&
|
|
||||||
!shared_group_ptr->get_associated_with_executor_atomic().load())
|
|
||||||
{
|
|
||||||
this->add_callback_group_to_map(
|
|
||||||
shared_group_ptr,
|
|
||||||
node,
|
|
||||||
weak_groups_to_nodes_associated_with_executor_,
|
|
||||||
true);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void
|
|
||||||
Executor::add_callback_group_to_map(
|
|
||||||
rclcpp::CallbackGroup::SharedPtr group_ptr,
|
rclcpp::CallbackGroup::SharedPtr group_ptr,
|
||||||
rclcpp::node_interfaces::NodeBaseInterface::SharedPtr node_ptr,
|
rclcpp::node_interfaces::NodeBaseInterface::SharedPtr node_ptr,
|
||||||
rclcpp::memory_strategy::MemoryStrategy::WeakCallbackGroupsToNodesMap & weak_groups_to_nodes,
|
|
||||||
bool notify)
|
bool notify)
|
||||||
{
|
{
|
||||||
// If the callback_group already has an executor
|
(void) node_ptr;
|
||||||
std::atomic_bool & has_executor = group_ptr->get_associated_with_executor_atomic();
|
this->collector_.add_callback_group(group_ptr);
|
||||||
if (has_executor.exchange(true)) {
|
|
||||||
throw std::runtime_error("Callback group has already been added to an executor.");
|
|
||||||
}
|
|
||||||
|
|
||||||
rclcpp::CallbackGroup::WeakPtr weak_group_ptr = group_ptr;
|
if (!spinning.load()) {
|
||||||
auto insert_info =
|
std::lock_guard<std::mutex> guard(mutex_);
|
||||||
weak_groups_to_nodes.insert(std::make_pair(weak_group_ptr, node_ptr));
|
this->collect_entities();
|
||||||
bool was_inserted = insert_info.second;
|
|
||||||
if (!was_inserted) {
|
|
||||||
throw std::runtime_error("Callback group was already added to executor.");
|
|
||||||
}
|
|
||||||
// Also add to the map that contains all callback groups
|
|
||||||
weak_groups_to_nodes_.insert(std::make_pair(weak_group_ptr, node_ptr));
|
|
||||||
|
|
||||||
if (node_ptr->get_context()->is_valid()) {
|
|
||||||
auto callback_group_guard_condition = group_ptr->get_notify_guard_condition();
|
|
||||||
weak_groups_to_guard_conditions_[weak_group_ptr] = callback_group_guard_condition.get();
|
|
||||||
// Add the callback_group's notify condition to the guard condition handles
|
|
||||||
memory_strategy_->add_guard_condition(*callback_group_guard_condition);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (notify) {
|
if (notify) {
|
||||||
// Interrupt waiting to handle new node
|
|
||||||
try {
|
try {
|
||||||
interrupt_guard_condition_->trigger();
|
interrupt_guard_condition_->trigger();
|
||||||
} catch (const rclcpp::exceptions::RCLError & ex) {
|
} catch (const rclcpp::exceptions::RCLError & ex) {
|
||||||
@@ -238,91 +178,23 @@ Executor::add_callback_group_to_map(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void
|
|
||||||
Executor::add_callback_group(
|
|
||||||
rclcpp::CallbackGroup::SharedPtr group_ptr,
|
|
||||||
rclcpp::node_interfaces::NodeBaseInterface::SharedPtr node_ptr,
|
|
||||||
bool notify)
|
|
||||||
{
|
|
||||||
std::lock_guard<std::mutex> guard{mutex_};
|
|
||||||
this->add_callback_group_to_map(
|
|
||||||
group_ptr,
|
|
||||||
node_ptr,
|
|
||||||
weak_groups_associated_with_executor_to_nodes_,
|
|
||||||
notify);
|
|
||||||
}
|
|
||||||
|
|
||||||
void
|
void
|
||||||
Executor::add_node(rclcpp::node_interfaces::NodeBaseInterface::SharedPtr node_ptr, bool notify)
|
Executor::add_node(rclcpp::node_interfaces::NodeBaseInterface::SharedPtr node_ptr, bool notify)
|
||||||
{
|
{
|
||||||
// If the node already has an executor
|
this->collector_.add_node(node_ptr);
|
||||||
std::atomic_bool & has_executor = node_ptr->get_associated_with_executor_atomic();
|
|
||||||
if (has_executor.exchange(true)) {
|
if (!spinning.load()) {
|
||||||
throw std::runtime_error(
|
std::lock_guard<std::mutex> guard(mutex_);
|
||||||
std::string("Node '") + node_ptr->get_fully_qualified_name() +
|
this->collect_entities();
|
||||||
"' has already been added to an executor.");
|
|
||||||
}
|
}
|
||||||
std::lock_guard<std::mutex> guard{mutex_};
|
|
||||||
node_ptr->for_each_callback_group(
|
|
||||||
[this, node_ptr, notify](rclcpp::CallbackGroup::SharedPtr group_ptr)
|
|
||||||
{
|
|
||||||
if (!group_ptr->get_associated_with_executor_atomic().load() &&
|
|
||||||
group_ptr->automatically_add_to_executor_with_node())
|
|
||||||
{
|
|
||||||
this->add_callback_group_to_map(
|
|
||||||
group_ptr,
|
|
||||||
node_ptr,
|
|
||||||
weak_groups_to_nodes_associated_with_executor_,
|
|
||||||
notify);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
const auto gc = node_ptr->get_shared_notify_guard_condition();
|
if (notify) {
|
||||||
weak_nodes_to_guard_conditions_[node_ptr] = gc.get();
|
try {
|
||||||
// Add the node's notify condition to the guard condition handles
|
interrupt_guard_condition_->trigger();
|
||||||
memory_strategy_->add_guard_condition(*gc);
|
} catch (const rclcpp::exceptions::RCLError & ex) {
|
||||||
weak_nodes_.push_back(node_ptr);
|
throw std::runtime_error(
|
||||||
}
|
std::string(
|
||||||
|
"Failed to trigger guard condition on node add: ") + ex.what());
|
||||||
void
|
|
||||||
Executor::remove_callback_group_from_map(
|
|
||||||
rclcpp::CallbackGroup::SharedPtr group_ptr,
|
|
||||||
rclcpp::memory_strategy::MemoryStrategy::WeakCallbackGroupsToNodesMap & weak_groups_to_nodes,
|
|
||||||
bool notify)
|
|
||||||
{
|
|
||||||
rclcpp::node_interfaces::NodeBaseInterface::SharedPtr node_ptr;
|
|
||||||
rclcpp::CallbackGroup::WeakPtr weak_group_ptr = group_ptr;
|
|
||||||
auto iter = weak_groups_to_nodes.find(weak_group_ptr);
|
|
||||||
if (iter != weak_groups_to_nodes.end()) {
|
|
||||||
node_ptr = iter->second.lock();
|
|
||||||
if (node_ptr == nullptr) {
|
|
||||||
throw std::runtime_error("Node must not be deleted before its callback group(s).");
|
|
||||||
}
|
|
||||||
weak_groups_to_nodes.erase(iter);
|
|
||||||
weak_groups_to_nodes_.erase(group_ptr);
|
|
||||||
std::atomic_bool & has_executor = group_ptr->get_associated_with_executor_atomic();
|
|
||||||
has_executor.store(false);
|
|
||||||
} else {
|
|
||||||
throw std::runtime_error("Callback group needs to be associated with executor.");
|
|
||||||
}
|
|
||||||
// If the node was matched and removed, interrupt waiting.
|
|
||||||
if (!has_node(node_ptr, weak_groups_to_nodes_associated_with_executor_) &&
|
|
||||||
!has_node(node_ptr, weak_groups_associated_with_executor_to_nodes_))
|
|
||||||
{
|
|
||||||
auto iter = weak_groups_to_guard_conditions_.find(weak_group_ptr);
|
|
||||||
if (iter != weak_groups_to_guard_conditions_.end()) {
|
|
||||||
memory_strategy_->remove_guard_condition(iter->second);
|
|
||||||
}
|
|
||||||
weak_groups_to_guard_conditions_.erase(weak_group_ptr);
|
|
||||||
|
|
||||||
if (notify) {
|
|
||||||
try {
|
|
||||||
interrupt_guard_condition_->trigger();
|
|
||||||
} catch (const rclcpp::exceptions::RCLError & ex) {
|
|
||||||
throw std::runtime_error(
|
|
||||||
std::string(
|
|
||||||
"Failed to trigger guard condition on callback group remove: ") + ex.what());
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -332,11 +204,21 @@ Executor::remove_callback_group(
|
|||||||
rclcpp::CallbackGroup::SharedPtr group_ptr,
|
rclcpp::CallbackGroup::SharedPtr group_ptr,
|
||||||
bool notify)
|
bool notify)
|
||||||
{
|
{
|
||||||
std::lock_guard<std::mutex> guard{mutex_};
|
this->collector_.remove_callback_group(group_ptr);
|
||||||
this->remove_callback_group_from_map(
|
|
||||||
group_ptr,
|
if (!spinning.load()) {
|
||||||
weak_groups_associated_with_executor_to_nodes_,
|
std::lock_guard<std::mutex> guard(mutex_);
|
||||||
notify);
|
this->collect_entities();
|
||||||
|
}
|
||||||
|
if (notify) {
|
||||||
|
try {
|
||||||
|
interrupt_guard_condition_->trigger();
|
||||||
|
} catch (const rclcpp::exceptions::RCLError & ex) {
|
||||||
|
throw std::runtime_error(
|
||||||
|
std::string(
|
||||||
|
"Failed to trigger guard condition on callback group remove: ") + ex.what());
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void
|
void
|
||||||
@@ -348,48 +230,22 @@ Executor::add_node(std::shared_ptr<rclcpp::Node> node_ptr, bool notify)
|
|||||||
void
|
void
|
||||||
Executor::remove_node(rclcpp::node_interfaces::NodeBaseInterface::SharedPtr node_ptr, bool notify)
|
Executor::remove_node(rclcpp::node_interfaces::NodeBaseInterface::SharedPtr node_ptr, bool notify)
|
||||||
{
|
{
|
||||||
if (!node_ptr->get_associated_with_executor_atomic().load()) {
|
this->collector_.remove_node(node_ptr);
|
||||||
throw std::runtime_error("Node needs to be associated with an executor.");
|
|
||||||
|
if (!spinning.load()) {
|
||||||
|
std::lock_guard<std::mutex> guard(mutex_);
|
||||||
|
this->collect_entities();
|
||||||
}
|
}
|
||||||
|
|
||||||
std::lock_guard<std::mutex> guard{mutex_};
|
if (notify) {
|
||||||
bool found_node = false;
|
try {
|
||||||
auto node_it = weak_nodes_.begin();
|
interrupt_guard_condition_->trigger();
|
||||||
while (node_it != weak_nodes_.end()) {
|
} catch (const rclcpp::exceptions::RCLError & ex) {
|
||||||
bool matched = (node_it->lock() == node_ptr);
|
throw std::runtime_error(
|
||||||
if (matched) {
|
std::string(
|
||||||
found_node = true;
|
"Failed to trigger guard condition on node remove: ") + ex.what());
|
||||||
node_it = weak_nodes_.erase(node_it);
|
|
||||||
} else {
|
|
||||||
++node_it;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (!found_node) {
|
|
||||||
throw std::runtime_error("Node needs to be associated with this executor.");
|
|
||||||
}
|
|
||||||
|
|
||||||
for (auto it = weak_groups_to_nodes_associated_with_executor_.begin();
|
|
||||||
it != weak_groups_to_nodes_associated_with_executor_.end(); )
|
|
||||||
{
|
|
||||||
auto weak_node_ptr = it->second;
|
|
||||||
auto shared_node_ptr = weak_node_ptr.lock();
|
|
||||||
auto group_ptr = it->first.lock();
|
|
||||||
|
|
||||||
// Increment iterator before removing in case it's invalidated
|
|
||||||
it++;
|
|
||||||
if (shared_node_ptr == node_ptr) {
|
|
||||||
remove_callback_group_from_map(
|
|
||||||
group_ptr,
|
|
||||||
weak_groups_to_nodes_associated_with_executor_,
|
|
||||||
notify);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
memory_strategy_->remove_guard_condition(node_ptr->get_shared_notify_guard_condition().get());
|
|
||||||
weak_nodes_to_guard_conditions_.erase(node_ptr);
|
|
||||||
|
|
||||||
std::atomic_bool & has_executor = node_ptr->get_associated_with_executor_atomic();
|
|
||||||
has_executor.store(false);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void
|
void
|
||||||
@@ -456,21 +312,44 @@ Executor::spin_some_impl(std::chrono::nanoseconds max_duration, bool exhaustive)
|
|||||||
throw std::runtime_error("spin_some() called while already spinning");
|
throw std::runtime_error("spin_some() called while already spinning");
|
||||||
}
|
}
|
||||||
RCPPUTILS_SCOPE_EXIT(this->spinning.store(false); );
|
RCPPUTILS_SCOPE_EXIT(this->spinning.store(false); );
|
||||||
bool work_available = false;
|
|
||||||
|
size_t work_in_queue = 0;
|
||||||
|
bool has_waited = false;
|
||||||
|
|
||||||
|
{
|
||||||
|
std::lock_guard<std::mutex> lock(mutex_);
|
||||||
|
work_in_queue = ready_executables_.size();
|
||||||
|
}
|
||||||
|
// The logic below is to guarantee that we:
|
||||||
|
// a) run all of the work in the queue before we spin the first time
|
||||||
|
// b) spin at least once
|
||||||
|
// c) run all of the work in the queue after we spin
|
||||||
|
|
||||||
while (rclcpp::ok(context_) && spinning.load() && max_duration_not_elapsed()) {
|
while (rclcpp::ok(context_) && spinning.load() && max_duration_not_elapsed()) {
|
||||||
AnyExecutable any_exec;
|
AnyExecutable any_exec;
|
||||||
if (!work_available) {
|
if (work_in_queue > 0) {
|
||||||
wait_for_work(std::chrono::milliseconds::zero());
|
// If there is work in the queue, then execute it
|
||||||
}
|
// This covers the case that there are things left in the queue from a
|
||||||
if (get_next_ready_executable(any_exec)) {
|
// previous spin.
|
||||||
execute_any_executable(any_exec);
|
if (get_next_ready_executable(any_exec)) {
|
||||||
work_available = true;
|
execute_any_executable(any_exec);
|
||||||
} else {
|
}
|
||||||
if (!work_available || !exhaustive) {
|
} else if (!has_waited && !work_in_queue) {
|
||||||
break;
|
// Once the ready queue is empty, then we need to wait at least once.
|
||||||
|
wait_for_work(std::chrono::milliseconds(0));
|
||||||
|
has_waited = true;
|
||||||
|
} else if (has_waited && !work_in_queue) {
|
||||||
|
// Once we have emptied the ready queue, but have already waited:
|
||||||
|
if (!exhaustive) {
|
||||||
|
// In the case of spin some, then we can exit
|
||||||
|
break;
|
||||||
|
} else {
|
||||||
|
// In the case of spin all, then we will allow ourselves to wait again.
|
||||||
|
has_waited = false;
|
||||||
}
|
}
|
||||||
work_available = false;
|
|
||||||
}
|
}
|
||||||
|
std::lock_guard<std::mutex> lock(mutex_);
|
||||||
|
work_in_queue = ready_executables_.size();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -505,22 +384,13 @@ Executor::cancel()
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void
|
|
||||||
Executor::set_memory_strategy(rclcpp::memory_strategy::MemoryStrategy::SharedPtr memory_strategy)
|
|
||||||
{
|
|
||||||
if (memory_strategy == nullptr) {
|
|
||||||
throw std::runtime_error("Received NULL memory strategy in executor.");
|
|
||||||
}
|
|
||||||
std::lock_guard<std::mutex> guard{mutex_};
|
|
||||||
memory_strategy_ = memory_strategy;
|
|
||||||
}
|
|
||||||
|
|
||||||
void
|
void
|
||||||
Executor::execute_any_executable(AnyExecutable & any_exec)
|
Executor::execute_any_executable(AnyExecutable & any_exec)
|
||||||
{
|
{
|
||||||
if (!spinning.load()) {
|
if (!spinning.load()) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (any_exec.timer) {
|
if (any_exec.timer) {
|
||||||
TRACEPOINT(
|
TRACEPOINT(
|
||||||
rclcpp_executor_execute,
|
rclcpp_executor_execute,
|
||||||
@@ -542,16 +412,10 @@ Executor::execute_any_executable(AnyExecutable & any_exec)
|
|||||||
if (any_exec.waitable) {
|
if (any_exec.waitable) {
|
||||||
any_exec.waitable->execute(any_exec.data);
|
any_exec.waitable->execute(any_exec.data);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Reset the callback_group, regardless of type
|
// Reset the callback_group, regardless of type
|
||||||
any_exec.callback_group->can_be_taken_from().store(true);
|
if (any_exec.callback_group) {
|
||||||
// Wake the wait, because it may need to be recalculated or work that
|
any_exec.callback_group->can_be_taken_from().store(true);
|
||||||
// was previously blocked is now available.
|
|
||||||
try {
|
|
||||||
interrupt_guard_condition_->trigger();
|
|
||||||
} catch (const rclcpp::exceptions::RCLError & ex) {
|
|
||||||
throw std::runtime_error(
|
|
||||||
std::string(
|
|
||||||
"Failed to trigger guard condition from execute_any_executable: ") + ex.what());
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -720,228 +584,117 @@ Executor::execute_client(
|
|||||||
[&]() {client->handle_response(request_header, response);});
|
[&]() {client->handle_response(request_header, response);});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
Executor::collect_entities()
|
||||||
|
{
|
||||||
|
// Get the current list of available waitables from the collector.
|
||||||
|
rclcpp::executors::ExecutorEntitiesCollection collection;
|
||||||
|
this->collector_.update_collections();
|
||||||
|
auto callback_groups = this->collector_.get_all_callback_groups();
|
||||||
|
rclcpp::executors::build_entities_collection(callback_groups, collection);
|
||||||
|
|
||||||
|
// Make a copy of notify waitable so we can continue to mutate the original
|
||||||
|
// one outside of the execute loop.
|
||||||
|
// This prevents the collection of guard conditions in the waitable from changing
|
||||||
|
// while we are waiting on it.
|
||||||
|
if (notify_waitable_) {
|
||||||
|
current_notify_waitable_ = std::make_shared<rclcpp::executors::ExecutorNotifyWaitable>(
|
||||||
|
*notify_waitable_);
|
||||||
|
auto notify_waitable = std::static_pointer_cast<rclcpp::Waitable>(current_notify_waitable_);
|
||||||
|
collection.waitables.insert({notify_waitable.get(), {notify_waitable, {}}});
|
||||||
|
}
|
||||||
|
|
||||||
|
std::cout << "current_collection.timers: " << current_collection_.timers.size() << std::endl;
|
||||||
|
std::cout << "next_collection.timers: " << collection.timers.size() << std::endl;
|
||||||
|
// Update each of the groups of entities in the current collection, adding or removing
|
||||||
|
// from the wait set as necessary.
|
||||||
|
current_collection_.timers.update(
|
||||||
|
collection.timers,
|
||||||
|
[this](auto timer) {
|
||||||
|
std::cout << "add_timer(" << timer << ")" << std::endl;
|
||||||
|
wait_set_.add_timer(timer);},
|
||||||
|
[this](auto timer) {
|
||||||
|
std::cout << "remove_timer(" << timer << ")" << std::endl;
|
||||||
|
wait_set_.remove_timer(timer);});
|
||||||
|
|
||||||
|
std::cout << "current_collection.subscriptions: " << current_collection_.subscriptions.size() << std::endl;
|
||||||
|
std::cout << "next_collection.subscriptions: " << collection.subscriptions.size() << std::endl;
|
||||||
|
current_collection_.subscriptions.update(
|
||||||
|
collection.subscriptions,
|
||||||
|
[this](auto subscription) {
|
||||||
|
std::cout << "add_subscription(" << subscription << ")" << std::endl;
|
||||||
|
wait_set_.add_subscription(subscription, kDefaultSubscriptionMask);
|
||||||
|
},
|
||||||
|
[this](auto subscription) {
|
||||||
|
std::cout << "remove_subscription(" << subscription << ")" << std::endl;
|
||||||
|
wait_set_.remove_subscription(subscription, kDefaultSubscriptionMask);
|
||||||
|
});
|
||||||
|
|
||||||
|
current_collection_.clients.update(
|
||||||
|
collection.clients,
|
||||||
|
[this](auto client) {wait_set_.add_client(client);},
|
||||||
|
[this](auto client) {wait_set_.remove_client(client);});
|
||||||
|
|
||||||
|
current_collection_.services.update(
|
||||||
|
collection.services,
|
||||||
|
[this](auto service) {wait_set_.add_service(service);},
|
||||||
|
[this](auto service) {wait_set_.remove_service(service);});
|
||||||
|
|
||||||
|
current_collection_.guard_conditions.update(
|
||||||
|
collection.guard_conditions,
|
||||||
|
[this](auto guard_condition) {wait_set_.add_guard_condition(guard_condition);},
|
||||||
|
[this](auto guard_condition) {wait_set_.remove_guard_condition(guard_condition);});
|
||||||
|
|
||||||
|
current_collection_.waitables.update(
|
||||||
|
collection.waitables,
|
||||||
|
[this](auto waitable) {
|
||||||
|
wait_set_.add_waitable(waitable);
|
||||||
|
},
|
||||||
|
[this](auto waitable) {wait_set_.remove_waitable(waitable);});
|
||||||
|
|
||||||
|
this->entities_need_rebuild_.store(false);
|
||||||
|
}
|
||||||
|
|
||||||
void
|
void
|
||||||
Executor::wait_for_work(std::chrono::nanoseconds timeout)
|
Executor::wait_for_work(std::chrono::nanoseconds timeout)
|
||||||
{
|
{
|
||||||
TRACEPOINT(rclcpp_executor_wait_for_work, timeout.count());
|
TRACEPOINT(rclcpp_executor_wait_for_work, timeout.count());
|
||||||
{
|
|
||||||
std::lock_guard<std::mutex> guard(mutex_);
|
|
||||||
|
|
||||||
// Check weak_nodes_ to find any callback group that is not owned
|
std::lock_guard<std::mutex> guard(mutex_);
|
||||||
// by an executor and add it to the list of callbackgroups for
|
if (current_collection_.empty() || this->entities_need_rebuild_.load()) {
|
||||||
// collect entities. Also exchange to false so it is not
|
this->collect_entities();
|
||||||
// allowed to add to another executor
|
|
||||||
add_callback_groups_from_nodes_associated_to_executor();
|
|
||||||
|
|
||||||
// Collect the subscriptions and timers to be waited on
|
|
||||||
memory_strategy_->clear_handles();
|
|
||||||
bool has_invalid_weak_groups_or_nodes =
|
|
||||||
memory_strategy_->collect_entities(weak_groups_to_nodes_);
|
|
||||||
|
|
||||||
if (has_invalid_weak_groups_or_nodes) {
|
|
||||||
std::vector<rclcpp::CallbackGroup::WeakPtr> invalid_group_ptrs;
|
|
||||||
for (auto pair : weak_groups_to_nodes_) {
|
|
||||||
auto weak_group_ptr = pair.first;
|
|
||||||
auto weak_node_ptr = pair.second;
|
|
||||||
if (weak_group_ptr.expired() || weak_node_ptr.expired()) {
|
|
||||||
invalid_group_ptrs.push_back(weak_group_ptr);
|
|
||||||
auto node_guard_pair = weak_nodes_to_guard_conditions_.find(weak_node_ptr);
|
|
||||||
if (node_guard_pair != weak_nodes_to_guard_conditions_.end()) {
|
|
||||||
auto guard_condition = node_guard_pair->second;
|
|
||||||
weak_nodes_to_guard_conditions_.erase(weak_node_ptr);
|
|
||||||
memory_strategy_->remove_guard_condition(guard_condition);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
std::for_each(
|
|
||||||
invalid_group_ptrs.begin(), invalid_group_ptrs.end(),
|
|
||||||
[this](rclcpp::CallbackGroup::WeakPtr group_ptr) {
|
|
||||||
if (weak_groups_to_nodes_associated_with_executor_.find(group_ptr) !=
|
|
||||||
weak_groups_to_nodes_associated_with_executor_.end())
|
|
||||||
{
|
|
||||||
weak_groups_to_nodes_associated_with_executor_.erase(group_ptr);
|
|
||||||
}
|
|
||||||
if (weak_groups_associated_with_executor_to_nodes_.find(group_ptr) !=
|
|
||||||
weak_groups_associated_with_executor_to_nodes_.end())
|
|
||||||
{
|
|
||||||
weak_groups_associated_with_executor_to_nodes_.erase(group_ptr);
|
|
||||||
}
|
|
||||||
auto callback_guard_pair = weak_groups_to_guard_conditions_.find(group_ptr);
|
|
||||||
if (callback_guard_pair != weak_groups_to_guard_conditions_.end()) {
|
|
||||||
auto guard_condition = callback_guard_pair->second;
|
|
||||||
weak_groups_to_guard_conditions_.erase(group_ptr);
|
|
||||||
memory_strategy_->remove_guard_condition(guard_condition);
|
|
||||||
}
|
|
||||||
weak_groups_to_nodes_.erase(group_ptr);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
// clear wait set
|
|
||||||
rcl_ret_t ret = rcl_wait_set_clear(&wait_set_);
|
|
||||||
if (ret != RCL_RET_OK) {
|
|
||||||
throw_from_rcl_error(ret, "Couldn't clear wait set");
|
|
||||||
}
|
|
||||||
|
|
||||||
// The size of waitables are accounted for in size of the other entities
|
|
||||||
ret = rcl_wait_set_resize(
|
|
||||||
&wait_set_, memory_strategy_->number_of_ready_subscriptions(),
|
|
||||||
memory_strategy_->number_of_guard_conditions(), memory_strategy_->number_of_ready_timers(),
|
|
||||||
memory_strategy_->number_of_ready_clients(), memory_strategy_->number_of_ready_services(),
|
|
||||||
memory_strategy_->number_of_ready_events());
|
|
||||||
if (RCL_RET_OK != ret) {
|
|
||||||
throw_from_rcl_error(ret, "Couldn't resize the wait set");
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!memory_strategy_->add_handles_to_wait_set(&wait_set_)) {
|
|
||||||
throw std::runtime_error("Couldn't fill wait set");
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
rcl_ret_t status =
|
auto wait_result = wait_set_.wait(timeout);
|
||||||
rcl_wait(&wait_set_, std::chrono::duration_cast<std::chrono::nanoseconds>(timeout).count());
|
if (wait_result.kind() == WaitResultKind::Empty) {
|
||||||
if (status == RCL_RET_WAIT_SET_EMPTY) {
|
|
||||||
RCUTILS_LOG_WARN_NAMED(
|
RCUTILS_LOG_WARN_NAMED(
|
||||||
"rclcpp",
|
"rclcpp",
|
||||||
"empty wait set received in rcl_wait(). This should never happen.");
|
"empty wait set received in wait(). This should never happen.");
|
||||||
} else if (status != RCL_RET_OK && status != RCL_RET_TIMEOUT) {
|
|
||||||
using rclcpp::exceptions::throw_from_rcl_error;
|
|
||||||
throw_from_rcl_error(status, "rcl_wait() failed");
|
|
||||||
}
|
}
|
||||||
|
rclcpp::executors::ready_executables(current_collection_, wait_result, ready_executables_);
|
||||||
// check the null handles in the wait set and remove them from the handles in memory strategy
|
|
||||||
// for callback-based entities
|
|
||||||
std::lock_guard<std::mutex> guard(mutex_);
|
|
||||||
memory_strategy_->remove_null_handles(&wait_set_);
|
|
||||||
}
|
|
||||||
|
|
||||||
rclcpp::node_interfaces::NodeBaseInterface::SharedPtr
|
|
||||||
Executor::get_node_by_group(
|
|
||||||
const rclcpp::memory_strategy::MemoryStrategy::WeakCallbackGroupsToNodesMap &
|
|
||||||
weak_groups_to_nodes,
|
|
||||||
rclcpp::CallbackGroup::SharedPtr group)
|
|
||||||
{
|
|
||||||
if (!group) {
|
|
||||||
return nullptr;
|
|
||||||
}
|
|
||||||
rclcpp::CallbackGroup::WeakPtr weak_group_ptr(group);
|
|
||||||
const auto finder = weak_groups_to_nodes.find(weak_group_ptr);
|
|
||||||
if (finder != weak_groups_to_nodes.end()) {
|
|
||||||
rclcpp::node_interfaces::NodeBaseInterface::SharedPtr node_ptr = finder->second.lock();
|
|
||||||
return node_ptr;
|
|
||||||
}
|
|
||||||
return nullptr;
|
|
||||||
}
|
|
||||||
|
|
||||||
rclcpp::CallbackGroup::SharedPtr
|
|
||||||
Executor::get_group_by_timer(rclcpp::TimerBase::SharedPtr timer)
|
|
||||||
{
|
|
||||||
std::lock_guard<std::mutex> guard{mutex_};
|
|
||||||
for (const auto & pair : weak_groups_associated_with_executor_to_nodes_) {
|
|
||||||
auto group = pair.first.lock();
|
|
||||||
if (!group) {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
auto timer_ref = group->find_timer_ptrs_if(
|
|
||||||
[timer](const rclcpp::TimerBase::SharedPtr & timer_ptr) -> bool {
|
|
||||||
return timer_ptr == timer;
|
|
||||||
});
|
|
||||||
if (timer_ref) {
|
|
||||||
return group;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
for (const auto & pair : weak_groups_to_nodes_associated_with_executor_) {
|
|
||||||
auto group = pair.first.lock();
|
|
||||||
if (!group) {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
auto timer_ref = group->find_timer_ptrs_if(
|
|
||||||
[timer](const rclcpp::TimerBase::SharedPtr & timer_ptr) -> bool {
|
|
||||||
return timer_ptr == timer;
|
|
||||||
});
|
|
||||||
if (timer_ref) {
|
|
||||||
return group;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return nullptr;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
bool
|
bool
|
||||||
Executor::get_next_ready_executable(AnyExecutable & any_executable)
|
Executor::get_next_ready_executable(AnyExecutable & any_executable)
|
||||||
{
|
|
||||||
bool success = get_next_ready_executable_from_map(any_executable, weak_groups_to_nodes_);
|
|
||||||
return success;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool
|
|
||||||
Executor::get_next_ready_executable_from_map(
|
|
||||||
AnyExecutable & any_executable,
|
|
||||||
const rclcpp::memory_strategy::MemoryStrategy::WeakCallbackGroupsToNodesMap &
|
|
||||||
weak_groups_to_nodes)
|
|
||||||
{
|
{
|
||||||
TRACEPOINT(rclcpp_executor_get_next_ready);
|
TRACEPOINT(rclcpp_executor_get_next_ready);
|
||||||
bool success = false;
|
|
||||||
std::lock_guard<std::mutex> guard{mutex_};
|
std::lock_guard<std::mutex> guard(mutex_);
|
||||||
// Check the timers to see if there are any that are ready
|
if (ready_executables_.size() == 0) {
|
||||||
memory_strategy_->get_next_timer(any_executable, weak_groups_to_nodes);
|
return false;
|
||||||
if (any_executable.timer) {
|
|
||||||
success = true;
|
|
||||||
}
|
|
||||||
if (!success) {
|
|
||||||
// Check the subscriptions to see if there are any that are ready
|
|
||||||
memory_strategy_->get_next_subscription(any_executable, weak_groups_to_nodes);
|
|
||||||
if (any_executable.subscription) {
|
|
||||||
success = true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (!success) {
|
|
||||||
// Check the services to see if there are any that are ready
|
|
||||||
memory_strategy_->get_next_service(any_executable, weak_groups_to_nodes);
|
|
||||||
if (any_executable.service) {
|
|
||||||
success = true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (!success) {
|
|
||||||
// Check the clients to see if there are any that are ready
|
|
||||||
memory_strategy_->get_next_client(any_executable, weak_groups_to_nodes);
|
|
||||||
if (any_executable.client) {
|
|
||||||
success = true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (!success) {
|
|
||||||
// Check the waitables to see if there are any that are ready
|
|
||||||
memory_strategy_->get_next_waitable(any_executable, weak_groups_to_nodes);
|
|
||||||
if (any_executable.waitable) {
|
|
||||||
any_executable.data = any_executable.waitable->take_data();
|
|
||||||
success = true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
// At this point any_executable should be valid with either a valid subscription
|
|
||||||
// or a valid timer, or it should be a null shared_ptr
|
|
||||||
if (success) {
|
|
||||||
rclcpp::CallbackGroup::WeakPtr weak_group_ptr = any_executable.callback_group;
|
|
||||||
auto iter = weak_groups_to_nodes.find(weak_group_ptr);
|
|
||||||
if (iter == weak_groups_to_nodes.end()) {
|
|
||||||
success = false;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (success) {
|
any_executable = ready_executables_.front();
|
||||||
// If it is valid, check to see if the group is mutually exclusive or
|
ready_executables_.pop_front();
|
||||||
// not, then mark it accordingly ..Check if the callback_group belongs to this executor
|
|
||||||
if (any_executable.callback_group && any_executable.callback_group->type() == \
|
if (any_executable.callback_group &&
|
||||||
CallbackGroupType::MutuallyExclusive)
|
any_executable.callback_group->type() == CallbackGroupType::MutuallyExclusive)
|
||||||
{
|
{
|
||||||
// It should not have been taken otherwise
|
assert(any_executable.callback_group->can_be_taken_from().load());
|
||||||
assert(any_executable.callback_group->can_be_taken_from().load());
|
any_executable.callback_group->can_be_taken_from().store(false);
|
||||||
// Set to false to indicate something is being run from this group
|
|
||||||
// This is reset to true either when the any_executable is executed or when the
|
|
||||||
// any_executable is destructued
|
|
||||||
any_executable.callback_group->can_be_taken_from().store(false);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
// If there is no ready executable, return false
|
|
||||||
return success;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool
|
bool
|
||||||
@@ -964,22 +717,6 @@ Executor::get_next_executable(AnyExecutable & any_executable, std::chrono::nanos
|
|||||||
return success;
|
return success;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Returns true iff the weak_groups_to_nodes map has node_ptr as the value in any of its entry.
|
|
||||||
bool
|
|
||||||
Executor::has_node(
|
|
||||||
const rclcpp::node_interfaces::NodeBaseInterface::SharedPtr node_ptr,
|
|
||||||
const rclcpp::memory_strategy::MemoryStrategy::WeakCallbackGroupsToNodesMap &
|
|
||||||
weak_groups_to_nodes) const
|
|
||||||
{
|
|
||||||
return std::find_if(
|
|
||||||
weak_groups_to_nodes.begin(),
|
|
||||||
weak_groups_to_nodes.end(),
|
|
||||||
[&](const WeakCallbackGroupsToNodesMap::value_type & other) -> bool {
|
|
||||||
auto other_ptr = other.second.lock();
|
|
||||||
return other_ptr == node_ptr;
|
|
||||||
}) != weak_groups_to_nodes.end();
|
|
||||||
}
|
|
||||||
|
|
||||||
bool
|
bool
|
||||||
Executor::is_spinning()
|
Executor::is_spinning()
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -20,12 +20,13 @@ namespace executors
|
|||||||
{
|
{
|
||||||
bool ExecutorEntitiesCollection::empty() const
|
bool ExecutorEntitiesCollection::empty() const
|
||||||
{
|
{
|
||||||
return subscriptions.empty() &&
|
return
|
||||||
timers.empty() &&
|
subscriptions.empty() &&
|
||||||
guard_conditions.empty() &&
|
timers.empty() &&
|
||||||
clients.empty() &&
|
guard_conditions.empty() &&
|
||||||
services.empty() &&
|
clients.empty() &&
|
||||||
waitables.empty();
|
services.empty() &&
|
||||||
|
waitables.empty();
|
||||||
}
|
}
|
||||||
|
|
||||||
void ExecutorEntitiesCollection::clear()
|
void ExecutorEntitiesCollection::clear()
|
||||||
@@ -38,7 +39,6 @@ void ExecutorEntitiesCollection::clear()
|
|||||||
waitables.clear();
|
waitables.clear();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
void
|
void
|
||||||
build_entities_collection(
|
build_entities_collection(
|
||||||
const std::vector<rclcpp::CallbackGroup::WeakPtr> & callback_groups,
|
const std::vector<rclcpp::CallbackGroup::WeakPtr> & callback_groups,
|
||||||
@@ -94,110 +94,135 @@ build_entities_collection(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
template<typename EntityCollectionType>
|
size_t
|
||||||
void check_ready(
|
ready_executables(
|
||||||
EntityCollectionType & collection,
|
const ExecutorEntitiesCollection & collection,
|
||||||
std::deque<rclcpp::AnyExecutable> & executables,
|
rclcpp::WaitResult<rclcpp::WaitSet> & wait_result,
|
||||||
size_t size_of_waited_entities,
|
std::deque<rclcpp::AnyExecutable> & executables
|
||||||
typename EntityCollectionType::Key * waited_entities,
|
)
|
||||||
std::function<bool(rclcpp::AnyExecutable &,
|
|
||||||
typename EntityCollectionType::EntitySharedPtr &)> fill_executable)
|
|
||||||
{
|
{
|
||||||
for (size_t ii = 0; ii < size_of_waited_entities; ++ii) {
|
size_t added = 0;
|
||||||
if (!waited_entities[ii]) {continue;}
|
if (wait_result.kind() != rclcpp::WaitResultKind::Ready) {
|
||||||
auto entity_iter = collection.find(waited_entities[ii]);
|
return added;
|
||||||
if (entity_iter != collection.end()) {
|
}
|
||||||
|
auto rcl_wait_set = wait_result.get_wait_set().get_rcl_wait_set();
|
||||||
|
|
||||||
|
// Cache shared pointers to groups to avoid extra work re-locking them
|
||||||
|
std::map<rclcpp::CallbackGroup::WeakPtr,
|
||||||
|
rclcpp::CallbackGroup::SharedPtr,
|
||||||
|
std::owner_less<rclcpp::CallbackGroup::WeakPtr>> group_map;
|
||||||
|
|
||||||
|
auto group_cache = [&group_map](const rclcpp::CallbackGroup::WeakPtr & weak_cbg_ptr)
|
||||||
|
{
|
||||||
|
if (group_map.count(weak_cbg_ptr) == 0) {
|
||||||
|
group_map.insert({weak_cbg_ptr, weak_cbg_ptr.lock()});
|
||||||
|
}
|
||||||
|
return group_map.find(weak_cbg_ptr)->second;
|
||||||
|
};
|
||||||
|
|
||||||
|
for (size_t ii = 0; ii < rcl_wait_set.size_of_timers; ++ii) {
|
||||||
|
if (nullptr == rcl_wait_set.timers[ii]) {continue;}
|
||||||
|
auto entity_iter = collection.timers.find(rcl_wait_set.timers[ii]);
|
||||||
|
if (entity_iter != collection.timers.end()) {
|
||||||
auto entity = entity_iter->second.entity.lock();
|
auto entity = entity_iter->second.entity.lock();
|
||||||
if (!entity) {
|
if (!entity) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
auto group_info = group_cache(entity_iter->second.callback_group);
|
||||||
auto callback_group = entity_iter->second.callback_group.lock();
|
if (group_info && !group_info->can_be_taken_from().load()) {
|
||||||
if (callback_group && !callback_group->can_be_taken_from().load()) {
|
continue;
|
||||||
|
}
|
||||||
|
if (!entity->call()) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
rclcpp::AnyExecutable exec;
|
rclcpp::AnyExecutable exec;
|
||||||
|
exec.timer = entity;
|
||||||
exec.callback_group = callback_group;
|
exec.callback_group = group_info;
|
||||||
if (fill_executable(exec, entity)) {
|
executables.push_back(exec);
|
||||||
executables.push_back(exec);
|
added++;
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
std::deque<rclcpp::AnyExecutable>
|
for (size_t ii = 0; ii < rcl_wait_set.size_of_subscriptions; ++ii) {
|
||||||
ready_executables(
|
if (nullptr == rcl_wait_set.subscriptions[ii]) {continue;}
|
||||||
const ExecutorEntitiesCollection & collection,
|
auto entity_iter = collection.subscriptions.find(rcl_wait_set.subscriptions[ii]);
|
||||||
rclcpp::WaitResult<rclcpp::WaitSet> & wait_result
|
if (entity_iter != collection.subscriptions.end()) {
|
||||||
)
|
auto entity = entity_iter->second.entity.lock();
|
||||||
{
|
if (!entity) {
|
||||||
std::deque<rclcpp::AnyExecutable> ret;
|
continue;
|
||||||
|
|
||||||
if (wait_result.kind() != rclcpp::WaitResultKind::Ready) {
|
|
||||||
return ret;
|
|
||||||
}
|
|
||||||
auto rcl_wait_set = wait_result.get_wait_set().get_rcl_wait_set();
|
|
||||||
check_ready(
|
|
||||||
collection.timers,
|
|
||||||
ret,
|
|
||||||
rcl_wait_set.size_of_timers,
|
|
||||||
rcl_wait_set.timers,
|
|
||||||
[](rclcpp::AnyExecutable & exec, auto timer) {
|
|
||||||
if (!timer->call()) {
|
|
||||||
return false;
|
|
||||||
}
|
}
|
||||||
exec.timer = timer;
|
auto group_info = group_cache(entity_iter->second.callback_group);
|
||||||
return true;
|
if (group_info && !group_info->can_be_taken_from().load()) {
|
||||||
});
|
continue;
|
||||||
|
}
|
||||||
|
rclcpp::AnyExecutable exec;
|
||||||
|
exec.subscription = entity;
|
||||||
|
exec.callback_group = group_info;
|
||||||
|
executables.push_back(exec);
|
||||||
|
added++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
check_ready(
|
for (size_t ii = 0; ii < rcl_wait_set.size_of_services; ++ii) {
|
||||||
collection.subscriptions,
|
if (nullptr == rcl_wait_set.services[ii]) {continue;}
|
||||||
ret,
|
auto entity_iter = collection.services.find(rcl_wait_set.services[ii]);
|
||||||
rcl_wait_set.size_of_subscriptions,
|
if (entity_iter != collection.services.end()) {
|
||||||
rcl_wait_set.subscriptions,
|
auto entity = entity_iter->second.entity.lock();
|
||||||
[](rclcpp::AnyExecutable & exec, auto subscription) {
|
if (!entity) {
|
||||||
exec.subscription = subscription;
|
continue;
|
||||||
return true;
|
}
|
||||||
});
|
auto group_info = group_cache(entity_iter->second.callback_group);
|
||||||
|
if (group_info && !group_info->can_be_taken_from().load()) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
rclcpp::AnyExecutable exec;
|
||||||
|
exec.service = entity;
|
||||||
|
exec.callback_group = group_info;
|
||||||
|
executables.push_back(exec);
|
||||||
|
added++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
for (size_t ii = 0; ii < rcl_wait_set.size_of_clients; ++ii) {
|
||||||
check_ready(
|
if (nullptr == rcl_wait_set.clients[ii]) {continue;}
|
||||||
collection.services,
|
auto entity_iter = collection.clients.find(rcl_wait_set.clients[ii]);
|
||||||
ret,
|
if (entity_iter != collection.clients.end()) {
|
||||||
rcl_wait_set.size_of_services,
|
auto entity = entity_iter->second.entity.lock();
|
||||||
rcl_wait_set.services,
|
if (!entity) {
|
||||||
[](rclcpp::AnyExecutable & exec, auto service) {
|
continue;
|
||||||
exec.service = service;
|
}
|
||||||
return true;
|
auto group_info = group_cache(entity_iter->second.callback_group);
|
||||||
});
|
if (group_info && !group_info->can_be_taken_from().load()) {
|
||||||
|
continue;
|
||||||
check_ready(
|
}
|
||||||
collection.clients,
|
rclcpp::AnyExecutable exec;
|
||||||
ret,
|
exec.client = entity;
|
||||||
rcl_wait_set.size_of_clients,
|
exec.callback_group = group_info;
|
||||||
rcl_wait_set.clients,
|
executables.push_back(exec);
|
||||||
[](rclcpp::AnyExecutable & exec, auto client) {
|
added++;
|
||||||
exec.client = client;
|
}
|
||||||
return true;
|
}
|
||||||
});
|
|
||||||
|
|
||||||
for (auto & [handle, entry] : collection.waitables) {
|
for (auto & [handle, entry] : collection.waitables) {
|
||||||
auto waitable = entry.entity.lock();
|
auto waitable = entry.entity.lock();
|
||||||
if (waitable && waitable->is_ready(&rcl_wait_set)) {
|
if (!waitable) {
|
||||||
auto group = entry.callback_group.lock();
|
continue;
|
||||||
if (group && !group->can_be_taken_from().load()) {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
rclcpp::AnyExecutable exec;
|
|
||||||
exec.waitable = waitable;
|
|
||||||
exec.callback_group = group;
|
|
||||||
ret.push_back(exec);
|
|
||||||
}
|
}
|
||||||
|
if (!waitable->is_ready(&rcl_wait_set)) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
auto group_info = group_cache(entry.callback_group);
|
||||||
|
if (group_info && !group_info->can_be_taken_from().load()) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
rclcpp::AnyExecutable exec;
|
||||||
|
exec.waitable = waitable;
|
||||||
|
exec.callback_group = group_info;
|
||||||
|
exec.data = waitable->take_data();
|
||||||
|
executables.push_back(exec);
|
||||||
|
added++;
|
||||||
}
|
}
|
||||||
return ret;
|
return added;
|
||||||
}
|
}
|
||||||
|
|
||||||
} // namespace executors
|
} // namespace executors
|
||||||
} // namespace rclcpp
|
} // namespace rclcpp
|
||||||
|
|||||||
@@ -105,7 +105,8 @@ void
|
|||||||
ExecutorEntitiesCollector::remove_node(
|
ExecutorEntitiesCollector::remove_node(
|
||||||
rclcpp::node_interfaces::NodeBaseInterface::SharedPtr node_ptr)
|
rclcpp::node_interfaces::NodeBaseInterface::SharedPtr node_ptr)
|
||||||
{
|
{
|
||||||
if (!node_ptr->get_associated_with_executor_atomic().load()) {
|
std::atomic_bool & has_executor = node_ptr->get_associated_with_executor_atomic();
|
||||||
|
if (!has_executor.exchange(false)) {
|
||||||
throw std::runtime_error(
|
throw std::runtime_error(
|
||||||
std::string("Node '") + node_ptr->get_fully_qualified_name() +
|
std::string("Node '") + node_ptr->get_fully_qualified_name() +
|
||||||
"' needs to be associated with an executor.");
|
"' needs to be associated with an executor.");
|
||||||
@@ -161,7 +162,6 @@ ExecutorEntitiesCollector::remove_callback_group(rclcpp::CallbackGroup::SharedPt
|
|||||||
throw std::runtime_error("Node must not be deleted before its callback group(s).");
|
throw std::runtime_error("Node must not be deleted before its callback group(s).");
|
||||||
}
|
}
|
||||||
*/
|
*/
|
||||||
|
|
||||||
auto weak_group_ptr = rclcpp::CallbackGroup::WeakPtr(group_ptr);
|
auto weak_group_ptr = rclcpp::CallbackGroup::WeakPtr(group_ptr);
|
||||||
std::lock_guard<std::mutex> lock(mutex_);
|
std::lock_guard<std::mutex> lock(mutex_);
|
||||||
bool associated = manually_added_groups_.count(group_ptr) != 0;
|
bool associated = manually_added_groups_.count(group_ptr) != 0;
|
||||||
@@ -314,7 +314,11 @@ ExecutorEntitiesCollector::process_queues()
|
|||||||
if (node_it != weak_nodes_.end()) {
|
if (node_it != weak_nodes_.end()) {
|
||||||
remove_weak_node(node_it);
|
remove_weak_node(node_it);
|
||||||
} else {
|
} else {
|
||||||
throw std::runtime_error("Node needs to be associated with this executor.");
|
// The node may have been destroyed and removed from the colletion before
|
||||||
|
// we processed the queues. Don't throw if the pointer is already expired.
|
||||||
|
if (!weak_node_ptr.expired()) {
|
||||||
|
throw std::runtime_error("Node needs to be associated with this executor.");
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
auto node_ptr = weak_node_ptr.lock();
|
auto node_ptr = weak_node_ptr.lock();
|
||||||
|
|||||||
@@ -46,20 +46,18 @@ void
|
|||||||
ExecutorNotifyWaitable::add_to_wait_set(rcl_wait_set_t * wait_set)
|
ExecutorNotifyWaitable::add_to_wait_set(rcl_wait_set_t * wait_set)
|
||||||
{
|
{
|
||||||
std::lock_guard<std::mutex> lock(guard_condition_mutex_);
|
std::lock_guard<std::mutex> lock(guard_condition_mutex_);
|
||||||
|
|
||||||
for (auto weak_guard_condition : this->notify_guard_conditions_) {
|
for (auto weak_guard_condition : this->notify_guard_conditions_) {
|
||||||
auto guard_condition = weak_guard_condition.lock();
|
auto guard_condition = weak_guard_condition.lock();
|
||||||
if (guard_condition) {
|
if (!guard_condition) {continue;}
|
||||||
auto rcl_guard_condition = &guard_condition->get_rcl_guard_condition();
|
|
||||||
|
|
||||||
rcl_ret_t ret = rcl_wait_set_add_guard_condition(
|
rcl_guard_condition_t * cond = &guard_condition->get_rcl_guard_condition();
|
||||||
wait_set,
|
|
||||||
rcl_guard_condition, NULL);
|
|
||||||
|
|
||||||
if (RCL_RET_OK != ret) {
|
rcl_ret_t ret = rcl_wait_set_add_guard_condition(
|
||||||
rclcpp::exceptions::throw_from_rcl_error(
|
wait_set, cond, NULL);
|
||||||
ret, "failed to add guard condition to wait set");
|
|
||||||
}
|
if (RCL_RET_OK != ret) {
|
||||||
|
rclcpp::exceptions::throw_from_rcl_error(
|
||||||
|
ret, "failed to add guard condition to wait set");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -99,6 +99,19 @@ MultiThreadedExecutor::run(size_t this_thread_number)
|
|||||||
|
|
||||||
execute_any_executable(any_exec);
|
execute_any_executable(any_exec);
|
||||||
|
|
||||||
|
if (any_exec.callback_group &&
|
||||||
|
any_exec.callback_group->type() == CallbackGroupType::MutuallyExclusive &&
|
||||||
|
any_exec.callback_group->size() > 1)
|
||||||
|
{
|
||||||
|
try {
|
||||||
|
interrupt_guard_condition_->trigger();
|
||||||
|
} catch (const rclcpp::exceptions::RCLError & ex) {
|
||||||
|
throw std::runtime_error(
|
||||||
|
std::string(
|
||||||
|
"Failed to trigger guard condition on callback group change: ") + ex.what());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// Clear the callback_group to prevent the AnyExecutable destructor from
|
// Clear the callback_group to prevent the AnyExecutable destructor from
|
||||||
// resetting the callback group `can_be_taken_from`
|
// resetting the callback group `can_be_taken_from`
|
||||||
any_exec.callback_group.reset();
|
any_exec.callback_group.reset();
|
||||||
|
|||||||
@@ -1,524 +0,0 @@
|
|||||||
// Copyright 2020 Open Source Robotics Foundation, Inc.
|
|
||||||
//
|
|
||||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
|
||||||
// you may not use this file except in compliance with the License.
|
|
||||||
// You may obtain a copy of the License at
|
|
||||||
//
|
|
||||||
// http://www.apache.org/licenses/LICENSE-2.0
|
|
||||||
//
|
|
||||||
// Unless required by applicable law or agreed to in writing, software
|
|
||||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
|
||||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
||||||
// See the License for the specific language governing permissions and
|
|
||||||
// limitations under the License.
|
|
||||||
|
|
||||||
#include "rclcpp/executors/static_executor_entities_collector.hpp"
|
|
||||||
|
|
||||||
#include <algorithm>
|
|
||||||
#include <memory>
|
|
||||||
#include <stdexcept>
|
|
||||||
#include <string>
|
|
||||||
#include <utility>
|
|
||||||
#include <vector>
|
|
||||||
|
|
||||||
#include "rclcpp/memory_strategy.hpp"
|
|
||||||
#include "rclcpp/executors/static_single_threaded_executor.hpp"
|
|
||||||
#include "rclcpp/detail/add_guard_condition_to_rcl_wait_set.hpp"
|
|
||||||
|
|
||||||
using rclcpp::executors::StaticExecutorEntitiesCollector;
|
|
||||||
|
|
||||||
StaticExecutorEntitiesCollector::~StaticExecutorEntitiesCollector()
|
|
||||||
{
|
|
||||||
// Disassociate all callback groups and thus nodes.
|
|
||||||
for (const auto & pair : weak_groups_associated_with_executor_to_nodes_) {
|
|
||||||
auto group = pair.first.lock();
|
|
||||||
if (group) {
|
|
||||||
std::atomic_bool & has_executor = group->get_associated_with_executor_atomic();
|
|
||||||
has_executor.store(false);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
for (const auto & pair : weak_groups_to_nodes_associated_with_executor_) {
|
|
||||||
auto group = pair.first.lock();
|
|
||||||
if (group) {
|
|
||||||
std::atomic_bool & has_executor = group->get_associated_with_executor_atomic();
|
|
||||||
has_executor.store(false);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
// Disassociate all nodes
|
|
||||||
for (const auto & weak_node : weak_nodes_) {
|
|
||||||
auto node = weak_node.lock();
|
|
||||||
if (node) {
|
|
||||||
std::atomic_bool & has_executor = node->get_associated_with_executor_atomic();
|
|
||||||
has_executor.store(false);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
weak_groups_associated_with_executor_to_nodes_.clear();
|
|
||||||
weak_groups_to_nodes_associated_with_executor_.clear();
|
|
||||||
exec_list_.clear();
|
|
||||||
weak_nodes_.clear();
|
|
||||||
weak_nodes_to_guard_conditions_.clear();
|
|
||||||
}
|
|
||||||
|
|
||||||
void
|
|
||||||
StaticExecutorEntitiesCollector::init(
|
|
||||||
rcl_wait_set_t * p_wait_set,
|
|
||||||
rclcpp::memory_strategy::MemoryStrategy::SharedPtr memory_strategy)
|
|
||||||
{
|
|
||||||
// Empty initialize executable list
|
|
||||||
exec_list_ = rclcpp::experimental::ExecutableList();
|
|
||||||
// Get executor's wait_set_ pointer
|
|
||||||
p_wait_set_ = p_wait_set;
|
|
||||||
// Get executor's memory strategy ptr
|
|
||||||
if (memory_strategy == nullptr) {
|
|
||||||
throw std::runtime_error("Received NULL memory strategy in executor waitable.");
|
|
||||||
}
|
|
||||||
memory_strategy_ = memory_strategy;
|
|
||||||
|
|
||||||
// Get memory strategy and executable list. Prepare wait_set_
|
|
||||||
std::shared_ptr<void> shared_ptr;
|
|
||||||
execute(shared_ptr);
|
|
||||||
|
|
||||||
// The entities collector is now initialized
|
|
||||||
initialized_ = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
void
|
|
||||||
StaticExecutorEntitiesCollector::fini()
|
|
||||||
{
|
|
||||||
memory_strategy_->clear_handles();
|
|
||||||
exec_list_.clear();
|
|
||||||
}
|
|
||||||
|
|
||||||
std::shared_ptr<void>
|
|
||||||
StaticExecutorEntitiesCollector::take_data()
|
|
||||||
{
|
|
||||||
return nullptr;
|
|
||||||
}
|
|
||||||
|
|
||||||
void
|
|
||||||
StaticExecutorEntitiesCollector::execute(std::shared_ptr<void> & data)
|
|
||||||
{
|
|
||||||
(void) data;
|
|
||||||
// Fill memory strategy with entities coming from weak_nodes_
|
|
||||||
fill_memory_strategy();
|
|
||||||
// Fill exec_list_ with entities coming from weak_nodes_ (same as memory strategy)
|
|
||||||
fill_executable_list();
|
|
||||||
// Resize the wait_set_ based on memory_strategy handles (rcl_wait_set_resize)
|
|
||||||
prepare_wait_set();
|
|
||||||
// Add new nodes guard conditions to map
|
|
||||||
std::lock_guard<std::mutex> guard{new_nodes_mutex_};
|
|
||||||
for (const auto & weak_node : new_nodes_) {
|
|
||||||
if (auto node_ptr = weak_node.lock()) {
|
|
||||||
weak_nodes_to_guard_conditions_[node_ptr] =
|
|
||||||
node_ptr->get_shared_notify_guard_condition().get();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
new_nodes_.clear();
|
|
||||||
}
|
|
||||||
|
|
||||||
void
|
|
||||||
StaticExecutorEntitiesCollector::fill_memory_strategy()
|
|
||||||
{
|
|
||||||
memory_strategy_->clear_handles();
|
|
||||||
bool has_invalid_weak_groups_or_nodes =
|
|
||||||
memory_strategy_->collect_entities(weak_groups_to_nodes_associated_with_executor_);
|
|
||||||
// Clean up any invalid nodes, if they were detected
|
|
||||||
if (has_invalid_weak_groups_or_nodes) {
|
|
||||||
std::vector<rclcpp::CallbackGroup::WeakPtr> invalid_group_ptrs;
|
|
||||||
for (const auto & pair : weak_groups_to_nodes_associated_with_executor_) {
|
|
||||||
auto & weak_group_ptr = pair.first;
|
|
||||||
auto & weak_node_ptr = pair.second;
|
|
||||||
if (weak_group_ptr.expired() || weak_node_ptr.expired()) {
|
|
||||||
invalid_group_ptrs.push_back(weak_group_ptr);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
std::for_each(
|
|
||||||
invalid_group_ptrs.begin(), invalid_group_ptrs.end(),
|
|
||||||
[this](rclcpp::CallbackGroup::WeakPtr group_ptr) {
|
|
||||||
weak_groups_to_nodes_associated_with_executor_.erase(group_ptr);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
has_invalid_weak_groups_or_nodes =
|
|
||||||
memory_strategy_->collect_entities(weak_groups_associated_with_executor_to_nodes_);
|
|
||||||
// Clean up any invalid nodes, if they were detected
|
|
||||||
if (has_invalid_weak_groups_or_nodes) {
|
|
||||||
std::vector<rclcpp::CallbackGroup::WeakPtr> invalid_group_ptrs;
|
|
||||||
for (const auto & pair : weak_groups_associated_with_executor_to_nodes_) {
|
|
||||||
auto & weak_group_ptr = pair.first;
|
|
||||||
const auto & weak_node_ptr = pair.second;
|
|
||||||
if (weak_group_ptr.expired() || weak_node_ptr.expired()) {
|
|
||||||
invalid_group_ptrs.push_back(weak_group_ptr);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
std::for_each(
|
|
||||||
invalid_group_ptrs.begin(), invalid_group_ptrs.end(),
|
|
||||||
[this](rclcpp::CallbackGroup::WeakPtr group_ptr) {
|
|
||||||
weak_groups_associated_with_executor_to_nodes_.erase(group_ptr);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
// Add the static executor waitable to the memory strategy
|
|
||||||
memory_strategy_->add_waitable_handle(this->shared_from_this());
|
|
||||||
}
|
|
||||||
|
|
||||||
void
|
|
||||||
StaticExecutorEntitiesCollector::fill_executable_list()
|
|
||||||
{
|
|
||||||
exec_list_.clear();
|
|
||||||
add_callback_groups_from_nodes_associated_to_executor();
|
|
||||||
fill_executable_list_from_map(weak_groups_associated_with_executor_to_nodes_);
|
|
||||||
fill_executable_list_from_map(weak_groups_to_nodes_associated_with_executor_);
|
|
||||||
// Add the executor's waitable to the executable list
|
|
||||||
exec_list_.add_waitable(shared_from_this());
|
|
||||||
}
|
|
||||||
void
|
|
||||||
StaticExecutorEntitiesCollector::fill_executable_list_from_map(
|
|
||||||
const rclcpp::memory_strategy::MemoryStrategy::WeakCallbackGroupsToNodesMap &
|
|
||||||
weak_groups_to_nodes)
|
|
||||||
{
|
|
||||||
for (const auto & pair : weak_groups_to_nodes) {
|
|
||||||
auto group = pair.first.lock();
|
|
||||||
auto node = pair.second.lock();
|
|
||||||
if (!node || !group || !group->can_be_taken_from().load()) {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
group->find_timer_ptrs_if(
|
|
||||||
[this](const rclcpp::TimerBase::SharedPtr & timer) {
|
|
||||||
if (timer) {
|
|
||||||
exec_list_.add_timer(timer);
|
|
||||||
}
|
|
||||||
return false;
|
|
||||||
});
|
|
||||||
group->find_subscription_ptrs_if(
|
|
||||||
[this](const rclcpp::SubscriptionBase::SharedPtr & subscription) {
|
|
||||||
if (subscription) {
|
|
||||||
exec_list_.add_subscription(subscription);
|
|
||||||
}
|
|
||||||
return false;
|
|
||||||
});
|
|
||||||
group->find_service_ptrs_if(
|
|
||||||
[this](const rclcpp::ServiceBase::SharedPtr & service) {
|
|
||||||
if (service) {
|
|
||||||
exec_list_.add_service(service);
|
|
||||||
}
|
|
||||||
return false;
|
|
||||||
});
|
|
||||||
group->find_client_ptrs_if(
|
|
||||||
[this](const rclcpp::ClientBase::SharedPtr & client) {
|
|
||||||
if (client) {
|
|
||||||
exec_list_.add_client(client);
|
|
||||||
}
|
|
||||||
return false;
|
|
||||||
});
|
|
||||||
group->find_waitable_ptrs_if(
|
|
||||||
[this](const rclcpp::Waitable::SharedPtr & waitable) {
|
|
||||||
if (waitable) {
|
|
||||||
exec_list_.add_waitable(waitable);
|
|
||||||
}
|
|
||||||
return false;
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void
|
|
||||||
StaticExecutorEntitiesCollector::prepare_wait_set()
|
|
||||||
{
|
|
||||||
// clear wait set
|
|
||||||
if (rcl_wait_set_clear(p_wait_set_) != RCL_RET_OK) {
|
|
||||||
throw std::runtime_error("Couldn't clear wait set");
|
|
||||||
}
|
|
||||||
|
|
||||||
// The size of waitables are accounted for in size of the other entities
|
|
||||||
rcl_ret_t ret = rcl_wait_set_resize(
|
|
||||||
p_wait_set_, memory_strategy_->number_of_ready_subscriptions(),
|
|
||||||
memory_strategy_->number_of_guard_conditions(), memory_strategy_->number_of_ready_timers(),
|
|
||||||
memory_strategy_->number_of_ready_clients(), memory_strategy_->number_of_ready_services(),
|
|
||||||
memory_strategy_->number_of_ready_events());
|
|
||||||
|
|
||||||
if (RCL_RET_OK != ret) {
|
|
||||||
throw std::runtime_error(
|
|
||||||
std::string("Couldn't resize the wait set: ") + rcl_get_error_string().str);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void
|
|
||||||
StaticExecutorEntitiesCollector::refresh_wait_set(std::chrono::nanoseconds timeout)
|
|
||||||
{
|
|
||||||
// clear wait set (memset to '0' all wait_set_ entities
|
|
||||||
// but keeps the wait_set_ number of entities)
|
|
||||||
if (rcl_wait_set_clear(p_wait_set_) != RCL_RET_OK) {
|
|
||||||
throw std::runtime_error("Couldn't clear wait set");
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!memory_strategy_->add_handles_to_wait_set(p_wait_set_)) {
|
|
||||||
throw std::runtime_error("Couldn't fill wait set");
|
|
||||||
}
|
|
||||||
|
|
||||||
rcl_ret_t status =
|
|
||||||
rcl_wait(p_wait_set_, std::chrono::duration_cast<std::chrono::nanoseconds>(timeout).count());
|
|
||||||
|
|
||||||
if (status == RCL_RET_WAIT_SET_EMPTY) {
|
|
||||||
RCUTILS_LOG_WARN_NAMED(
|
|
||||||
"rclcpp",
|
|
||||||
"empty wait set received in rcl_wait(). This should never happen.");
|
|
||||||
} else if (status != RCL_RET_OK && status != RCL_RET_TIMEOUT) {
|
|
||||||
using rclcpp::exceptions::throw_from_rcl_error;
|
|
||||||
throw_from_rcl_error(status, "rcl_wait() failed");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void
|
|
||||||
StaticExecutorEntitiesCollector::add_to_wait_set(rcl_wait_set_t * wait_set)
|
|
||||||
{
|
|
||||||
// Add waitable guard conditions (one for each registered node) into the wait set.
|
|
||||||
for (const auto & pair : weak_nodes_to_guard_conditions_) {
|
|
||||||
auto & gc = pair.second;
|
|
||||||
detail::add_guard_condition_to_rcl_wait_set(*wait_set, *gc);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
size_t StaticExecutorEntitiesCollector::get_number_of_ready_guard_conditions()
|
|
||||||
{
|
|
||||||
std::lock_guard<std::mutex> guard{new_nodes_mutex_};
|
|
||||||
return weak_nodes_to_guard_conditions_.size() + new_nodes_.size();
|
|
||||||
}
|
|
||||||
|
|
||||||
bool
|
|
||||||
StaticExecutorEntitiesCollector::add_node(
|
|
||||||
rclcpp::node_interfaces::NodeBaseInterface::SharedPtr node_ptr)
|
|
||||||
{
|
|
||||||
bool is_new_node = false;
|
|
||||||
// If the node already has an executor
|
|
||||||
std::atomic_bool & has_executor = node_ptr->get_associated_with_executor_atomic();
|
|
||||||
if (has_executor.exchange(true)) {
|
|
||||||
throw std::runtime_error("Node has already been added to an executor.");
|
|
||||||
}
|
|
||||||
node_ptr->for_each_callback_group(
|
|
||||||
[this, node_ptr, &is_new_node](rclcpp::CallbackGroup::SharedPtr group_ptr)
|
|
||||||
{
|
|
||||||
if (
|
|
||||||
!group_ptr->get_associated_with_executor_atomic().load() &&
|
|
||||||
group_ptr->automatically_add_to_executor_with_node())
|
|
||||||
{
|
|
||||||
is_new_node = (add_callback_group(
|
|
||||||
group_ptr,
|
|
||||||
node_ptr,
|
|
||||||
weak_groups_to_nodes_associated_with_executor_) ||
|
|
||||||
is_new_node);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
weak_nodes_.push_back(node_ptr);
|
|
||||||
return is_new_node;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool
|
|
||||||
StaticExecutorEntitiesCollector::add_callback_group(
|
|
||||||
rclcpp::CallbackGroup::SharedPtr group_ptr,
|
|
||||||
rclcpp::node_interfaces::NodeBaseInterface::SharedPtr node_ptr,
|
|
||||||
rclcpp::memory_strategy::MemoryStrategy::WeakCallbackGroupsToNodesMap & weak_groups_to_nodes)
|
|
||||||
{
|
|
||||||
// If the callback_group already has an executor
|
|
||||||
std::atomic_bool & has_executor = group_ptr->get_associated_with_executor_atomic();
|
|
||||||
if (has_executor.exchange(true)) {
|
|
||||||
throw std::runtime_error("Callback group has already been added to an executor.");
|
|
||||||
}
|
|
||||||
bool is_new_node = !has_node(node_ptr, weak_groups_associated_with_executor_to_nodes_) &&
|
|
||||||
!has_node(node_ptr, weak_groups_to_nodes_associated_with_executor_);
|
|
||||||
rclcpp::CallbackGroup::WeakPtr weak_group_ptr = group_ptr;
|
|
||||||
auto insert_info = weak_groups_to_nodes.insert(
|
|
||||||
std::make_pair(weak_group_ptr, node_ptr));
|
|
||||||
bool was_inserted = insert_info.second;
|
|
||||||
if (!was_inserted) {
|
|
||||||
throw std::runtime_error("Callback group was already added to executor.");
|
|
||||||
}
|
|
||||||
if (is_new_node) {
|
|
||||||
std::lock_guard<std::mutex> guard{new_nodes_mutex_};
|
|
||||||
new_nodes_.push_back(node_ptr);
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool
|
|
||||||
StaticExecutorEntitiesCollector::add_callback_group(
|
|
||||||
rclcpp::CallbackGroup::SharedPtr group_ptr,
|
|
||||||
rclcpp::node_interfaces::NodeBaseInterface::SharedPtr node_ptr)
|
|
||||||
{
|
|
||||||
return add_callback_group(group_ptr, node_ptr, weak_groups_associated_with_executor_to_nodes_);
|
|
||||||
}
|
|
||||||
|
|
||||||
bool
|
|
||||||
StaticExecutorEntitiesCollector::remove_callback_group(
|
|
||||||
rclcpp::CallbackGroup::SharedPtr group_ptr)
|
|
||||||
{
|
|
||||||
return this->remove_callback_group_from_map(
|
|
||||||
group_ptr,
|
|
||||||
weak_groups_associated_with_executor_to_nodes_);
|
|
||||||
}
|
|
||||||
|
|
||||||
bool
|
|
||||||
StaticExecutorEntitiesCollector::remove_callback_group_from_map(
|
|
||||||
rclcpp::CallbackGroup::SharedPtr group_ptr,
|
|
||||||
rclcpp::memory_strategy::MemoryStrategy::WeakCallbackGroupsToNodesMap & weak_groups_to_nodes)
|
|
||||||
{
|
|
||||||
rclcpp::node_interfaces::NodeBaseInterface::SharedPtr node_ptr;
|
|
||||||
rclcpp::CallbackGroup::WeakPtr weak_group_ptr = group_ptr;
|
|
||||||
auto iter = weak_groups_to_nodes.find(weak_group_ptr);
|
|
||||||
if (iter != weak_groups_to_nodes.end()) {
|
|
||||||
node_ptr = iter->second.lock();
|
|
||||||
if (node_ptr == nullptr) {
|
|
||||||
throw std::runtime_error("Node must not be deleted before its callback group(s).");
|
|
||||||
}
|
|
||||||
weak_groups_to_nodes.erase(iter);
|
|
||||||
} else {
|
|
||||||
throw std::runtime_error("Callback group needs to be associated with executor.");
|
|
||||||
}
|
|
||||||
// If the node was matched and removed, interrupt waiting.
|
|
||||||
if (!has_node(node_ptr, weak_groups_associated_with_executor_to_nodes_) &&
|
|
||||||
!has_node(node_ptr, weak_groups_to_nodes_associated_with_executor_))
|
|
||||||
{
|
|
||||||
rclcpp::node_interfaces::NodeBaseInterface::WeakPtr node_weak_ptr(node_ptr);
|
|
||||||
weak_nodes_to_guard_conditions_.erase(node_weak_ptr);
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool
|
|
||||||
StaticExecutorEntitiesCollector::remove_node(
|
|
||||||
rclcpp::node_interfaces::NodeBaseInterface::SharedPtr node_ptr)
|
|
||||||
{
|
|
||||||
if (!node_ptr->get_associated_with_executor_atomic().load()) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
bool node_found = false;
|
|
||||||
auto node_it = weak_nodes_.begin();
|
|
||||||
while (node_it != weak_nodes_.end()) {
|
|
||||||
bool matched = (node_it->lock() == node_ptr);
|
|
||||||
if (matched) {
|
|
||||||
weak_nodes_.erase(node_it);
|
|
||||||
node_found = true;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
++node_it;
|
|
||||||
}
|
|
||||||
if (!node_found) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
std::vector<rclcpp::CallbackGroup::SharedPtr> found_group_ptrs;
|
|
||||||
std::for_each(
|
|
||||||
weak_groups_to_nodes_associated_with_executor_.begin(),
|
|
||||||
weak_groups_to_nodes_associated_with_executor_.end(),
|
|
||||||
[&found_group_ptrs, node_ptr](std::pair<rclcpp::CallbackGroup::WeakPtr,
|
|
||||||
rclcpp::node_interfaces::NodeBaseInterface::WeakPtr> key_value_pair) {
|
|
||||||
auto & weak_node_ptr = key_value_pair.second;
|
|
||||||
auto shared_node_ptr = weak_node_ptr.lock();
|
|
||||||
auto group_ptr = key_value_pair.first.lock();
|
|
||||||
if (shared_node_ptr == node_ptr) {
|
|
||||||
found_group_ptrs.push_back(group_ptr);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
std::for_each(
|
|
||||||
found_group_ptrs.begin(), found_group_ptrs.end(), [this]
|
|
||||||
(rclcpp::CallbackGroup::SharedPtr group_ptr) {
|
|
||||||
this->remove_callback_group_from_map(
|
|
||||||
group_ptr,
|
|
||||||
weak_groups_to_nodes_associated_with_executor_);
|
|
||||||
});
|
|
||||||
std::atomic_bool & has_executor = node_ptr->get_associated_with_executor_atomic();
|
|
||||||
has_executor.store(false);
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool
|
|
||||||
StaticExecutorEntitiesCollector::is_ready(rcl_wait_set_t * p_wait_set)
|
|
||||||
{
|
|
||||||
// Check wait_set guard_conditions for added/removed entities to/from a node
|
|
||||||
for (size_t i = 0; i < p_wait_set->size_of_guard_conditions; ++i) {
|
|
||||||
if (p_wait_set->guard_conditions[i] != NULL) {
|
|
||||||
auto found_guard_condition = std::find_if(
|
|
||||||
weak_nodes_to_guard_conditions_.begin(), weak_nodes_to_guard_conditions_.end(),
|
|
||||||
[&](std::pair<rclcpp::node_interfaces::NodeBaseInterface::WeakPtr,
|
|
||||||
const GuardCondition *> pair) -> bool {
|
|
||||||
const rcl_guard_condition_t & rcl_gc = pair.second->get_rcl_guard_condition();
|
|
||||||
return &rcl_gc == p_wait_set->guard_conditions[i];
|
|
||||||
});
|
|
||||||
if (found_guard_condition != weak_nodes_to_guard_conditions_.end()) {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
// None of the guard conditions triggered belong to a registered node
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Returns true iff the weak_groups_to_nodes map has node_ptr as the value in any of its entry.
|
|
||||||
bool
|
|
||||||
StaticExecutorEntitiesCollector::has_node(
|
|
||||||
const rclcpp::node_interfaces::NodeBaseInterface::SharedPtr node_ptr,
|
|
||||||
const rclcpp::memory_strategy::MemoryStrategy::WeakCallbackGroupsToNodesMap &
|
|
||||||
weak_groups_to_nodes) const
|
|
||||||
{
|
|
||||||
return std::find_if(
|
|
||||||
weak_groups_to_nodes.begin(),
|
|
||||||
weak_groups_to_nodes.end(),
|
|
||||||
[&](const WeakCallbackGroupsToNodesMap::value_type & other) -> bool {
|
|
||||||
auto other_ptr = other.second.lock();
|
|
||||||
return other_ptr == node_ptr;
|
|
||||||
}) != weak_groups_to_nodes.end();
|
|
||||||
}
|
|
||||||
|
|
||||||
void
|
|
||||||
StaticExecutorEntitiesCollector::add_callback_groups_from_nodes_associated_to_executor()
|
|
||||||
{
|
|
||||||
for (const auto & weak_node : weak_nodes_) {
|
|
||||||
auto node = weak_node.lock();
|
|
||||||
if (node) {
|
|
||||||
node->for_each_callback_group(
|
|
||||||
[this, node](rclcpp::CallbackGroup::SharedPtr shared_group_ptr)
|
|
||||||
{
|
|
||||||
if (shared_group_ptr->automatically_add_to_executor_with_node() &&
|
|
||||||
!shared_group_ptr->get_associated_with_executor_atomic().load())
|
|
||||||
{
|
|
||||||
add_callback_group(
|
|
||||||
shared_group_ptr,
|
|
||||||
node,
|
|
||||||
weak_groups_to_nodes_associated_with_executor_);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
std::vector<rclcpp::CallbackGroup::WeakPtr>
|
|
||||||
StaticExecutorEntitiesCollector::get_all_callback_groups()
|
|
||||||
{
|
|
||||||
std::vector<rclcpp::CallbackGroup::WeakPtr> groups;
|
|
||||||
for (const auto & group_node_ptr : weak_groups_associated_with_executor_to_nodes_) {
|
|
||||||
groups.push_back(group_node_ptr.first);
|
|
||||||
}
|
|
||||||
for (const auto & group_node_ptr : weak_groups_to_nodes_associated_with_executor_) {
|
|
||||||
groups.push_back(group_node_ptr.first);
|
|
||||||
}
|
|
||||||
return groups;
|
|
||||||
}
|
|
||||||
|
|
||||||
std::vector<rclcpp::CallbackGroup::WeakPtr>
|
|
||||||
StaticExecutorEntitiesCollector::get_manually_added_callback_groups()
|
|
||||||
{
|
|
||||||
std::vector<rclcpp::CallbackGroup::WeakPtr> groups;
|
|
||||||
for (const auto & group_node_ptr : weak_groups_associated_with_executor_to_nodes_) {
|
|
||||||
groups.push_back(group_node_ptr.first);
|
|
||||||
}
|
|
||||||
return groups;
|
|
||||||
}
|
|
||||||
|
|
||||||
std::vector<rclcpp::CallbackGroup::WeakPtr>
|
|
||||||
StaticExecutorEntitiesCollector::get_automatically_added_callback_groups_from_nodes()
|
|
||||||
{
|
|
||||||
std::vector<rclcpp::CallbackGroup::WeakPtr> groups;
|
|
||||||
for (const auto & group_node_ptr : weak_groups_to_nodes_associated_with_executor_) {
|
|
||||||
groups.push_back(group_node_ptr.first);
|
|
||||||
}
|
|
||||||
return groups;
|
|
||||||
}
|
|
||||||
@@ -12,31 +12,21 @@
|
|||||||
// See the License for the specific language governing permissions and
|
// See the License for the specific language governing permissions and
|
||||||
// limitations under the License.
|
// limitations under the License.
|
||||||
|
|
||||||
#include "rclcpp/executors/static_single_threaded_executor.hpp"
|
#include "rclcpp/executors/executor_entities_collection.hpp"
|
||||||
|
#include "rclcpp/executors/executor_notify_waitable.hpp"
|
||||||
#include <chrono>
|
|
||||||
#include <memory>
|
|
||||||
#include <utility>
|
|
||||||
#include <vector>
|
|
||||||
|
|
||||||
#include "rcpputils/scope_exit.hpp"
|
#include "rcpputils/scope_exit.hpp"
|
||||||
|
|
||||||
using rclcpp::executors::StaticSingleThreadedExecutor;
|
#include "rclcpp/executors/static_single_threaded_executor.hpp"
|
||||||
using rclcpp::experimental::ExecutableList;
|
#include "rclcpp/any_executable.hpp"
|
||||||
|
|
||||||
StaticSingleThreadedExecutor::StaticSingleThreadedExecutor(
|
using rclcpp::executors::StaticSingleThreadedExecutor;
|
||||||
const rclcpp::ExecutorOptions & options)
|
|
||||||
|
StaticSingleThreadedExecutor::StaticSingleThreadedExecutor(const rclcpp::ExecutorOptions & options)
|
||||||
: rclcpp::Executor(options)
|
: rclcpp::Executor(options)
|
||||||
{
|
{
|
||||||
entities_collector_ = std::make_shared<StaticExecutorEntitiesCollector>();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
StaticSingleThreadedExecutor::~StaticSingleThreadedExecutor()
|
StaticSingleThreadedExecutor::~StaticSingleThreadedExecutor() {}
|
||||||
{
|
|
||||||
if (entities_collector_->is_init()) {
|
|
||||||
entities_collector_->fini();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void
|
void
|
||||||
StaticSingleThreadedExecutor::spin()
|
StaticSingleThreadedExecutor::spin()
|
||||||
@@ -46,14 +36,25 @@ StaticSingleThreadedExecutor::spin()
|
|||||||
}
|
}
|
||||||
RCPPUTILS_SCOPE_EXIT(this->spinning.store(false); );
|
RCPPUTILS_SCOPE_EXIT(this->spinning.store(false); );
|
||||||
|
|
||||||
// Set memory_strategy_ and exec_list_ based on weak_nodes_
|
// This is essentially the contents of the rclcpp::Executor::wait_for_work method,
|
||||||
// Prepare wait_set_ based on memory_strategy_
|
// except we need to keep the wait result to reproduce the StaticSingleThreadedExecutor
|
||||||
entities_collector_->init(&wait_set_, memory_strategy_);
|
// behavior.
|
||||||
|
|
||||||
while (rclcpp::ok(this->context_) && spinning.load()) {
|
while (rclcpp::ok(this->context_) && spinning.load()) {
|
||||||
// Refresh wait set and wait for work
|
std::deque<rclcpp::AnyExecutable> to_exec;
|
||||||
entities_collector_->refresh_wait_set();
|
|
||||||
execute_ready_executables();
|
std::lock_guard<std::mutex> guard(mutex_);
|
||||||
|
if (current_collection_.empty() || this->entities_need_rebuild_.load()) {
|
||||||
|
this->collect_entities();
|
||||||
|
}
|
||||||
|
|
||||||
|
auto wait_result = wait_set_.wait(std::chrono::nanoseconds(-1));
|
||||||
|
if (wait_result.kind() == WaitResultKind::Empty) {
|
||||||
|
RCUTILS_LOG_WARN_NAMED(
|
||||||
|
"rclcpp",
|
||||||
|
"empty wait set received in wait(). This should never happen.");
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
execute_ready_executables(current_collection_, wait_result, false);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -80,11 +81,6 @@ StaticSingleThreadedExecutor::spin_all(std::chrono::nanoseconds max_duration)
|
|||||||
void
|
void
|
||||||
StaticSingleThreadedExecutor::spin_some_impl(std::chrono::nanoseconds max_duration, bool exhaustive)
|
StaticSingleThreadedExecutor::spin_some_impl(std::chrono::nanoseconds max_duration, bool exhaustive)
|
||||||
{
|
{
|
||||||
// Make sure the entities collector has been initialized
|
|
||||||
if (!entities_collector_->is_init()) {
|
|
||||||
entities_collector_->init(&wait_set_, memory_strategy_);
|
|
||||||
}
|
|
||||||
|
|
||||||
auto start = std::chrono::steady_clock::now();
|
auto start = std::chrono::steady_clock::now();
|
||||||
auto max_duration_not_elapsed = [max_duration, start]() {
|
auto max_duration_not_elapsed = [max_duration, start]() {
|
||||||
if (std::chrono::nanoseconds(0) == max_duration) {
|
if (std::chrono::nanoseconds(0) == max_duration) {
|
||||||
@@ -105,9 +101,21 @@ StaticSingleThreadedExecutor::spin_some_impl(std::chrono::nanoseconds max_durati
|
|||||||
|
|
||||||
while (rclcpp::ok(context_) && spinning.load() && max_duration_not_elapsed()) {
|
while (rclcpp::ok(context_) && spinning.load() && max_duration_not_elapsed()) {
|
||||||
// Get executables that are ready now
|
// Get executables that are ready now
|
||||||
entities_collector_->refresh_wait_set(std::chrono::milliseconds::zero());
|
std::lock_guard<std::mutex> guard(mutex_);
|
||||||
|
if (current_collection_.empty() || this->entities_need_rebuild_.load()) {
|
||||||
|
this->collect_entities();
|
||||||
|
}
|
||||||
|
|
||||||
|
auto wait_result = wait_set_.wait(std::chrono::nanoseconds(0));
|
||||||
|
if (wait_result.kind() == WaitResultKind::Empty) {
|
||||||
|
RCUTILS_LOG_WARN_NAMED(
|
||||||
|
"rclcpp",
|
||||||
|
"empty wait set received in wait(). This should never happen.");
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
// Execute ready executables
|
// Execute ready executables
|
||||||
bool work_available = execute_ready_executables();
|
bool work_available = execute_ready_executables(current_collection_, wait_result, false);
|
||||||
if (!work_available || !exhaustive) {
|
if (!work_available || !exhaustive) {
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
@@ -117,164 +125,122 @@ StaticSingleThreadedExecutor::spin_some_impl(std::chrono::nanoseconds max_durati
|
|||||||
void
|
void
|
||||||
StaticSingleThreadedExecutor::spin_once_impl(std::chrono::nanoseconds timeout)
|
StaticSingleThreadedExecutor::spin_once_impl(std::chrono::nanoseconds timeout)
|
||||||
{
|
{
|
||||||
// Make sure the entities collector has been initialized
|
|
||||||
if (!entities_collector_->is_init()) {
|
|
||||||
entities_collector_->init(&wait_set_, memory_strategy_);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (rclcpp::ok(context_) && spinning.load()) {
|
if (rclcpp::ok(context_) && spinning.load()) {
|
||||||
// Wait until we have a ready entity or timeout expired
|
std::lock_guard<std::mutex> guard(mutex_);
|
||||||
entities_collector_->refresh_wait_set(timeout);
|
if (current_collection_.empty() || this->entities_need_rebuild_.load()) {
|
||||||
|
this->collect_entities();
|
||||||
|
}
|
||||||
|
|
||||||
|
auto wait_result = wait_set_.wait(std::chrono::nanoseconds(timeout));
|
||||||
|
if (wait_result.kind() == WaitResultKind::Empty) {
|
||||||
|
RCUTILS_LOG_WARN_NAMED(
|
||||||
|
"rclcpp",
|
||||||
|
"empty wait set received in wait(). This should never happen.");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
// Execute ready executables
|
// Execute ready executables
|
||||||
execute_ready_executables(true);
|
execute_ready_executables(current_collection_, wait_result, true);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void
|
// This preserves the "scheduling semantics" of the StaticSingleThreadedExecutor
|
||||||
StaticSingleThreadedExecutor::add_callback_group(
|
// from the original implementation.
|
||||||
rclcpp::CallbackGroup::SharedPtr group_ptr,
|
bool StaticSingleThreadedExecutor::execute_ready_executables(
|
||||||
rclcpp::node_interfaces::NodeBaseInterface::SharedPtr node_ptr,
|
const rclcpp::executors::ExecutorEntitiesCollection & collection,
|
||||||
bool notify)
|
rclcpp::WaitResult<rclcpp::WaitSet> & wait_result,
|
||||||
{
|
bool spin_once)
|
||||||
bool is_new_node = entities_collector_->add_callback_group(group_ptr, node_ptr);
|
|
||||||
if (is_new_node && notify) {
|
|
||||||
// Interrupt waiting to handle new node
|
|
||||||
interrupt_guard_condition_->trigger();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void
|
|
||||||
StaticSingleThreadedExecutor::add_node(
|
|
||||||
rclcpp::node_interfaces::NodeBaseInterface::SharedPtr node_ptr, bool notify)
|
|
||||||
{
|
|
||||||
bool is_new_node = entities_collector_->add_node(node_ptr);
|
|
||||||
if (is_new_node && notify) {
|
|
||||||
// Interrupt waiting to handle new node
|
|
||||||
interrupt_guard_condition_->trigger();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void
|
|
||||||
StaticSingleThreadedExecutor::add_node(std::shared_ptr<rclcpp::Node> node_ptr, bool notify)
|
|
||||||
{
|
|
||||||
this->add_node(node_ptr->get_node_base_interface(), notify);
|
|
||||||
}
|
|
||||||
|
|
||||||
void
|
|
||||||
StaticSingleThreadedExecutor::remove_callback_group(
|
|
||||||
rclcpp::CallbackGroup::SharedPtr group_ptr, bool notify)
|
|
||||||
{
|
|
||||||
bool node_removed = entities_collector_->remove_callback_group(group_ptr);
|
|
||||||
// If the node was matched and removed, interrupt waiting
|
|
||||||
if (node_removed && notify) {
|
|
||||||
interrupt_guard_condition_->trigger();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void
|
|
||||||
StaticSingleThreadedExecutor::remove_node(
|
|
||||||
rclcpp::node_interfaces::NodeBaseInterface::SharedPtr node_ptr, bool notify)
|
|
||||||
{
|
|
||||||
bool node_removed = entities_collector_->remove_node(node_ptr);
|
|
||||||
if (!node_removed) {
|
|
||||||
throw std::runtime_error("Node needs to be associated with this executor.");
|
|
||||||
}
|
|
||||||
// If the node was matched and removed, interrupt waiting
|
|
||||||
if (notify) {
|
|
||||||
interrupt_guard_condition_->trigger();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
std::vector<rclcpp::CallbackGroup::WeakPtr>
|
|
||||||
StaticSingleThreadedExecutor::get_all_callback_groups()
|
|
||||||
{
|
|
||||||
return entities_collector_->get_all_callback_groups();
|
|
||||||
}
|
|
||||||
|
|
||||||
std::vector<rclcpp::CallbackGroup::WeakPtr>
|
|
||||||
StaticSingleThreadedExecutor::get_manually_added_callback_groups()
|
|
||||||
{
|
|
||||||
return entities_collector_->get_manually_added_callback_groups();
|
|
||||||
}
|
|
||||||
|
|
||||||
std::vector<rclcpp::CallbackGroup::WeakPtr>
|
|
||||||
StaticSingleThreadedExecutor::get_automatically_added_callback_groups_from_nodes()
|
|
||||||
{
|
|
||||||
return entities_collector_->get_automatically_added_callback_groups_from_nodes();
|
|
||||||
}
|
|
||||||
|
|
||||||
void
|
|
||||||
StaticSingleThreadedExecutor::remove_node(std::shared_ptr<rclcpp::Node> node_ptr, bool notify)
|
|
||||||
{
|
|
||||||
this->remove_node(node_ptr->get_node_base_interface(), notify);
|
|
||||||
}
|
|
||||||
|
|
||||||
bool
|
|
||||||
StaticSingleThreadedExecutor::execute_ready_executables(bool spin_once)
|
|
||||||
{
|
{
|
||||||
bool any_ready_executable = false;
|
bool any_ready_executable = false;
|
||||||
|
|
||||||
// Execute all the ready subscriptions
|
if (wait_result.kind() != rclcpp::WaitResultKind::Ready) {
|
||||||
for (size_t i = 0; i < wait_set_.size_of_subscriptions; ++i) {
|
return any_ready_executable;
|
||||||
if (i < entities_collector_->get_number_of_subscriptions()) {
|
|
||||||
if (wait_set_.subscriptions[i]) {
|
|
||||||
execute_subscription(entities_collector_->get_subscription(i));
|
|
||||||
if (spin_once) {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
any_ready_executable = true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
// Execute all the ready timers
|
|
||||||
for (size_t i = 0; i < wait_set_.size_of_timers; ++i) {
|
auto rcl_wait_set = wait_result.get_wait_set().get_rcl_wait_set();
|
||||||
if (i < entities_collector_->get_number_of_timers()) {
|
|
||||||
if (wait_set_.timers[i] && entities_collector_->get_timer(i)->is_ready()) {
|
for (size_t ii = 0; ii < rcl_wait_set.size_of_timers; ++ii) {
|
||||||
auto timer = entities_collector_->get_timer(i);
|
if (nullptr == rcl_wait_set.timers[ii]) {continue;}
|
||||||
timer->call();
|
auto entity_iter = collection.timers.find(rcl_wait_set.timers[ii]);
|
||||||
execute_timer(std::move(timer));
|
if (entity_iter != collection.timers.end()) {
|
||||||
if (spin_once) {
|
auto entity = entity_iter->second.entity.lock();
|
||||||
return true;
|
if (!entity) {
|
||||||
}
|
continue;
|
||||||
any_ready_executable = true;
|
|
||||||
}
|
}
|
||||||
}
|
if (!entity->call()) {
|
||||||
}
|
continue;
|
||||||
// Execute all the ready services
|
|
||||||
for (size_t i = 0; i < wait_set_.size_of_services; ++i) {
|
|
||||||
if (i < entities_collector_->get_number_of_services()) {
|
|
||||||
if (wait_set_.services[i]) {
|
|
||||||
execute_service(entities_collector_->get_service(i));
|
|
||||||
if (spin_once) {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
any_ready_executable = true;
|
|
||||||
}
|
}
|
||||||
}
|
execute_timer(entity);
|
||||||
}
|
|
||||||
// Execute all the ready clients
|
|
||||||
for (size_t i = 0; i < wait_set_.size_of_clients; ++i) {
|
|
||||||
if (i < entities_collector_->get_number_of_clients()) {
|
|
||||||
if (wait_set_.clients[i]) {
|
|
||||||
execute_client(entities_collector_->get_client(i));
|
|
||||||
if (spin_once) {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
any_ready_executable = true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
// Execute all the ready waitables
|
|
||||||
for (size_t i = 0; i < entities_collector_->get_number_of_waitables(); ++i) {
|
|
||||||
auto waitable = entities_collector_->get_waitable(i);
|
|
||||||
if (waitable->is_ready(&wait_set_)) {
|
|
||||||
auto data = waitable->take_data();
|
|
||||||
waitable->execute(data);
|
|
||||||
if (spin_once) {
|
if (spin_once) {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
any_ready_executable = true;
|
any_ready_executable = true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
for (size_t ii = 0; ii < rcl_wait_set.size_of_subscriptions; ++ii) {
|
||||||
|
if (nullptr == rcl_wait_set.subscriptions[ii]) {continue;}
|
||||||
|
auto entity_iter = collection.subscriptions.find(rcl_wait_set.subscriptions[ii]);
|
||||||
|
if (entity_iter != collection.subscriptions.end()) {
|
||||||
|
auto entity = entity_iter->second.entity.lock();
|
||||||
|
if (!entity) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
execute_subscription(entity);
|
||||||
|
if (spin_once) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
any_ready_executable = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
for (size_t ii = 0; ii < rcl_wait_set.size_of_services; ++ii) {
|
||||||
|
if (nullptr == rcl_wait_set.services[ii]) {continue;}
|
||||||
|
auto entity_iter = collection.services.find(rcl_wait_set.services[ii]);
|
||||||
|
if (entity_iter != collection.services.end()) {
|
||||||
|
auto entity = entity_iter->second.entity.lock();
|
||||||
|
if (!entity) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
execute_service(entity);
|
||||||
|
if (spin_once) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
any_ready_executable = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
for (size_t ii = 0; ii < rcl_wait_set.size_of_clients; ++ii) {
|
||||||
|
if (nullptr == rcl_wait_set.clients[ii]) {continue;}
|
||||||
|
auto entity_iter = collection.clients.find(rcl_wait_set.clients[ii]);
|
||||||
|
if (entity_iter != collection.clients.end()) {
|
||||||
|
auto entity = entity_iter->second.entity.lock();
|
||||||
|
if (!entity) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
execute_client(entity);
|
||||||
|
if (spin_once) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
any_ready_executable = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
for (auto & [handle, entry] : collection.waitables) {
|
||||||
|
auto waitable = entry.entity.lock();
|
||||||
|
if (!waitable) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
if (!waitable->is_ready(&rcl_wait_set)) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
auto data = waitable->take_data();
|
||||||
|
waitable->execute(data);
|
||||||
|
if (spin_once) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
any_ready_executable = true;
|
||||||
|
}
|
||||||
return any_ready_executable;
|
return any_ready_executable;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -362,42 +362,3 @@ BENCHMARK_F(PerformanceTestExecutorSimple, spin_until_future_complete)(benchmark
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
BENCHMARK_F(
|
|
||||||
PerformanceTestExecutorSimple,
|
|
||||||
static_executor_entities_collector_execute)(benchmark::State & st)
|
|
||||||
{
|
|
||||||
rclcpp::executors::StaticExecutorEntitiesCollector::SharedPtr entities_collector_ =
|
|
||||||
std::make_shared<rclcpp::executors::StaticExecutorEntitiesCollector>();
|
|
||||||
entities_collector_->add_node(node->get_node_base_interface());
|
|
||||||
|
|
||||||
rcl_wait_set_t wait_set = rcl_get_zero_initialized_wait_set();
|
|
||||||
rcl_allocator_t allocator = rcl_get_default_allocator();
|
|
||||||
auto shared_context = node->get_node_base_interface()->get_context();
|
|
||||||
rcl_context_t * context = shared_context->get_rcl_context().get();
|
|
||||||
rcl_ret_t ret = rcl_wait_set_init(&wait_set, 100, 100, 100, 100, 100, 100, context, allocator);
|
|
||||||
if (ret != RCL_RET_OK) {
|
|
||||||
st.SkipWithError(rcutils_get_error_string().str);
|
|
||||||
}
|
|
||||||
RCPPUTILS_SCOPE_EXIT(
|
|
||||||
{
|
|
||||||
rcl_ret_t ret = rcl_wait_set_fini(&wait_set);
|
|
||||||
if (ret != RCL_RET_OK) {
|
|
||||||
st.SkipWithError(rcutils_get_error_string().str);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
auto memory_strategy = rclcpp::memory_strategies::create_default_strategy();
|
|
||||||
rclcpp::GuardCondition guard_condition(shared_context);
|
|
||||||
|
|
||||||
entities_collector_->init(&wait_set, memory_strategy);
|
|
||||||
RCPPUTILS_SCOPE_EXIT(entities_collector_->fini());
|
|
||||||
|
|
||||||
reset_heap_counters();
|
|
||||||
|
|
||||||
for (auto _ : st) {
|
|
||||||
(void)_;
|
|
||||||
std::shared_ptr<void> data = entities_collector_->take_data();
|
|
||||||
entities_collector_->execute(data);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|||||||
@@ -671,14 +671,6 @@ if(TARGET test_executors)
|
|||||||
target_link_libraries(test_executors ${PROJECT_NAME})
|
target_link_libraries(test_executors ${PROJECT_NAME})
|
||||||
endif()
|
endif()
|
||||||
|
|
||||||
ament_add_gtest(test_static_single_threaded_executor executors/test_static_single_threaded_executor.cpp
|
|
||||||
APPEND_LIBRARY_DIRS "${append_library_dirs}")
|
|
||||||
if(TARGET test_static_single_threaded_executor)
|
|
||||||
ament_target_dependencies(test_static_single_threaded_executor
|
|
||||||
"test_msgs")
|
|
||||||
target_link_libraries(test_static_single_threaded_executor ${PROJECT_NAME} mimick)
|
|
||||||
endif()
|
|
||||||
|
|
||||||
ament_add_gtest(test_multi_threaded_executor executors/test_multi_threaded_executor.cpp
|
ament_add_gtest(test_multi_threaded_executor executors/test_multi_threaded_executor.cpp
|
||||||
APPEND_LIBRARY_DIRS "${append_library_dirs}")
|
APPEND_LIBRARY_DIRS "${append_library_dirs}")
|
||||||
if(TARGET test_multi_threaded_executor)
|
if(TARGET test_multi_threaded_executor)
|
||||||
@@ -687,15 +679,6 @@ if(TARGET test_multi_threaded_executor)
|
|||||||
target_link_libraries(test_multi_threaded_executor ${PROJECT_NAME})
|
target_link_libraries(test_multi_threaded_executor ${PROJECT_NAME})
|
||||||
endif()
|
endif()
|
||||||
|
|
||||||
ament_add_gtest(test_static_executor_entities_collector executors/test_static_executor_entities_collector.cpp
|
|
||||||
APPEND_LIBRARY_DIRS "${append_library_dirs}" TIMEOUT 120)
|
|
||||||
if(TARGET test_static_executor_entities_collector)
|
|
||||||
ament_target_dependencies(test_static_executor_entities_collector
|
|
||||||
"rcl"
|
|
||||||
"test_msgs")
|
|
||||||
target_link_libraries(test_static_executor_entities_collector ${PROJECT_NAME} mimick)
|
|
||||||
endif()
|
|
||||||
|
|
||||||
ament_add_gtest(test_entities_collector executors/test_entities_collector.cpp
|
ament_add_gtest(test_entities_collector executors/test_entities_collector.cpp
|
||||||
APPEND_LIBRARY_DIRS "${append_library_dirs}" TIMEOUT 120)
|
APPEND_LIBRARY_DIRS "${append_library_dirs}" TIMEOUT 120)
|
||||||
if(TARGET test_entities_collector)
|
if(TARGET test_entities_collector)
|
||||||
|
|||||||
@@ -83,8 +83,6 @@ public:
|
|||||||
int callback_count;
|
int callback_count;
|
||||||
};
|
};
|
||||||
|
|
||||||
// spin_all and spin_some are not implemented correctly in StaticSingleThreadedExecutor, see:
|
|
||||||
// https://github.com/ros2/rclcpp/issues/1219 for tracking
|
|
||||||
template<typename T>
|
template<typename T>
|
||||||
class TestExecutorsStable : public TestExecutors<T> {};
|
class TestExecutorsStable : public TestExecutors<T> {};
|
||||||
|
|
||||||
@@ -126,15 +124,6 @@ public:
|
|||||||
// is updated.
|
// is updated.
|
||||||
TYPED_TEST_SUITE(TestExecutors, ExecutorTypes, ExecutorTypeNames);
|
TYPED_TEST_SUITE(TestExecutors, ExecutorTypes, ExecutorTypeNames);
|
||||||
|
|
||||||
// StaticSingleThreadedExecutor is not included in these tests for now, due to:
|
|
||||||
// https://github.com/ros2/rclcpp/issues/1219
|
|
||||||
using StandardExecutors =
|
|
||||||
::testing::Types<
|
|
||||||
rclcpp::executors::SingleThreadedExecutor,
|
|
||||||
rclcpp::executors::MultiThreadedExecutor,
|
|
||||||
rclcpp::experimental::executors::EventsExecutor>;
|
|
||||||
TYPED_TEST_SUITE(TestExecutorsStable, StandardExecutors, ExecutorTypeNames);
|
|
||||||
|
|
||||||
// Make sure that executors detach from nodes when destructing
|
// Make sure that executors detach from nodes when destructing
|
||||||
TYPED_TEST(TestExecutors, detachOnDestruction)
|
TYPED_TEST(TestExecutors, detachOnDestruction)
|
||||||
{
|
{
|
||||||
@@ -158,10 +147,7 @@ TYPED_TEST(TestExecutors, detachOnDestruction)
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Make sure that the executor can automatically remove expired nodes correctly
|
// Make sure that the executor can automatically remove expired nodes correctly
|
||||||
// Currently fails for StaticSingleThreadedExecutor so it is being skipped, see:
|
TYPED_TEST(TestExecutors, addTemporaryNode) {
|
||||||
// https://github.com/ros2/rclcpp/issues/1231
|
|
||||||
TYPED_TEST(TestExecutorsStable, addTemporaryNode)
|
|
||||||
{
|
|
||||||
using ExecutorType = TypeParam;
|
using ExecutorType = TypeParam;
|
||||||
// rmw_connextdds doesn't support events-executor
|
// rmw_connextdds doesn't support events-executor
|
||||||
if (
|
if (
|
||||||
@@ -271,14 +257,17 @@ TYPED_TEST(TestExecutors, spinWhileAlreadySpinning)
|
|||||||
ExecutorType executor;
|
ExecutorType executor;
|
||||||
executor.add_node(this->node);
|
executor.add_node(this->node);
|
||||||
|
|
||||||
bool timer_completed = false;
|
std::atomic_bool timer_completed = false;
|
||||||
auto timer = this->node->create_wall_timer(1ms, [&]() {timer_completed = true;});
|
auto timer = this->node->create_wall_timer(
|
||||||
|
1ms, [&]() {
|
||||||
|
timer_completed.store(true);
|
||||||
|
});
|
||||||
|
|
||||||
std::thread spinner([&]() {executor.spin();});
|
std::thread spinner([&]() {executor.spin();});
|
||||||
// Sleep for a short time to verify executor.spin() is going, and didn't throw.
|
// Sleep for a short time to verify executor.spin() is going, and didn't throw.
|
||||||
|
|
||||||
auto start = std::chrono::steady_clock::now();
|
auto start = std::chrono::steady_clock::now();
|
||||||
while (!timer_completed && (std::chrono::steady_clock::now() - start) < 10s) {
|
while (!timer_completed.load() && (std::chrono::steady_clock::now() - start) < 10s) {
|
||||||
std::this_thread::sleep_for(1ms);
|
std::this_thread::sleep_for(1ms);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -46,23 +46,6 @@ public:
|
|||||||
{
|
{
|
||||||
spin_node_once_nanoseconds(node, std::chrono::milliseconds(100));
|
spin_node_once_nanoseconds(node, std::chrono::milliseconds(100));
|
||||||
}
|
}
|
||||||
|
|
||||||
rclcpp::memory_strategy::MemoryStrategy * memory_strategy_ptr()
|
|
||||||
{
|
|
||||||
return memory_strategy_.get();
|
|
||||||
}
|
|
||||||
|
|
||||||
rclcpp::node_interfaces::NodeBaseInterface::SharedPtr local_get_node_by_group(
|
|
||||||
rclcpp::CallbackGroup::SharedPtr group)
|
|
||||||
{
|
|
||||||
std::lock_guard<std::mutex> guard_{mutex_}; // only to make the TSA happy
|
|
||||||
return get_node_by_group(weak_groups_to_nodes_, group);
|
|
||||||
}
|
|
||||||
|
|
||||||
rclcpp::CallbackGroup::SharedPtr local_get_group_by_timer(rclcpp::TimerBase::SharedPtr timer)
|
|
||||||
{
|
|
||||||
return get_group_by_timer(timer);
|
|
||||||
}
|
|
||||||
};
|
};
|
||||||
|
|
||||||
class TestExecutor : public ::testing::Test
|
class TestExecutor : public ::testing::Test
|
||||||
@@ -130,7 +113,7 @@ TEST_F(TestExecutor, constructor_bad_wait_set_init) {
|
|||||||
auto mock = mocking_utils::patch_and_return("lib:rclcpp", rcl_wait_set_init, RCL_RET_ERROR);
|
auto mock = mocking_utils::patch_and_return("lib:rclcpp", rcl_wait_set_init, RCL_RET_ERROR);
|
||||||
RCLCPP_EXPECT_THROW_EQ(
|
RCLCPP_EXPECT_THROW_EQ(
|
||||||
static_cast<void>(std::make_unique<DummyExecutor>()),
|
static_cast<void>(std::make_unique<DummyExecutor>()),
|
||||||
std::runtime_error("Failed to create wait set in Executor constructor: error not set"));
|
std::runtime_error("Failed to create wait set: error not set"));
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST_F(TestExecutor, add_callback_group_twice) {
|
TEST_F(TestExecutor, add_callback_group_twice) {
|
||||||
@@ -142,7 +125,7 @@ TEST_F(TestExecutor, add_callback_group_twice) {
|
|||||||
cb_group->get_associated_with_executor_atomic().exchange(false);
|
cb_group->get_associated_with_executor_atomic().exchange(false);
|
||||||
RCLCPP_EXPECT_THROW_EQ(
|
RCLCPP_EXPECT_THROW_EQ(
|
||||||
dummy.add_callback_group(cb_group, node->get_node_base_interface(), false),
|
dummy.add_callback_group(cb_group, node->get_node_base_interface(), false),
|
||||||
std::runtime_error("Callback group was already added to executor."));
|
std::runtime_error("Callback group has already been added to this executor."));
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST_F(TestExecutor, add_callback_group_failed_trigger_guard_condition) {
|
TEST_F(TestExecutor, add_callback_group_failed_trigger_guard_condition) {
|
||||||
@@ -168,9 +151,15 @@ TEST_F(TestExecutor, remove_callback_group_null_node) {
|
|||||||
|
|
||||||
node.reset();
|
node.reset();
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* TODO(mjcarroll): Assert this when we are enforcing that nodes must be destroyed
|
||||||
|
* after their created callback groups.
|
||||||
RCLCPP_EXPECT_THROW_EQ(
|
RCLCPP_EXPECT_THROW_EQ(
|
||||||
dummy.remove_callback_group(cb_group, false),
|
dummy.remove_callback_group(cb_group, false),
|
||||||
std::runtime_error("Node must not be deleted before its callback group(s)."));
|
std::runtime_error("Node must not be deleted before its callback group(s)."));
|
||||||
|
*/
|
||||||
|
EXPECT_NO_THROW(dummy.remove_callback_group(cb_group, false));
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST_F(TestExecutor, remove_callback_group_failed_trigger_guard_condition) {
|
TEST_F(TestExecutor, remove_callback_group_failed_trigger_guard_condition) {
|
||||||
@@ -197,7 +186,7 @@ TEST_F(TestExecutor, remove_node_not_associated) {
|
|||||||
|
|
||||||
RCLCPP_EXPECT_THROW_EQ(
|
RCLCPP_EXPECT_THROW_EQ(
|
||||||
dummy.remove_node(node->get_node_base_interface(), false),
|
dummy.remove_node(node->get_node_base_interface(), false),
|
||||||
std::runtime_error("Node needs to be associated with an executor."));
|
std::runtime_error("Node '/ns/node' needs to be associated with an executor."));
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST_F(TestExecutor, remove_node_associated_with_different_executor) {
|
TEST_F(TestExecutor, remove_node_associated_with_different_executor) {
|
||||||
@@ -211,7 +200,7 @@ TEST_F(TestExecutor, remove_node_associated_with_different_executor) {
|
|||||||
|
|
||||||
RCLCPP_EXPECT_THROW_EQ(
|
RCLCPP_EXPECT_THROW_EQ(
|
||||||
dummy2.remove_node(node1->get_node_base_interface(), false),
|
dummy2.remove_node(node1->get_node_base_interface(), false),
|
||||||
std::runtime_error("Node needs to be associated with this executor."));
|
std::runtime_error("Node '/ns/node1' needs to be associated with this executor."));
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST_F(TestExecutor, spin_node_once_nanoseconds) {
|
TEST_F(TestExecutor, spin_node_once_nanoseconds) {
|
||||||
@@ -328,42 +317,14 @@ TEST_F(TestExecutor, cancel_failed_trigger_guard_condition) {
|
|||||||
std::runtime_error("Failed to trigger guard condition in cancel: error not set"));
|
std::runtime_error("Failed to trigger guard condition in cancel: error not set"));
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST_F(TestExecutor, set_memory_strategy_nullptr) {
|
TEST_F(TestExecutor, create_executor_fail_wait_set_clear) {
|
||||||
DummyExecutor dummy;
|
auto mock = mocking_utils::patch_and_return("lib:rclcpp", rcl_wait_set_clear, RCL_RET_ERROR);
|
||||||
|
|
||||||
RCLCPP_EXPECT_THROW_EQ(
|
RCLCPP_EXPECT_THROW_EQ(
|
||||||
dummy.set_memory_strategy(nullptr),
|
DummyExecutor dummy,
|
||||||
std::runtime_error("Received NULL memory strategy in executor."));
|
std::runtime_error("Couldn't clear the wait set: error not set"));
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST_F(TestExecutor, set_memory_strategy) {
|
TEST_F(TestExecutor, spin_all_fail_wait_set_clear) {
|
||||||
DummyExecutor dummy;
|
|
||||||
rclcpp::memory_strategy::MemoryStrategy::SharedPtr strategy =
|
|
||||||
std::make_shared<
|
|
||||||
rclcpp::memory_strategies::allocator_memory_strategy::AllocatorMemoryStrategy<>>();
|
|
||||||
|
|
||||||
dummy.set_memory_strategy(strategy);
|
|
||||||
EXPECT_EQ(dummy.memory_strategy_ptr(), strategy.get());
|
|
||||||
}
|
|
||||||
|
|
||||||
TEST_F(TestExecutor, spin_once_failed_trigger_guard_condition) {
|
|
||||||
DummyExecutor dummy;
|
|
||||||
auto node = std::make_shared<rclcpp::Node>("node", "ns");
|
|
||||||
auto timer =
|
|
||||||
node->create_wall_timer(std::chrono::milliseconds(1), [&]() {});
|
|
||||||
|
|
||||||
dummy.add_node(node);
|
|
||||||
// Wait for the wall timer to have expired.
|
|
||||||
std::this_thread::sleep_for(std::chrono::milliseconds(50));
|
|
||||||
auto mock = mocking_utils::patch_and_return(
|
|
||||||
"lib:rclcpp", rcl_trigger_guard_condition, RCL_RET_ERROR);
|
|
||||||
RCLCPP_EXPECT_THROW_EQ(
|
|
||||||
dummy.spin_once(std::chrono::milliseconds(1)),
|
|
||||||
std::runtime_error(
|
|
||||||
"Failed to trigger guard condition from execute_any_executable: error not set"));
|
|
||||||
}
|
|
||||||
|
|
||||||
TEST_F(TestExecutor, spin_some_fail_wait_set_clear) {
|
|
||||||
DummyExecutor dummy;
|
DummyExecutor dummy;
|
||||||
auto node = std::make_shared<rclcpp::Node>("node", "ns");
|
auto node = std::make_shared<rclcpp::Node>("node", "ns");
|
||||||
auto timer =
|
auto timer =
|
||||||
@@ -371,9 +332,10 @@ TEST_F(TestExecutor, spin_some_fail_wait_set_clear) {
|
|||||||
|
|
||||||
dummy.add_node(node);
|
dummy.add_node(node);
|
||||||
auto mock = mocking_utils::patch_and_return("lib:rclcpp", rcl_wait_set_clear, RCL_RET_ERROR);
|
auto mock = mocking_utils::patch_and_return("lib:rclcpp", rcl_wait_set_clear, RCL_RET_ERROR);
|
||||||
|
|
||||||
RCLCPP_EXPECT_THROW_EQ(
|
RCLCPP_EXPECT_THROW_EQ(
|
||||||
dummy.spin_some(std::chrono::milliseconds(1)),
|
dummy.spin_all(std::chrono::milliseconds(1)),
|
||||||
std::runtime_error("Couldn't clear wait set: error not set"));
|
std::runtime_error("Couldn't clear the wait set: error not set"));
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST_F(TestExecutor, spin_some_fail_wait_set_resize) {
|
TEST_F(TestExecutor, spin_some_fail_wait_set_resize) {
|
||||||
@@ -401,7 +363,7 @@ TEST_F(TestExecutor, spin_some_fail_add_handles_to_wait_set) {
|
|||||||
RCL_RET_ERROR);
|
RCL_RET_ERROR);
|
||||||
RCLCPP_EXPECT_THROW_EQ(
|
RCLCPP_EXPECT_THROW_EQ(
|
||||||
dummy.spin_some(std::chrono::milliseconds(1)),
|
dummy.spin_some(std::chrono::milliseconds(1)),
|
||||||
std::runtime_error("Couldn't fill wait set"));
|
std::runtime_error("Couldn't fill wait set: error not set"));
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST_F(TestExecutor, spin_some_fail_wait) {
|
TEST_F(TestExecutor, spin_some_fail_wait) {
|
||||||
@@ -417,71 +379,6 @@ TEST_F(TestExecutor, spin_some_fail_wait) {
|
|||||||
std::runtime_error("rcl_wait() failed: error not set"));
|
std::runtime_error("rcl_wait() failed: error not set"));
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST_F(TestExecutor, get_node_by_group_null_group) {
|
|
||||||
DummyExecutor dummy;
|
|
||||||
ASSERT_EQ(nullptr, dummy.local_get_node_by_group(nullptr));
|
|
||||||
}
|
|
||||||
|
|
||||||
TEST_F(TestExecutor, get_node_by_group) {
|
|
||||||
DummyExecutor dummy;
|
|
||||||
auto node = std::make_shared<rclcpp::Node>("node", "ns");
|
|
||||||
rclcpp::CallbackGroup::SharedPtr cb_group = node->create_callback_group(
|
|
||||||
rclcpp::CallbackGroupType::MutuallyExclusive);
|
|
||||||
dummy.add_callback_group(cb_group, node->get_node_base_interface(), false);
|
|
||||||
ASSERT_EQ(node->get_node_base_interface().get(), dummy.local_get_node_by_group(cb_group).get());
|
|
||||||
}
|
|
||||||
|
|
||||||
TEST_F(TestExecutor, get_node_by_group_not_found) {
|
|
||||||
DummyExecutor dummy;
|
|
||||||
auto node = std::make_shared<rclcpp::Node>("node", "ns");
|
|
||||||
rclcpp::CallbackGroup::SharedPtr cb_group = node->create_callback_group(
|
|
||||||
rclcpp::CallbackGroupType::MutuallyExclusive);
|
|
||||||
ASSERT_EQ(nullptr, dummy.local_get_node_by_group(cb_group).get());
|
|
||||||
}
|
|
||||||
|
|
||||||
TEST_F(TestExecutor, get_group_by_timer_nullptr) {
|
|
||||||
DummyExecutor dummy;
|
|
||||||
ASSERT_EQ(nullptr, dummy.local_get_group_by_timer(nullptr));
|
|
||||||
}
|
|
||||||
|
|
||||||
TEST_F(TestExecutor, get_group_by_timer) {
|
|
||||||
DummyExecutor dummy;
|
|
||||||
auto node = std::make_shared<rclcpp::Node>("node", "ns");
|
|
||||||
rclcpp::CallbackGroup::SharedPtr cb_group = node->create_callback_group(
|
|
||||||
rclcpp::CallbackGroupType::MutuallyExclusive);
|
|
||||||
auto timer =
|
|
||||||
node->create_wall_timer(std::chrono::milliseconds(1), [&]() {}, cb_group);
|
|
||||||
dummy.add_node(node);
|
|
||||||
|
|
||||||
ASSERT_EQ(cb_group.get(), dummy.local_get_group_by_timer(timer).get());
|
|
||||||
}
|
|
||||||
|
|
||||||
TEST_F(TestExecutor, get_group_by_timer_with_deleted_group) {
|
|
||||||
DummyExecutor dummy;
|
|
||||||
auto node = std::make_shared<rclcpp::Node>("node", "ns");
|
|
||||||
rclcpp::CallbackGroup::SharedPtr cb_group = node->create_callback_group(
|
|
||||||
rclcpp::CallbackGroupType::MutuallyExclusive);
|
|
||||||
auto timer =
|
|
||||||
node->create_wall_timer(std::chrono::milliseconds(1), [&]() {}, cb_group);
|
|
||||||
dummy.add_node(node);
|
|
||||||
|
|
||||||
cb_group.reset();
|
|
||||||
|
|
||||||
ASSERT_EQ(nullptr, dummy.local_get_group_by_timer(timer).get());
|
|
||||||
}
|
|
||||||
|
|
||||||
TEST_F(TestExecutor, get_group_by_timer_add_callback_group) {
|
|
||||||
DummyExecutor dummy;
|
|
||||||
auto node = std::make_shared<rclcpp::Node>("node", "ns");
|
|
||||||
rclcpp::CallbackGroup::SharedPtr cb_group = node->create_callback_group(
|
|
||||||
rclcpp::CallbackGroupType::MutuallyExclusive);
|
|
||||||
auto timer =
|
|
||||||
node->create_wall_timer(std::chrono::milliseconds(1), [&]() {}, cb_group);
|
|
||||||
dummy.add_callback_group(cb_group, node->get_node_base_interface(), false);
|
|
||||||
|
|
||||||
ASSERT_EQ(cb_group.get(), dummy.local_get_group_by_timer(timer).get());
|
|
||||||
}
|
|
||||||
|
|
||||||
TEST_F(TestExecutor, spin_until_future_complete_in_spin_until_future_complete) {
|
TEST_F(TestExecutor, spin_until_future_complete_in_spin_until_future_complete) {
|
||||||
DummyExecutor dummy;
|
DummyExecutor dummy;
|
||||||
auto node = std::make_shared<rclcpp::Node>("node", "ns");
|
auto node = std::make_shared<rclcpp::Node>("node", "ns");
|
||||||
|
|||||||
Reference in New Issue
Block a user