Compare commits

..

31 Commits

Author SHA1 Message Date
methylDragon
10e482ee5c Use rmw_serialization_support_init
Signed-off-by: methylDragon <methylDragon@gmail.com>
2023-04-10 11:28:33 -07:00
methylDragon
c8734b3e1d Refactor dynamic type support structs to use allocators and refs
Signed-off-by: methylDragon <methylDragon@gmail.com>
2023-04-10 09:19:44 -07:00
methylDragon
8447d36206 Refactor serialization support to use allocators and refs
Signed-off-by: methylDragon <methylDragon@gmail.com>
2023-04-09 22:33:08 -07:00
methylDragon
969067242b Refactor dynamic message type support init and to use refs
Signed-off-by: methylDragon <methylDragon@gmail.com>
2023-04-09 22:33:03 -07:00
methylDragon
32859cdf3c Remove RMW interfaces
Signed-off-by: methylDragon <methylDragon@gmail.com>
2023-04-07 18:16:43 -07:00
methylDragon
30210a2c02 Remove print methods
Signed-off-by: methylDragon <methylDragon@gmail.com>
2023-04-07 15:43:56 -07:00
methylDragon
d871af007a Use create instead of init
Signed-off-by: methylDragon <methylDragon@gmail.com>
2023-04-06 21:13:58 -07:00
methylDragon
29577dab24 Move dynamic type support struct to rosidl and fix mem issues
Signed-off-by: methylDragon <methylDragon@gmail.com>
2023-04-05 23:12:25 -07:00
methylDragon
000f788b65 Use dynamic typesupport identifier getter
Signed-off-by: methylDragon <methylDragon@gmail.com>
2023-04-03 21:12:02 -07:00
methylDragon
ca17caf6c4 Support type hashes
Signed-off-by: methylDragon <methylDragon@gmail.com>
2023-04-03 15:07:43 -07:00
methylDragon
e7e9a298c0 Add initializers for description and source getters
Signed-off-by: methylDragon <methylDragon@gmail.com>
2023-04-03 15:07:43 -07:00
methylDragon
8571b28664 Support type hashes
Signed-off-by: methylDragon <methylDragon@gmail.com>
2023-04-03 15:07:43 -07:00
methylDragon
7e0d063154 Lint
Signed-off-by: methylDragon <methylDragon@gmail.com>
2023-04-03 15:07:43 -07:00
methylDragon
113dc01dc7 Migrate methods to use return types
Signed-off-by: methylDragon <methylDragon@gmail.com>
2023-04-03 15:07:43 -07:00
methylDragon
aac28e7b9f Remove dynamic data/type aliases
Signed-off-by: methylDragon <methylDragon@gmail.com>
2023-04-03 15:07:43 -07:00
methylDragon
24e533eba7 Unalias dynamic typesupport types
Signed-off-by: methylDragon <methylDragon@gmail.com>
2023-04-03 15:07:43 -07:00
methylDragon
ca1fe2b41b Implement SubscriptionType enum flag
Signed-off-by: methylDragon <methylDragon@gmail.com>
2023-04-03 15:07:43 -07:00
methylDragon
57900a9f44 Add fixed string support
Signed-off-by: methylDragon <methylDragon@gmail.com>
2023-04-03 15:07:43 -07:00
methylDragon
363861f70c Alias dynamic_data with dynamic_message, pass desc, and build ts top-down
Signed-off-by: methylDragon <methylDragon@gmail.com>
2023-04-03 15:07:43 -07:00
methylDragon
9555859691 Use new deletes for memory management
Signed-off-by: methylDragon <methylDragon@gmail.com>
2023-04-03 15:07:43 -07:00
methylDragon
78a568d14a Only use general type construction and implement default values
Signed-off-by: methylDragon <methylDragon@gmail.com>
2023-04-03 15:07:43 -07:00
methylDragon
9ff28439d1 Implement direct taking of dynamic messages
Signed-off-by: methylDragon <methylDragon@gmail.com>
2023-04-03 15:07:43 -07:00
methylDragon
bfcc29e64b Use dynamic message type support wrappers
Signed-off-by: methylDragon <methylDragon@gmail.com>
2023-04-03 15:07:43 -07:00
methylDragon
927b9de2a5 Refactor dynamic typesupport to couple serialization support to objects
Signed-off-by: methylDragon <methylDragon@gmail.com>
2023-04-03 15:07:43 -07:00
methylDragon
5af57f8010 Migrate to rosidl_dynamic_typesupport and update field IDs
Signed-off-by: methylDragon <methylDragon@gmail.com>
2023-04-03 15:07:43 -07:00
methylDragon
0ce81c4fed Implement first cut
Signed-off-by: methylDragon <methylDragon@gmail.com>
2023-04-03 15:07:43 -07:00
methylDragon
a2484be7c3 Take ownership of description, add print, and update ser support API
Signed-off-by: methylDragon <methylDragon@gmail.com>
2023-04-03 15:07:43 -07:00
methylDragon
833efe0e2d Delete redundant wrapper class definitions
Signed-off-by: methylDragon <methylDragon@gmail.com>
2023-04-03 15:07:43 -07:00
methylDragon
554ad022a6 Refine wrappers
Signed-off-by: methylDragon <methylDragon@gmail.com>
2023-04-03 15:07:43 -07:00
methylDragon
a3e5d0a91a Implement dynamic message type support wrapper
Signed-off-by: methylDragon <methylDragon@gmail.com>
2023-04-03 15:07:43 -07:00
methylDragon
b8fd304546 Implement dynamic typesupport wrappers
Signed-off-by: methylDragon <methylDragon@gmail.com>
2023-04-03 15:07:43 -07:00
84 changed files with 5884 additions and 6271 deletions

View File

@@ -2,26 +2,6 @@
Changelog for package rclcpp
^^^^^^^^^^^^^^^^^^^^^^^^^^^^
20.0.0 (2023-04-13)
-------------------
* applied tracepoints for ring_buffer (`#2091 <https://github.com/ros2/rclcpp/issues/2091>`_)
* Dynamic Subscription (REP-2011 Subset): Stubs for rclcpp (`#2165 <https://github.com/ros2/rclcpp/issues/2165>`_)
* Add type_hash to cpp TopicEndpointInfo (`#2137 <https://github.com/ros2/rclcpp/issues/2137>`_)
* Trigger the intraprocess guard condition with data (`#2164 <https://github.com/ros2/rclcpp/issues/2164>`_)
* Minor grammar fix (`#2149 <https://github.com/ros2/rclcpp/issues/2149>`_)
* Fix unnecessary allocations in executor.cpp (`#2135 <https://github.com/ros2/rclcpp/issues/2135>`_)
* add Logger::get_effective_level(). (`#2141 <https://github.com/ros2/rclcpp/issues/2141>`_)
* Remove deprecated header (`#2139 <https://github.com/ros2/rclcpp/issues/2139>`_)
* Implement matched event (`#2105 <https://github.com/ros2/rclcpp/issues/2105>`_)
* use allocator via init_options argument. (`#2129 <https://github.com/ros2/rclcpp/issues/2129>`_)
* Fixes to silence some clang warnings. (`#2127 <https://github.com/ros2/rclcpp/issues/2127>`_)
* Documentation improvements on the executor (`#2125 <https://github.com/ros2/rclcpp/issues/2125>`_)
* Avoid losing waitable handles while using MultiThreadedExecutor (`#2109 <https://github.com/ros2/rclcpp/issues/2109>`_)
* Hook up the incompatible type event inside of rclcpp (`#2069 <https://github.com/ros2/rclcpp/issues/2069>`_)
* Update all rclcpp packages to C++17. (`#2121 <https://github.com/ros2/rclcpp/issues/2121>`_)
* Fix clang warning: bugprone-use-after-move (`#2116 <https://github.com/ros2/rclcpp/issues/2116>`_)
* Contributors: Barry Xu, Chris Lalancette, Christopher Wecht, Emerson Knapp, Michael Carroll, Tomoya Fujita, Yadu, mauropasse, methylDragon, ymski
19.3.0 (2023-03-01)
-------------------
* Fix memory leak in tracetools::get_symbol() (`#2104 <https://github.com/ros2/rclcpp/issues/2104>`_)

View File

@@ -59,15 +59,11 @@ set(${PROJECT_NAME}_SRCS
src/rclcpp/executable_list.cpp
src/rclcpp/executor.cpp
src/rclcpp/executors.cpp
src/rclcpp/executors/executor_entities_collection.cpp
src/rclcpp/executors/executor_entities_collector.cpp
src/rclcpp/executors/executor_notify_waitable.cpp
src/rclcpp/executors/multi_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/expand_topic_or_service_name.cpp
src/rclcpp/experimental/executors/events_executor/events_executor.cpp
src/rclcpp/experimental/timers_manager.cpp
src/rclcpp/future_return_code.cpp
src/rclcpp/generic_publisher.cpp
src/rclcpp/generic_subscription.cpp
@@ -104,6 +100,7 @@ set(${PROJECT_NAME}_SRCS
src/rclcpp/qos.cpp
src/rclcpp/event_handler.cpp
src/rclcpp/qos_overriding_options.cpp
src/rclcpp/dynamic_subscription.cpp
src/rclcpp/serialization.cpp
src/rclcpp/serialized_message.cpp
src/rclcpp/service.cpp

View File

@@ -93,54 +93,11 @@ public:
* determines whether a callback group is automatically added to an executor
* with the node with which it is associated.
*/
[[deprecated("Use CallbackGroup constructor with context function argument")]]
RCLCPP_PUBLIC
explicit CallbackGroup(
CallbackGroupType group_type,
bool automatically_add_to_executor_with_node = true);
/// Constructor for CallbackGroup.
/**
* Callback Groups have a type, either 'Mutually Exclusive' or 'Reentrant'
* and when creating one the type must be specified.
*
* Callbacks in Reentrant Callback Groups must be able to:
* - run at the same time as themselves (reentrant)
* - run at the same time as other callbacks in their group
* - run at the same time as other callbacks in other groups
*
* Callbacks in Mutually Exclusive Callback Groups:
* - will not be run multiple times simultaneously (non-reentrant)
* - will not be run at the same time as other callbacks in their group
* - but must run at the same time as callbacks in other groups
*
* Additionally, callback groups have a property which determines whether or
* not they are added to an executor with their associated node automatically.
* When creating a callback group the automatically_add_to_executor_with_node
* argument determines this behavior, and if true it will cause the newly
* created callback group to be added to an executor with the node when the
* Executor::add_node method is used.
* If false, this callback group will not be added automatically and would
* have to be added to an executor manually using the
* Executor::add_callback_group method.
*
* Whether the node was added to the executor before creating the callback
* group, or after, is irrelevant; the callback group will be automatically
* added to the executor in either case.
*
* \param[in] group_type The type of the callback group.
* \param[in] get_node_context Lambda to retrieve the node context when
* checking that the creating node is valid and using the guard condition.
* \param[in] automatically_add_to_executor_with_node A boolean that
* determines whether a callback group is automatically added to an executor
* with the node with which it is associated.
*/
RCLCPP_PUBLIC
explicit CallbackGroup(
CallbackGroupType group_type,
std::function<rclcpp::Context::SharedPtr(void)> get_node_context,
bool automatically_add_to_executor_with_node = true);
/// Default destructor.
RCLCPP_PUBLIC
~CallbackGroup();
@@ -180,42 +137,14 @@ public:
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
std::atomic_bool &
can_be_taken_from();
/// Get the group type.
/**
* \return the group type
*/
RCLCPP_PUBLIC
const CallbackGroupType &
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
void collect_all_ptrs(
std::function<void(const rclcpp::SubscriptionBase::SharedPtr &)> sub_func,
@@ -249,24 +178,11 @@ public:
bool
automatically_add_to_executor_with_node() const;
/// Retrieve the guard condition used to signal changes to this callback group.
/**
* \param[in] context_ptr context to use when creating the guard condition
* \return guard condition if it is valid, otherwise nullptr.
*/
[[deprecated("Use get_notify_guard_condition() without arguments")]]
/// Defer creating the notify guard condition and return it.
RCLCPP_PUBLIC
rclcpp::GuardCondition::SharedPtr
get_notify_guard_condition(const rclcpp::Context::SharedPtr context_ptr);
/// Retrieve the guard condition used to signal changes to this callback group.
/**
* \return guard condition if it is valid, otherwise nullptr.
*/
RCLCPP_PUBLIC
rclcpp::GuardCondition::SharedPtr
get_notify_guard_condition();
/// Trigger the notify guard condition.
RCLCPP_PUBLIC
void
@@ -318,8 +234,6 @@ protected:
std::shared_ptr<rclcpp::GuardCondition> notify_guard_condition_ = nullptr;
std::recursive_mutex notify_guard_condition_mutex_;
std::function<rclcpp::Context::SharedPtr(void)> get_context_;
private:
template<typename TypeT, typename Function>
typename TypeT::SharedPtr _find_ptrs_if_impl(

View File

@@ -0,0 +1,176 @@
// Copyright 2022 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__DYNAMIC_SUBSCRIPTION_HPP_
#define RCLCPP__DYNAMIC_SUBSCRIPTION_HPP_
#include <rosidl_dynamic_typesupport/identifier.h>
#include <functional>
#include <memory>
#include <string>
#include "rcpputils/shared_library.hpp"
#include "rclcpp/callback_group.hpp"
#include "rclcpp/dynamic_typesupport/dynamic_message_type_support.hpp"
#include "rclcpp/macros.hpp"
#include "rclcpp/node_interfaces/node_base_interface.hpp"
#include "rclcpp/node_interfaces/node_topics_interface.hpp"
#include "rclcpp/qos.hpp"
#include "rclcpp/serialized_message.hpp"
#include "rclcpp/subscription_base.hpp"
#include "rclcpp/typesupport_helpers.hpp"
#include "rclcpp/visibility_control.hpp"
namespace rclcpp
{
/// %Subscription for messages whose type descriptions are obtained at runtime.
/**
* Since the type is not known at compile time, this is not a template, and the dynamic library
* containing type support information has to be identified and loaded based on the type name.
*
* NOTE(methylDragon): No considerations for intra-process handling are made.
*/
class DynamicSubscription : public rclcpp::SubscriptionBase
{
public:
// cppcheck-suppress unknownMacro
RCLCPP_SMART_PTR_DEFINITIONS(DynamicSubscription)
template<typename AllocatorT = std::allocator<void>>
DynamicSubscription(
rclcpp::node_interfaces::NodeBaseInterface * node_base,
rclcpp::dynamic_typesupport::DynamicMessageTypeSupport::SharedPtr type_support,
const std::string & topic_name,
const rclcpp::QoS & qos,
std::function<void(
rclcpp::dynamic_typesupport::DynamicMessage::SharedPtr,
const rosidl_runtime_c__type_description__TypeDescription &
)> callback,
const rclcpp::SubscriptionOptionsWithAllocator<AllocatorT> & options,
bool use_take_dynamic_message = true)
: SubscriptionBase(
node_base,
type_support->get_const_rosidl_message_type_support(),
topic_name,
options.to_rcl_subscription_options(
qos),
options.event_callbacks,
options.use_default_callbacks,
use_take_dynamic_message ? SubscriptionType::DYNAMIC_MESSAGE_DIRECT : SubscriptionType::DYNAMIC_MESSAGE_FROM_SERIALIZED), // NOLINT
ts_(type_support),
callback_(callback),
serialization_support_(nullptr),
dynamic_message_(nullptr),
dynamic_message_type_(nullptr)
{
if (!type_support) {
throw std::runtime_error("DynamicMessageTypeSupport cannot be nullptr!");
}
if (type_support->get_const_rosidl_message_type_support().typesupport_identifier !=
rosidl_get_dynamic_typesupport_identifier())
{
throw std::runtime_error(
"DynamicSubscription must use dynamic type introspection type support!");
}
serialization_support_ = type_support->get_shared_dynamic_serialization_support();
dynamic_message_type_ = type_support->get_shared_dynamic_message_type()->clone_shared();
dynamic_message_ = type_support->get_shared_dynamic_message()->clone_shared();
}
// TODO(methylDragon):
/// Deferred type description constructor, only usable if the middleware implementation supports
/// type discovery
RCLCPP_PUBLIC
virtual ~DynamicSubscription() = default;
// Same as create_serialized_message() as the subscription is to serialized_messages only
RCLCPP_PUBLIC
std::shared_ptr<void> create_message() override;
RCLCPP_PUBLIC
std::shared_ptr<rclcpp::SerializedMessage> create_serialized_message() override;
/// Cast the message to a rclcpp::SerializedMessage and call the callback.
RCLCPP_PUBLIC
void handle_message(
std::shared_ptr<void> & message, const rclcpp::MessageInfo & message_info) override;
/// Handle dispatching rclcpp::SerializedMessage to user callback.
RCLCPP_PUBLIC
void
handle_serialized_message(
const std::shared_ptr<rclcpp::SerializedMessage> & serialized_message,
const rclcpp::MessageInfo & message_info) override;
/// This function is currently not implemented.
RCLCPP_PUBLIC
void handle_loaned_message(
void * loaned_message, const rclcpp::MessageInfo & message_info) override;
// Same as return_serialized_message() as the subscription is to serialized_messages only
RCLCPP_PUBLIC
void return_message(std::shared_ptr<void> & message) override;
RCLCPP_PUBLIC
void return_serialized_message(std::shared_ptr<rclcpp::SerializedMessage> & message) override;
// DYNAMIC TYPE ==================================================================================
// TODO(methylDragon): Reorder later
RCLCPP_PUBLIC
rclcpp::dynamic_typesupport::DynamicMessageType::SharedPtr
get_shared_dynamic_message_type() override;
RCLCPP_PUBLIC
rclcpp::dynamic_typesupport::DynamicMessage::SharedPtr get_shared_dynamic_message() override;
RCLCPP_PUBLIC
rclcpp::dynamic_typesupport::DynamicSerializationSupport::SharedPtr
get_shared_dynamic_serialization_support() override;
RCLCPP_PUBLIC
rclcpp::dynamic_typesupport::DynamicMessage::SharedPtr create_dynamic_message() override;
RCLCPP_PUBLIC
void return_dynamic_message(
rclcpp::dynamic_typesupport::DynamicMessage::SharedPtr & message) override;
RCLCPP_PUBLIC
void handle_dynamic_message(
const rclcpp::dynamic_typesupport::DynamicMessage::SharedPtr & message,
const rclcpp::MessageInfo & message_info) override;
private:
RCLCPP_DISABLE_COPY(DynamicSubscription)
rclcpp::dynamic_typesupport::DynamicMessageTypeSupport::SharedPtr ts_;
std::function<void(
rclcpp::dynamic_typesupport::DynamicMessage::SharedPtr,
const rosidl_runtime_c__type_description__TypeDescription &
)> callback_;
rclcpp::dynamic_typesupport::DynamicSerializationSupport::SharedPtr serialization_support_;
rclcpp::dynamic_typesupport::DynamicMessage::SharedPtr dynamic_message_;
rclcpp::dynamic_typesupport::DynamicMessageType::SharedPtr dynamic_message_type_;
};
} // namespace rclcpp
#endif // RCLCPP__DYNAMIC_SUBSCRIPTION_HPP_

View File

@@ -0,0 +1,327 @@
// Copyright 2023 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__DYNAMIC_TYPESUPPORT__DETAIL__DYNAMIC_MESSAGE_IMPL_HPP_
#define RCLCPP__DYNAMIC_TYPESUPPORT__DETAIL__DYNAMIC_MESSAGE_IMPL_HPP_
#include <rosidl_dynamic_typesupport/types.h>
#include <rosidl_dynamic_typesupport/api/dynamic_data.h>
#include <cstdint>
#include <cstddef>
#include <memory>
#include <string>
#include "rclcpp/exceptions.hpp"
#ifndef RCLCPP__DYNAMIC_TYPESUPPORT__DYNAMIC_MESSAGE_HPP_
#include "rclcpp/dynamic_typesupport/dynamic_message.hpp"
#endif
#define __DYNAMIC_MESSAGE_GET_VALUE_BY_ID_FN(ValueT, FunctionT) \
template<> \
ValueT \
DynamicMessage::get_value<ValueT>(rosidl_dynamic_typesupport_member_id_t id) \
{ \
ValueT out; \
rosidl_dynamic_typesupport_dynamic_data_get_ ## FunctionT ## _value( \
&rosidl_dynamic_data_, id, &out); \
return out; \
}
#define __DYNAMIC_MESSAGE_GET_VALUE_BY_NAME_FN(ValueT, FunctionT) \
template<> \
ValueT \
DynamicMessage::get_value<ValueT>(const std::string & name) \
{ \
return get_value<ValueT>(get_member_id(name)); \
}
#define __DYNAMIC_MESSAGE_SET_VALUE_BY_ID_FN(ValueT, FunctionT) \
template<> \
void \
DynamicMessage::set_value<ValueT>(rosidl_dynamic_typesupport_member_id_t id, ValueT value) \
{ \
rosidl_dynamic_typesupport_dynamic_data_set_ ## FunctionT ## _value( \
&rosidl_dynamic_data_, id, value); \
}
#define __DYNAMIC_MESSAGE_SET_VALUE_BY_NAME_FN(ValueT, FunctionT) \
template<> \
void \
DynamicMessage::set_value<ValueT>(const std::string & name, ValueT value) \
{ \
set_value<ValueT>(get_member_id(name), value); \
}
#define __DYNAMIC_MESSAGE_INSERT_VALUE(ValueT, FunctionT) \
template<> \
rosidl_dynamic_typesupport_member_id_t \
DynamicMessage::insert_value<ValueT>(ValueT value) \
{ \
rosidl_dynamic_typesupport_member_id_t out; \
rosidl_dynamic_typesupport_dynamic_data_insert_ ## FunctionT ## _value( \
&rosidl_dynamic_data_, value, &out); \
return out; \
}
#define DYNAMIC_MESSAGE_DEFINITIONS(ValueT, FunctionT) \
__DYNAMIC_MESSAGE_GET_VALUE_BY_ID_FN(ValueT, FunctionT) \
__DYNAMIC_MESSAGE_GET_VALUE_BY_NAME_FN(ValueT, FunctionT) \
__DYNAMIC_MESSAGE_SET_VALUE_BY_ID_FN(ValueT, FunctionT) \
__DYNAMIC_MESSAGE_SET_VALUE_BY_NAME_FN(ValueT, FunctionT) \
__DYNAMIC_MESSAGE_INSERT_VALUE(ValueT, FunctionT)
namespace rclcpp
{
namespace dynamic_typesupport
{
/**
* Since we're in a ROS layer, these should support all ROS interface C++ types as found in:
* https://docs.ros.org/en/rolling/Concepts/About-ROS-Interfaces.html
*
* Explicitly:
* - Basic types: bool, byte, char
* - Float types: float, double
* - Int types: int8_t, int16_t, int32_t, int64_t
* - Unsigned int types: uint8_t, uint16_t, uint32_t, uint64_t
* - String types: std::string, std::u16string
*/
DYNAMIC_MESSAGE_DEFINITIONS(bool, bool);
// DYNAMIC_MESSAGE_DEFINITIONS(std::byte, byte);
DYNAMIC_MESSAGE_DEFINITIONS(char, char);
DYNAMIC_MESSAGE_DEFINITIONS(float, float32);
DYNAMIC_MESSAGE_DEFINITIONS(double, float64);
DYNAMIC_MESSAGE_DEFINITIONS(int8_t, int8);
DYNAMIC_MESSAGE_DEFINITIONS(int16_t, int16);
DYNAMIC_MESSAGE_DEFINITIONS(int32_t, int32);
DYNAMIC_MESSAGE_DEFINITIONS(int64_t, int64);
DYNAMIC_MESSAGE_DEFINITIONS(uint8_t, uint8);
DYNAMIC_MESSAGE_DEFINITIONS(uint16_t, uint16);
DYNAMIC_MESSAGE_DEFINITIONS(uint32_t, uint32);
DYNAMIC_MESSAGE_DEFINITIONS(uint64_t, uint64);
// DYNAMIC_MESSAGE_DEFINITIONS(std::string, std::string);
// DYNAMIC_MESSAGE_DEFINITIONS(std::u16string, std::u16string);
// Byte and String getters have a different implementation and are defined below
// BYTE ============================================================================================
template<>
std::byte
DynamicMessage::get_value<std::byte>(rosidl_dynamic_typesupport_member_id_t id)
{
unsigned char out;
rosidl_dynamic_typesupport_dynamic_data_get_byte_value(&get_rosidl_dynamic_data(), id, &out);
return static_cast<std::byte>(out);
}
template<>
std::byte
DynamicMessage::get_value<std::byte>(const std::string & name)
{
return get_value<std::byte>(get_member_id(name));
}
template<>
void
DynamicMessage::set_value<std::byte>(
rosidl_dynamic_typesupport_member_id_t id, const std::byte value)
{
rosidl_dynamic_typesupport_dynamic_data_set_byte_value(
&rosidl_dynamic_data_, id, static_cast<unsigned char>(value));
}
template<>
void
DynamicMessage::set_value<std::byte>(const std::string & name, const std::byte value)
{
set_value<std::byte>(get_member_id(name), value);
}
template<>
rosidl_dynamic_typesupport_member_id_t
DynamicMessage::insert_value<std::byte>(const std::byte value)
{
rosidl_dynamic_typesupport_member_id_t out;
rosidl_dynamic_typesupport_dynamic_data_insert_byte_value(
&rosidl_dynamic_data_, static_cast<unsigned char>(value), &out);
return out;
}
// STRINGS =========================================================================================
template<>
std::string
DynamicMessage::get_value<std::string>(rosidl_dynamic_typesupport_member_id_t id)
{
size_t buf_length;
char * buf = nullptr;
rosidl_dynamic_typesupport_dynamic_data_get_string_value(
&get_rosidl_dynamic_data(), id, &buf, &buf_length);
auto out = std::string(buf, buf_length);
delete buf;
return out;
}
template<>
std::u16string
DynamicMessage::get_value<std::u16string>(rosidl_dynamic_typesupport_member_id_t id)
{
size_t buf_length;
char16_t * buf = nullptr;
rosidl_dynamic_typesupport_dynamic_data_get_wstring_value(
&get_rosidl_dynamic_data(), id, &buf, &buf_length);
auto out = std::u16string(buf, buf_length);
delete buf;
return out;
}
template<>
std::string
DynamicMessage::get_value<std::string>(const std::string & name)
{
return get_value<std::string>(get_member_id(name));
}
template<>
std::u16string
DynamicMessage::get_value<std::u16string>(const std::string & name)
{
return get_value<std::u16string>(get_member_id(name));
}
template<>
void
DynamicMessage::set_value<std::string>(
rosidl_dynamic_typesupport_member_id_t id, const std::string value)
{
rosidl_dynamic_typesupport_dynamic_data_set_string_value(
&rosidl_dynamic_data_, id, value.c_str(), value.size());
}
template<>
void
DynamicMessage::set_value<std::u16string>(
rosidl_dynamic_typesupport_member_id_t id, const std::u16string value)
{
rosidl_dynamic_typesupport_dynamic_data_set_wstring_value(
&rosidl_dynamic_data_, id, value.c_str(), value.size());
}
template<>
void
DynamicMessage::set_value<std::string>(const std::string & name, const std::string value)
{
set_value<std::string>(get_member_id(name), value);
}
template<>
void
DynamicMessage::set_value<std::u16string>(const std::string & name, const std::u16string value)
{
set_value<std::u16string>(get_member_id(name), value);
}
template<>
rosidl_dynamic_typesupport_member_id_t
DynamicMessage::insert_value<std::string>(const std::string value)
{
rosidl_dynamic_typesupport_member_id_t out;
rosidl_dynamic_typesupport_dynamic_data_insert_string_value(
&rosidl_dynamic_data_, value.c_str(), value.size(), &out);
return out;
}
template<>
rosidl_dynamic_typesupport_member_id_t
DynamicMessage::insert_value<std::u16string>(const std::u16string value)
{
rosidl_dynamic_typesupport_member_id_t out;
rosidl_dynamic_typesupport_dynamic_data_insert_wstring_value(
&rosidl_dynamic_data_, value.c_str(), value.size(), &out);
return out;
}
// THROW FOR UNSUPPORTED TYPES =====================================================================
template<typename ValueT>
ValueT
DynamicMessage::get_value(rosidl_dynamic_typesupport_member_id_t id)
{
throw rclcpp::exceptions::UnimplementedError("get_value is not implemented for input type");
}
template<typename ValueT>
ValueT
DynamicMessage::get_value(const std::string & name)
{
throw rclcpp::exceptions::UnimplementedError("get_value is not implemented for input type");
}
template<typename ValueT>
void
DynamicMessage::set_value(
rosidl_dynamic_typesupport_member_id_t id, ValueT value)
{
throw rclcpp::exceptions::UnimplementedError("set_value is not implemented for input type");
}
template<typename ValueT>
void
DynamicMessage::set_value(const std::string & name, ValueT value)
{
throw rclcpp::exceptions::UnimplementedError("set_value is not implemented for input type");
}
template<typename ValueT>
rosidl_dynamic_typesupport_member_id_t
DynamicMessage::insert_value(ValueT value)
{
throw rclcpp::exceptions::UnimplementedError("insert_value is not implemented for input type");
}
} // namespace dynamic_typesupport
} // namespace rclcpp
#undef __DYNAMIC_MESSAGE_GET_VALUE_BY_ID_FN
#undef __DYNAMIC_MESSAGE_GET_VALUE_BY_NAME_FN
#undef __DYNAMIC_MESSAGE_SET_VALUE_BY_ID_FN
#undef __DYNAMIC_MESSAGE_SET_VALUE_BY_NAME_FN
#undef __DYNAMIC_MESSAGE_INSERT_VALUE
#undef DYNAMIC_MESSAGE_DEFINITIONS
#endif // RCLCPP__DYNAMIC_TYPESUPPORT__DETAIL__DYNAMIC_MESSAGE_IMPL_HPP_

View File

@@ -0,0 +1,189 @@
// Copyright 2023 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__DYNAMIC_TYPESUPPORT__DETAIL__DYNAMIC_MESSAGE_TYPE_BUILDER_IMPL_HPP_
#define RCLCPP__DYNAMIC_TYPESUPPORT__DETAIL__DYNAMIC_MESSAGE_TYPE_BUILDER_IMPL_HPP_
#include <rosidl_dynamic_typesupport/types.h>
#include <rosidl_dynamic_typesupport/api/dynamic_type.h>
#include <cstdint>
#include <cstddef>
#include <memory>
#include <string>
#include "rclcpp/exceptions.hpp"
#ifndef RCLCPP__DYNAMIC_TYPESUPPORT__DYNAMIC_MESSAGE_TYPE_BUILDER_HPP_
#include "rclcpp/dynamic_typesupport/dynamic_message_type_builder.hpp"
#endif
#define __DYNAMIC_MESSAGE_TYPE_BUILDER_ADD_MEMBER_FN(MemberT, FunctionT) \
template<> \
void \
DynamicMessageTypeBuilder::add_member<MemberT>( \
rosidl_dynamic_typesupport_member_id_t id, \
const std::string & name, \
const std::string & default_value) \
{ \
rosidl_dynamic_typesupport_dynamic_type_builder_add_ ## FunctionT ## _member( \
&rosidl_dynamic_type_builder_, \
id, name.c_str(), name.size(), default_value.c_str(), default_value.size()); \
}
#define __DYNAMIC_MESSAGE_TYPE_BUILDER_ADD_ARRAY_MEMBER_FN(MemberT, FunctionT) \
template<> \
void \
DynamicMessageTypeBuilder::add_array_member<MemberT>( \
rosidl_dynamic_typesupport_member_id_t id, const std::string & name, \
size_t array_length, \
const std::string & default_value) \
{ \
rosidl_dynamic_typesupport_dynamic_type_builder_add_ ## FunctionT ## _array_member( \
&rosidl_dynamic_type_builder_, \
id, name.c_str(), name.size(), default_value.c_str(), default_value.size(), \
array_length); \
}
#define __DYNAMIC_MESSAGE_TYPE_BUILDER_ADD_UNBOUNDED_SEQUENCE_MEMBER_FN(MemberT, FunctionT) \
template<> \
void \
DynamicMessageTypeBuilder::add_unbounded_sequence_member<MemberT>( \
rosidl_dynamic_typesupport_member_id_t id, \
const std::string & name, \
const std::string & default_value) \
{ \
rosidl_dynamic_typesupport_dynamic_type_builder_add_ ## FunctionT ## \
_unbounded_sequence_member( \
&rosidl_dynamic_type_builder_, \
id, name.c_str(), name.size(), default_value.c_str(), default_value.size()); \
}
#define __DYNAMIC_MESSAGE_TYPE_BUILDER_ADD_BOUNDED_SEQUENCE_MEMBER_FN(MemberT, FunctionT) \
template<> \
void \
DynamicMessageTypeBuilder::add_bounded_sequence_member<MemberT>( \
rosidl_dynamic_typesupport_member_id_t id, \
const std::string & name, \
size_t sequence_bound, \
const std::string & default_value) \
{ \
rosidl_dynamic_typesupport_dynamic_type_builder_add_ ## FunctionT ## _bounded_sequence_member( \
&rosidl_dynamic_type_builder_, \
id, name.c_str(), name.size(), default_value.c_str(), default_value.size(), \
sequence_bound); \
}
#define DYNAMIC_MESSAGE_TYPE_BUILDER_DEFINITIONS(MemberT, FunctionT) \
__DYNAMIC_MESSAGE_TYPE_BUILDER_ADD_MEMBER_FN(MemberT, FunctionT) \
__DYNAMIC_MESSAGE_TYPE_BUILDER_ADD_ARRAY_MEMBER_FN(MemberT, FunctionT) \
__DYNAMIC_MESSAGE_TYPE_BUILDER_ADD_UNBOUNDED_SEQUENCE_MEMBER_FN(MemberT, FunctionT) \
__DYNAMIC_MESSAGE_TYPE_BUILDER_ADD_BOUNDED_SEQUENCE_MEMBER_FN(MemberT, FunctionT) \
namespace rclcpp
{
namespace dynamic_typesupport
{
/**
* Since we're in a ROS layer, these should support all ROS interface C++ types as found in:
* https://docs.ros.org/en/rolling/Concepts/About-ROS-Interfaces.html
*
* Explicitly:
* - Basic types: bool, byte, char
* - Float types: float, double
* - Int types: int8_t, int16_t, int32_t, int64_t
* - Unsigned int types: uint8_t, uint16_t, uint32_t, uint64_t
* - String types: std::string, std::u16string
*/
DYNAMIC_MESSAGE_TYPE_BUILDER_DEFINITIONS(bool, bool);
DYNAMIC_MESSAGE_TYPE_BUILDER_DEFINITIONS(std::byte, byte);
DYNAMIC_MESSAGE_TYPE_BUILDER_DEFINITIONS(char, char);
DYNAMIC_MESSAGE_TYPE_BUILDER_DEFINITIONS(float, float32);
DYNAMIC_MESSAGE_TYPE_BUILDER_DEFINITIONS(double, float64);
DYNAMIC_MESSAGE_TYPE_BUILDER_DEFINITIONS(int8_t, int8);
DYNAMIC_MESSAGE_TYPE_BUILDER_DEFINITIONS(int16_t, int16);
DYNAMIC_MESSAGE_TYPE_BUILDER_DEFINITIONS(int32_t, int32);
DYNAMIC_MESSAGE_TYPE_BUILDER_DEFINITIONS(int64_t, int64);
DYNAMIC_MESSAGE_TYPE_BUILDER_DEFINITIONS(uint8_t, uint8);
DYNAMIC_MESSAGE_TYPE_BUILDER_DEFINITIONS(uint16_t, uint16);
DYNAMIC_MESSAGE_TYPE_BUILDER_DEFINITIONS(uint32_t, uint32);
DYNAMIC_MESSAGE_TYPE_BUILDER_DEFINITIONS(uint64_t, uint64);
DYNAMIC_MESSAGE_TYPE_BUILDER_DEFINITIONS(std::string, string);
DYNAMIC_MESSAGE_TYPE_BUILDER_DEFINITIONS(std::u16string, wstring);
// THROW FOR UNSUPPORTED TYPES =====================================================================
template<typename MemberT>
void
DynamicMessageTypeBuilder::add_member(
rosidl_dynamic_typesupport_member_id_t id,
const std::string & name,
const std::string & default_value)
{
throw rclcpp::exceptions::UnimplementedError(
"add_member is not implemented for input type");
}
template<typename MemberT>
void
DynamicMessageTypeBuilder::add_array_member(
rosidl_dynamic_typesupport_member_id_t id,
const std::string & name,
size_t array_length, const std::string & default_value)
{
throw rclcpp::exceptions::UnimplementedError(
"add_array_member is not implemented for input type");
}
template<typename MemberT>
void
DynamicMessageTypeBuilder::add_unbounded_sequence_member(
rosidl_dynamic_typesupport_member_id_t id,
const std::string & name,
const std::string & default_value)
{
throw rclcpp::exceptions::UnimplementedError(
"add_unbounded_sequence_member is not implemented for input type");
}
template<typename MemberT>
void
DynamicMessageTypeBuilder::add_bounded_sequence_member(
rosidl_dynamic_typesupport_member_id_t id,
const std::string & name,
size_t sequence_bound,
const std::string & default_value)
{
throw rclcpp::exceptions::UnimplementedError(
"add_bounded_sequence_member is not implemented for input type");
}
} // namespace dynamic_typesupport
} // namespace rclcpp
#undef __DYNAMIC_MESSAGE_TYPE_BUILDER_ADD_MEMBER_FN
#undef __DYNAMIC_MESSAGE_TYPE_BUILDER_ADD_ARRAY_MEMBER_FN
#undef __DYNAMIC_MESSAGE_TYPE_BUILDER_ADD_UNBOUNDED_SEQUENCE_MEMBER_FN
#undef __DYNAMIC_MESSAGE_TYPE_BUILDER_ADD_BOUNDED_SEQUENCE_MEMBER_FN
#undef DYNAMIC_MESSAGE_TYPE_BUILDER_DEFINITIONS
#endif // RCLCPP__DYNAMIC_TYPESUPPORT__DETAIL__DYNAMIC_MESSAGE_TYPE_BUILDER_IMPL_HPP_

View File

@@ -32,16 +32,372 @@ namespace rclcpp
namespace dynamic_typesupport
{
class DynamicMessageType;
class DynamicMessageTypeBuilder;
/// Utility wrapper class for rosidl_dynamic_typesupport_dynamic_data_t
/// STUBBED OUT
/**
* This class:
* - Exposes getter methods for the struct
* - Exposes the underlying serialization support API
*
* Ownership:
* - This class borrows the rosidl_dynamic_typesupport_serialization_support_t stored in the passed
* DynamicSerializationSupport. So it cannot outlive the DynamicSerializationSupport.
* - The DynamicSerializationSupport's rosidl_dynamic_typesupport_serialization_support_t pointer
* must point to the same location in memory as the stored raw pointer!
*
* Note: This class is meant to map to the lower level rosidl_dynamic_typesupport_dynamic_data_t,
* even though rosidl_dynamic_typesupport_dynamic_data_t is equivalent to
* rosidl_dynamic_typesupport_dynamic_data_t, exposing the fundamental methods available to
* rosidl_dynamic_typesupport_dynamic_data_t, allowing the user to access the data's fields.
*/
class DynamicMessage : public std::enable_shared_from_this<DynamicMessage>
{
public:
RCLCPP_SMART_PTR_ALIASES_ONLY(DynamicMessage)
// CONSTRUCTION ==================================================================================
// Most constructors require a passed in DynamicSerializationSupport::SharedPtr, to extend the
// lifetime of the serialization support (if the constructor cannot otherwise get it from args).
//
// In cases where a dynamic data pointer is passed, the serialization support composed by
// the data should be the exact same object managed by the DynamicSerializationSupport,
// otherwise the lifetime management will not work properly.
/// Construct a new DynamicMessage with the provided dynamic type builder, using its allocator
RCLCPP_PUBLIC
explicit DynamicMessage(std::shared_ptr<DynamicMessageTypeBuilder> dynamic_type_builder);
/// Construct a new DynamicMessage with the provided dynamic type builder and allocator
RCLCPP_PUBLIC
DynamicMessage(
std::shared_ptr<DynamicMessageTypeBuilder> dynamic_type_builder,
rcl_allocator_t allocator);
/// Construct a new DynamicMessage with the provided dynamic type, using its allocator
RCLCPP_PUBLIC
explicit DynamicMessage(std::shared_ptr<DynamicMessageType> dynamic_type);
/// Construct a new DynamicMessage with the provided dynamic type and allocator
RCLCPP_PUBLIC
DynamicMessage(
std::shared_ptr<DynamicMessageType> dynamic_type,
rcl_allocator_t allocator);
/// Assume ownership of struct
RCLCPP_PUBLIC
DynamicMessage(
DynamicSerializationSupport::SharedPtr serialization_support,
rosidl_dynamic_typesupport_dynamic_data_t && rosidl_dynamic_data);
/// Loaning constructor
/// Must only be called with a rosidl dynaimc data object obtained from loaning!
// NOTE(methylDragon): I'd put this in protected, but I need this exposed to
// enable_shared_from_this...
RCLCPP_PUBLIC
DynamicMessage(
DynamicMessage::SharedPtr parent_data,
rosidl_dynamic_typesupport_dynamic_data_t && rosidl_loaned_data);
// NOTE(methylDragon): Deliberately no constructor from description to nudge users towards using
// construction from dynamic type/builder, which is more efficient
/// Move constructor
RCLCPP_PUBLIC
DynamicMessage(DynamicMessage && other) noexcept;
/// Move assignment
RCLCPP_PUBLIC
DynamicMessage & operator=(DynamicMessage && other) noexcept;
RCLCPP_PUBLIC
virtual ~DynamicMessage();
// GETTERS =======================================================================================
RCLCPP_PUBLIC
const std::string
get_serialization_library_identifier() const;
RCLCPP_PUBLIC
const std::string
get_name() const;
RCLCPP_PUBLIC
rosidl_dynamic_typesupport_dynamic_data_t &
get_rosidl_dynamic_data();
RCLCPP_PUBLIC
const rosidl_dynamic_typesupport_dynamic_data_t &
get_rosidl_dynamic_data() const;
RCLCPP_PUBLIC
DynamicSerializationSupport::SharedPtr
get_shared_dynamic_serialization_support();
RCLCPP_PUBLIC
DynamicSerializationSupport::ConstSharedPtr
get_shared_dynamic_serialization_support() const;
RCLCPP_PUBLIC
size_t
get_item_count() const;
RCLCPP_PUBLIC
rosidl_dynamic_typesupport_member_id_t
get_member_id(size_t index) const;
RCLCPP_PUBLIC
rosidl_dynamic_typesupport_member_id_t
get_member_id(const std::string & name) const;
RCLCPP_PUBLIC
rosidl_dynamic_typesupport_member_id_t
get_array_index(size_t index) const;
RCLCPP_PUBLIC
rosidl_dynamic_typesupport_member_id_t
get_array_index(const std::string & name) const;
// METHODS =======================================================================================
RCLCPP_PUBLIC
DynamicMessage
clone(rcl_allocator_t allocator = rcl_get_default_allocator());
RCLCPP_PUBLIC
DynamicMessage::SharedPtr
clone_shared(rcl_allocator_t allocator = rcl_get_default_allocator());
RCLCPP_PUBLIC
DynamicMessage
init_from_type(
DynamicMessageType & type, rcl_allocator_t allocator = rcl_get_default_allocator()) const;
RCLCPP_PUBLIC
DynamicMessage::SharedPtr
init_from_type_shared(
DynamicMessageType & type, rcl_allocator_t allocator = rcl_get_default_allocator()) const;
RCLCPP_PUBLIC
bool
equals(const DynamicMessage & other) const;
RCLCPP_PUBLIC
DynamicMessage::SharedPtr
loan_value(
rosidl_dynamic_typesupport_member_id_t id,
rcl_allocator_t allocator = rcl_get_default_allocator());
RCLCPP_PUBLIC
DynamicMessage::SharedPtr
loan_value(
const std::string & name,
rcl_allocator_t allocator = rcl_get_default_allocator());
RCLCPP_PUBLIC
void
clear_all_values();
RCLCPP_PUBLIC
void
clear_nonkey_values();
RCLCPP_PUBLIC
void
clear_value(rosidl_dynamic_typesupport_member_id_t id);
RCLCPP_PUBLIC
void
clear_value(const std::string & name);
RCLCPP_PUBLIC
void
clear_sequence();
RCLCPP_PUBLIC
rosidl_dynamic_typesupport_member_id_t
insert_sequence_data();
RCLCPP_PUBLIC
void
remove_sequence_data(rosidl_dynamic_typesupport_member_id_t index);
RCLCPP_PUBLIC
bool
serialize(rcl_serialized_message_t & buffer);
RCLCPP_PUBLIC
bool
deserialize(rcl_serialized_message_t & buffer);
// MEMBER ACCESS TEMPLATES =======================================================================
/**
* Since we're in a ROS layer, these should support all ROS interface C++ types as found in:
* https://docs.ros.org/en/rolling/Concepts/About-ROS-Interfaces.html
*
* Explicitly:
* - Basic types: bool, byte, char
* - Float types: float, double
* - Int types: int8_t, int16_t, int32_t, int64_t
* - Unsigned int types: uint8_t, uint16_t, uint32_t, uint64_t
* - String types: std::string, std::u16string
*/
template<typename ValueT>
ValueT
get_value(rosidl_dynamic_typesupport_member_id_t id);
template<typename ValueT>
ValueT
get_value(const std::string & name);
template<typename ValueT>
void
set_value(rosidl_dynamic_typesupport_member_id_t id, ValueT value);
template<typename ValueT>
void
set_value(const std::string & name, ValueT value);
template<typename ValueT>
rosidl_dynamic_typesupport_member_id_t
insert_value(ValueT value);
// FIXED STRING MEMBER ACCESS ====================================================================
RCLCPP_PUBLIC
const std::string
get_fixed_string_value(rosidl_dynamic_typesupport_member_id_t id, size_t string_length);
RCLCPP_PUBLIC
const std::string
get_fixed_string_value(const std::string & name, size_t string_length);
RCLCPP_PUBLIC
const std::u16string
get_fixed_wstring_value(rosidl_dynamic_typesupport_member_id_t id, size_t wstring_length);
RCLCPP_PUBLIC
const std::u16string
get_fixed_wstring_value(const std::string & name, size_t wstring_length);
RCLCPP_PUBLIC
void
set_fixed_string_value(
rosidl_dynamic_typesupport_member_id_t id, const std::string value, size_t string_length);
RCLCPP_PUBLIC
void
set_fixed_string_value(const std::string & name, const std::string value, size_t string_length);
RCLCPP_PUBLIC
void
set_fixed_wstring_value(
rosidl_dynamic_typesupport_member_id_t id, const std::u16string value, size_t wstring_length);
RCLCPP_PUBLIC
void
set_fixed_wstring_value(
const std::string & name, const std::u16string value, size_t wstring_length);
RCLCPP_PUBLIC
rosidl_dynamic_typesupport_member_id_t
insert_fixed_string_value(const std::string value, size_t string_length);
RCLCPP_PUBLIC
rosidl_dynamic_typesupport_member_id_t
insert_fixed_wstring_value(const std::u16string value, size_t wstring_length);
// BOUNDED STRING MEMBER ACCESS ==================================================================
RCLCPP_PUBLIC
const std::string
get_bounded_string_value(rosidl_dynamic_typesupport_member_id_t id, size_t string_bound);
RCLCPP_PUBLIC
const std::string
get_bounded_string_value(const std::string & name, size_t string_bound);
RCLCPP_PUBLIC
const std::u16string
get_bounded_wstring_value(rosidl_dynamic_typesupport_member_id_t id, size_t wstring_bound);
RCLCPP_PUBLIC
const std::u16string
get_bounded_wstring_value(const std::string & name, size_t wstring_bound);
RCLCPP_PUBLIC
void
set_bounded_string_value(
rosidl_dynamic_typesupport_member_id_t id, const std::string value, size_t string_bound);
RCLCPP_PUBLIC
void
set_bounded_string_value(const std::string & name, const std::string value, size_t string_bound);
RCLCPP_PUBLIC
void
set_bounded_wstring_value(
rosidl_dynamic_typesupport_member_id_t id, const std::u16string value, size_t wstring_bound);
RCLCPP_PUBLIC
void
set_bounded_wstring_value(
const std::string & name, const std::u16string value, size_t wstring_bound);
RCLCPP_PUBLIC
rosidl_dynamic_typesupport_member_id_t
insert_bounded_string_value(const std::string value, size_t string_bound);
RCLCPP_PUBLIC
rosidl_dynamic_typesupport_member_id_t
insert_bounded_wstring_value(const std::u16string value, size_t wstring_bound);
// NESTED MEMBER ACCESS ==========================================================================
RCLCPP_PUBLIC
DynamicMessage
get_complex_value(
rosidl_dynamic_typesupport_member_id_t id,
rcl_allocator_t allocator = rcl_get_default_allocator());
RCLCPP_PUBLIC
DynamicMessage
get_complex_value(
const std::string & name,
rcl_allocator_t allocator = rcl_get_default_allocator());
RCLCPP_PUBLIC
DynamicMessage::SharedPtr
get_complex_value_shared(
rosidl_dynamic_typesupport_member_id_t id,
rcl_allocator_t allocator = rcl_get_default_allocator());
RCLCPP_PUBLIC
DynamicMessage::SharedPtr
get_complex_value_shared(
const std::string & name,
rcl_allocator_t allocator = rcl_get_default_allocator());
RCLCPP_PUBLIC
void
set_complex_value(rosidl_dynamic_typesupport_member_id_t id, DynamicMessage & value);
RCLCPP_PUBLIC
void
set_complex_value(const std::string & name, DynamicMessage & value);
RCLCPP_PUBLIC
rosidl_dynamic_typesupport_member_id_t
insert_complex_value_copy(const DynamicMessage & value);
RCLCPP_PUBLIC
rosidl_dynamic_typesupport_member_id_t
insert_complex_value(DynamicMessage & value);
protected:
// NOTE(methylDragon):
// This is just here to extend the lifetime of the serialization support
@@ -62,6 +418,12 @@ private:
RCLCPP_PUBLIC
DynamicMessage();
RCLCPP_PUBLIC
bool
match_serialization_support_(
const DynamicSerializationSupport & serialization_support,
const rosidl_dynamic_typesupport_dynamic_data_t & dynamic_data);
};
} // namespace dynamic_typesupport

View File

@@ -30,16 +30,143 @@ namespace rclcpp
namespace dynamic_typesupport
{
class DynamicMessage;
class DynamicMessageTypeBuilder;
/// Utility wrapper class for `rosidl_dynamic_typesupport_dynamic_type_t`
/// STUBBED OUT
/**
* This class:
* - Exposes getter methods for the struct
* - Exposes the underlying serialization support API
*
* Ownership:
* - This class borrows the `rosidl_dynamic_typesupport_serialization_support_t` stored in the
* passed `DynamicSerializationSupport`.
* So it cannot outlive the `DynamicSerializationSupport`.
* - The `DynamicSerializationSupport`'s `rosidl_dynamic_typesupport_serialization_support_t`
* pointer must point to the same location in memory as the stored raw pointer!
*
* This class is meant to map to the lower level `rosidl_dynamic_typesupport_dynamic_type_t`,
* which can be constructed via `DynamicMessageTypeBuilder`, which maps to
* `rosidl_dynamic_typesupport_dynamic_type_builder_t`.
*
* The usual method of obtaining a `DynamicMessageType` is through construction of
* `rosidl_message_type_support_t` via `rcl_dynamic_message_type_support_handle_create()`, then
* taking ownership of its contents. But `DynamicMessageTypeBuilder` can also be used to obtain
* `DynamicMessageType` by constructing it bottom-up instead, since it exposes the lower_level
* rosidl methods.
*/
class DynamicMessageType : public std::enable_shared_from_this<DynamicMessageType>
{
public:
RCLCPP_SMART_PTR_ALIASES_ONLY(DynamicMessageType)
// CONSTRUCTION ==================================================================================
// Most constructors require a passed in `DynamicSerializationSupport::SharedPtr`, to extend the
// lifetime of the serialization support (if the constructor cannot otherwise get it from args).
//
// In cases where a dynamic type pointer is passed, the serialization support composed by
// the type should be the exact same object managed by the `DynamicSerializationSupport`,
// otherwise the lifetime management will not work properly.
/// Construct a new `DynamicMessageType` with the provided dynamic type builder,
/// using its allocator
RCLCPP_PUBLIC
explicit DynamicMessageType(std::shared_ptr<DynamicMessageTypeBuilder> dynamic_type_builder);
/// Construct a new `DynamicMessageType` with the provided dynamic type builder and allocator
RCLCPP_PUBLIC
DynamicMessageType(
std::shared_ptr<DynamicMessageTypeBuilder> dynamic_type_builder,
rcl_allocator_t allocator);
/// Assume ownership of struct
RCLCPP_PUBLIC
DynamicMessageType(
DynamicSerializationSupport::SharedPtr serialization_support,
rosidl_dynamic_typesupport_dynamic_type_t && rosidl_dynamic_type);
/// From description
RCLCPP_PUBLIC
DynamicMessageType(
DynamicSerializationSupport::SharedPtr serialization_support,
const rosidl_runtime_c__type_description__TypeDescription & description,
rcl_allocator_t allocator = rcl_get_default_allocator());
/// Move constructor
RCLCPP_PUBLIC
DynamicMessageType(DynamicMessageType && other) noexcept;
/// Move assignment
RCLCPP_PUBLIC
DynamicMessageType & operator=(DynamicMessageType && other) noexcept;
RCLCPP_PUBLIC
virtual ~DynamicMessageType();
/// Swaps the serialization support if `serialization_support` is populated
/**
* The user can call this with another description to reconfigure the type without changing the
* serialization support
*/
RCLCPP_PUBLIC
void
init_from_description(
const rosidl_runtime_c__type_description__TypeDescription & description,
rcl_allocator_t allocator = rcl_get_default_allocator(),
DynamicSerializationSupport::SharedPtr serialization_support = nullptr);
// GETTERS =======================================================================================
RCLCPP_PUBLIC
const std::string
get_serialization_library_identifier() const;
RCLCPP_PUBLIC
const std::string
get_name() const;
RCLCPP_PUBLIC
size_t
get_member_count() const;
RCLCPP_PUBLIC
rosidl_dynamic_typesupport_dynamic_type_t &
get_rosidl_dynamic_type();
RCLCPP_PUBLIC
const rosidl_dynamic_typesupport_dynamic_type_t &
get_rosidl_dynamic_type() const;
RCLCPP_PUBLIC
DynamicSerializationSupport::SharedPtr
get_shared_dynamic_serialization_support();
RCLCPP_PUBLIC
DynamicSerializationSupport::ConstSharedPtr
get_shared_dynamic_serialization_support() const;
// METHODS =======================================================================================
RCLCPP_PUBLIC
DynamicMessageType
clone(rcl_allocator_t allocator = rcl_get_default_allocator());
RCLCPP_PUBLIC
DynamicMessageType::SharedPtr
clone_shared(rcl_allocator_t allocator = rcl_get_default_allocator());
RCLCPP_PUBLIC
bool
equals(const DynamicMessageType & other) const;
RCLCPP_PUBLIC
DynamicMessage
build_dynamic_message(rcl_allocator_t allocator = rcl_get_default_allocator());
RCLCPP_PUBLIC
std::shared_ptr<DynamicMessage>
build_dynamic_message_shared(rcl_allocator_t allocator = rcl_get_default_allocator());
protected:
// NOTE(methylDragon):
// This is just here to extend the lifetime of the serialization support
@@ -56,6 +183,12 @@ private:
RCLCPP_PUBLIC
DynamicMessageType();
RCLCPP_PUBLIC
bool
match_serialization_support_(
const DynamicSerializationSupport & serialization_support,
const rosidl_dynamic_typesupport_dynamic_type_t & rosidl_dynamic_type);
};
} // namespace dynamic_typesupport

View File

@@ -30,16 +30,347 @@ namespace rclcpp
namespace dynamic_typesupport
{
class DynamicMessage;
class DynamicMessageType;
/// Utility wrapper class for `rosidl_dynamic_typesupport_dynamic_type_builder_t *`
/// STUBBED OUT
/**
* This class:
* - Manages the lifetime of the raw pointer.
* - Exposes getter methods to get the raw pointer and shared pointers
* - Exposes the underlying serialization support API
*
* Ownership:
* - This class borrows the rosidl_dynamic_typesupport_serialization_support_t stored in the passed
* `DynamicSerializationSupport`.
* So it cannot outlive the `DynamicSerializationSupport`.
* - The `DynamicSerializationSupport`'s `rosidl_dynamic_typesupport_serialization_support_t`
* pointer must point to the same location in memory as the stored raw pointer!
*
* This class is meant to map to rosidl_dynamic_typesupport_dynamic_type_builder_t, facilitating the
* construction of dynamic types bottom-up in the C++ layer.
*
* The usual method of obtaining a `DynamicMessageType` is through construction of
* `rosidl_message_type_support_t` via `rcl_dynamic_message_type_support_handle_create()`, then
* taking ownership of its contents.
* But `DynamicMessageTypeBuilder` can also be used to obtain `DynamicMessageType` by constructing
* it bottom-up instead, since it exposes the lower_level rosidl methods.
*/
class DynamicMessageTypeBuilder : public std::enable_shared_from_this<DynamicMessageTypeBuilder>
{
public:
RCLCPP_SMART_PTR_ALIASES_ONLY(DynamicMessageTypeBuilder)
// CONSTRUCTION ==================================================================================
// All constructors require a passed in `DynamicSerializationSupport::SharedPtr`, to extend the
// lifetime of the serialization support.
//
// In cases where a dynamic type builder pointer is passed, the serialization support composed by
// the builder should be the exact same object managed by the `DynamicSerializationSupport`,
// otherwise the lifetime management will not work properly.
/// Construct a new `DynamicMessageTypeBuilder` with the provided serialization support using its
/// allocator
RCLCPP_PUBLIC
DynamicMessageTypeBuilder(
DynamicSerializationSupport::SharedPtr serialization_support,
const std::string & name);
/// Construct a new `DynamicMessageTypeBuilder` with the provided serialization support and
/// allocator
RCLCPP_PUBLIC
DynamicMessageTypeBuilder(
DynamicSerializationSupport::SharedPtr serialization_support,
const std::string & name,
rcl_allocator_t allocator);
/// Assume ownership of struct
RCLCPP_PUBLIC
DynamicMessageTypeBuilder(
DynamicSerializationSupport::SharedPtr serialization_support,
rosidl_dynamic_typesupport_dynamic_type_builder_t && dynamic_type_builder);
/// From description
RCLCPP_PUBLIC
DynamicMessageTypeBuilder(
DynamicSerializationSupport::SharedPtr serialization_support,
const rosidl_runtime_c__type_description__TypeDescription & description,
rcl_allocator_t allocator = rcl_get_default_allocator());
/// Move constructor
RCLCPP_PUBLIC
DynamicMessageTypeBuilder(DynamicMessageTypeBuilder && other) noexcept;
/// Move assignment
RCLCPP_PUBLIC
DynamicMessageTypeBuilder & operator=(DynamicMessageTypeBuilder && other) noexcept;
RCLCPP_PUBLIC
virtual ~DynamicMessageTypeBuilder();
/// Swaps the serialization support if serialization_support is populated
/**
* The user can call this with another description to reconfigure the type without changing the
* serialization support
*/
RCLCPP_PUBLIC
void
init_from_description(
const rosidl_runtime_c__type_description__TypeDescription & description,
rcl_allocator_t allocator = rcl_get_default_allocator(),
DynamicSerializationSupport::SharedPtr serialization_support = nullptr);
// GETTERS =======================================================================================
RCLCPP_PUBLIC
const std::string
get_serialization_library_identifier() const;
RCLCPP_PUBLIC
const std::string
get_name() const;
RCLCPP_PUBLIC
rosidl_dynamic_typesupport_dynamic_type_builder_t &
get_rosidl_dynamic_type_builder();
RCLCPP_PUBLIC
const rosidl_dynamic_typesupport_dynamic_type_builder_t &
get_rosidl_dynamic_type_builder() const;
RCLCPP_PUBLIC
DynamicSerializationSupport::SharedPtr
get_shared_dynamic_serialization_support();
RCLCPP_PUBLIC
DynamicSerializationSupport::ConstSharedPtr
get_shared_dynamic_serialization_support() const;
// METHODS =======================================================================================
RCLCPP_PUBLIC
void
set_name(const std::string & name);
RCLCPP_PUBLIC
DynamicMessageTypeBuilder
clone(rcl_allocator_t allocator = rcl_get_default_allocator());
RCLCPP_PUBLIC
DynamicMessageTypeBuilder::SharedPtr
clone_shared(rcl_allocator_t allocator = rcl_get_default_allocator());
RCLCPP_PUBLIC
void
clear(rcl_allocator_t allocator = rcl_get_default_allocator());
RCLCPP_PUBLIC
DynamicMessage
build_dynamic_message(rcl_allocator_t allocator = rcl_get_default_allocator());
RCLCPP_PUBLIC
DynamicMessage::SharedPtr
build_dynamic_message_shared(rcl_allocator_t allocator = rcl_get_default_allocator());
RCLCPP_PUBLIC
DynamicMessageType
build_dynamic_message_type(rcl_allocator_t allocator = rcl_get_default_allocator());
RCLCPP_PUBLIC
DynamicMessageType::SharedPtr
build_dynamic_message_type_shared(rcl_allocator_t allocator = rcl_get_default_allocator());
// ADD MEMBERS TEMPLATES =========================================================================
/**
* Since we're in a ROS layer, these should support all ROS interface C++ types as found in:
* https://docs.ros.org/en/rolling/Concepts/About-ROS-Interfaces.html
*
* Explicitly:
* - Basic types: bool, byte, char
* - Float types: float, double
* - Int types: int8_t, int16_t, int32_t, int64_t
* - Unsigned int types: uint8_t, uint16_t, uint32_t, uint64_t
* - String types: std::string, std::u16string
*/
template<typename MemberT>
void
add_member(
rosidl_dynamic_typesupport_member_id_t id, const std::string & name,
const std::string & default_value = "");
template<typename MemberT>
void
add_array_member(
rosidl_dynamic_typesupport_member_id_t id, const std::string & name, size_t array_length,
const std::string & default_value = "");
template<typename MemberT>
void
add_unbounded_sequence_member(
rosidl_dynamic_typesupport_member_id_t id, const std::string & name,
const std::string & default_value = "");
template<typename MemberT>
void
add_bounded_sequence_member(
rosidl_dynamic_typesupport_member_id_t id, const std::string & name, size_t sequence_bound,
const std::string & default_value = "");
// ADD FIXED STRING MEMBERS ======================================================================
RCLCPP_PUBLIC
void
add_fixed_string_member(
rosidl_dynamic_typesupport_member_id_t id, const std::string & name, size_t string_length,
const std::string & default_value = "");
RCLCPP_PUBLIC
void
add_fixed_wstring_member(
rosidl_dynamic_typesupport_member_id_t id, const std::string & name, size_t wstring_length,
const std::string & default_value = "");
RCLCPP_PUBLIC
void
add_fixed_string_array_member(
rosidl_dynamic_typesupport_member_id_t id, const std::string & name,
size_t string_length, size_t array_length, const std::string & default_value = "");
RCLCPP_PUBLIC
void
add_fixed_wstring_array_member(
rosidl_dynamic_typesupport_member_id_t id, const std::string & name,
size_t wstring_length, size_t array_length, const std::string & default_value = "");
RCLCPP_PUBLIC
void
add_fixed_string_unbounded_sequence_member(
rosidl_dynamic_typesupport_member_id_t id, const std::string & name, size_t string_length,
const std::string & default_value = "");
RCLCPP_PUBLIC
void
add_fixed_wstring_unbounded_sequence_member(
rosidl_dynamic_typesupport_member_id_t id, const std::string & name, size_t wstring_length,
const std::string & default_value = "");
RCLCPP_PUBLIC
void
add_fixed_string_bounded_sequence_member(
rosidl_dynamic_typesupport_member_id_t id, const std::string & name,
size_t string_length, size_t sequence_bound, const std::string & default_value = "");
RCLCPP_PUBLIC
void
add_fixed_wstring_bounded_sequence_member(
rosidl_dynamic_typesupport_member_id_t id, const std::string & name,
size_t wstring_length, size_t sequence_bound, const std::string & default_value = "");
// ADD BOUNDED STRING MEMBERS ====================================================================
RCLCPP_PUBLIC
void
add_bounded_string_member(
rosidl_dynamic_typesupport_member_id_t id, const std::string & name, size_t string_bound,
const std::string & default_value = "");
RCLCPP_PUBLIC
void
add_bounded_wstring_member(
rosidl_dynamic_typesupport_member_id_t id, const std::string & name, size_t wstring_bound,
const std::string & default_value = "");
RCLCPP_PUBLIC
void
add_bounded_string_array_member(
rosidl_dynamic_typesupport_member_id_t id, const std::string & name,
size_t string_bound, size_t array_length, const std::string & default_value = "");
RCLCPP_PUBLIC
void
add_bounded_wstring_array_member(
rosidl_dynamic_typesupport_member_id_t id, const std::string & name,
size_t wstring_bound, size_t array_length, const std::string & default_value = "");
RCLCPP_PUBLIC
void
add_bounded_string_unbounded_sequence_member(
rosidl_dynamic_typesupport_member_id_t id, const std::string & name, size_t string_bound,
const std::string & default_value = "");
RCLCPP_PUBLIC
void
add_bounded_wstring_unbounded_sequence_member(
rosidl_dynamic_typesupport_member_id_t id, const std::string & name, size_t wstring_bound,
const std::string & default_value = "");
RCLCPP_PUBLIC
void
add_bounded_string_bounded_sequence_member(
rosidl_dynamic_typesupport_member_id_t id, const std::string & name,
size_t string_bound, size_t sequence_bound, const std::string & default_value = "");
RCLCPP_PUBLIC
void
add_bounded_wstring_bounded_sequence_member(
rosidl_dynamic_typesupport_member_id_t id, const std::string & name,
size_t wstring_bound, size_t sequence_bound, const std::string & default_value = "");
// ADD NESTED MEMBERS ============================================================================
RCLCPP_PUBLIC
void
add_complex_member(
rosidl_dynamic_typesupport_member_id_t id, const std::string & name,
DynamicMessageType & nested_type, const std::string & default_value = "");
RCLCPP_PUBLIC
void
add_complex_array_member(
rosidl_dynamic_typesupport_member_id_t id, const std::string & name,
DynamicMessageType & nested_type, size_t array_length, const std::string & default_value = "");
RCLCPP_PUBLIC
void
add_complex_unbounded_sequence_member(
rosidl_dynamic_typesupport_member_id_t id, const std::string & name,
DynamicMessageType & nested_type, const std::string & default_value = "");
RCLCPP_PUBLIC
void
add_complex_bounded_sequence_member(
rosidl_dynamic_typesupport_member_id_t id, const std::string & name,
DynamicMessageType & nested_type, size_t sequence_bound,
const std::string & default_value = "");
RCLCPP_PUBLIC
void
add_complex_member_builder(
rosidl_dynamic_typesupport_member_id_t id, const std::string & name,
DynamicMessageTypeBuilder & nested_type_builder, const std::string & default_value = "");
RCLCPP_PUBLIC
void
add_complex_array_member_builder(
rosidl_dynamic_typesupport_member_id_t id, const std::string & name,
DynamicMessageTypeBuilder & nested_type_builder, size_t array_length,
const std::string & default_value = "");
RCLCPP_PUBLIC
void
add_complex_unbounded_sequence_member_builder(
rosidl_dynamic_typesupport_member_id_t id, const std::string & name,
DynamicMessageTypeBuilder & nested_type_builder, const std::string & default_value = "");
RCLCPP_PUBLIC
void
add_complex_bounded_sequence_member_builder(
rosidl_dynamic_typesupport_member_id_t id, const std::string & name,
DynamicMessageTypeBuilder & nested_type_builder, size_t sequence_bound,
const std::string & default_value = "");
protected:
// NOTE(methylDragon):
// This is just here to extend the lifetime of the serialization support
@@ -56,6 +387,19 @@ private:
RCLCPP_PUBLIC
DynamicMessageTypeBuilder();
RCLCPP_PUBLIC
void
init_from_serialization_support_(
DynamicSerializationSupport::SharedPtr serialization_support,
const std::string & name,
rcl_allocator_t allocator);
RCLCPP_PUBLIC
bool
match_serialization_support_(
const DynamicSerializationSupport & serialization_support,
const rosidl_dynamic_typesupport_dynamic_type_builder_t & dynamic_type_builder);
};
} // namespace dynamic_typesupport

View File

@@ -38,27 +38,126 @@ namespace dynamic_typesupport
{
/// Utility wrapper class for `rosidl_message_type_support_t` containing managed
/// STUBBED OUT
/// instances of the typesupport handle impl.
/**
*
* NOTE: This class is the recommended way to obtain the dynamic message type
* support struct, instead of `rcl_dynamic_message_type_support_handle_init()`,
* because this class will manage the lifetimes for you.
*
* Do NOT call rcl_dynamic_message_type_support_handle_fini
*
* This class:
* - Exposes getter methods for the struct
* - Stores shared pointers to wrapper classes that expose the underlying
* serialization support API
*
* Ownership:
* - This class, similarly to the `rosidl_dynamic_typesupport_serialization_support_t`, must outlive
* all downstream usages of the serialization support.
*/
class DynamicMessageTypeSupport : public std::enable_shared_from_this<DynamicMessageTypeSupport>
{
public:
RCLCPP_SMART_PTR_ALIASES_ONLY(DynamicMessageTypeSupport)
// CONSTRUCTION ==================================================================================
/// From description
/// Does NOT take ownership of the description (copies instead.)
/// Constructs type support top-down (calling `rcl_dynamic_message_type_support_handle_create()`)
RCLCPP_PUBLIC
DynamicMessageTypeSupport(
const rosidl_runtime_c__type_description__TypeDescription & description,
const std::string & serialization_library_name = "",
rcl_allocator_t allocator = rcl_get_default_allocator());
/// From description, for provided serialization support
/// Does NOT take ownership of the description (copies instead.)
/// Constructs type support top-down (calling `rosidl_dynamic_message_type_support_handle_init()`)
RCLCPP_PUBLIC
DynamicMessageTypeSupport(
DynamicSerializationSupport::SharedPtr serialization_support,
const rosidl_runtime_c__type_description__TypeDescription & description,
rcl_allocator_t allocator = rcl_get_default_allocator());
/// Assume ownership of managed types
/// Does NOT take ownership of the description (copies instead.)
///
/// The serialization support used to construct all managed SharedPtrs must match.
/// The structure of the provided `description` must match the `dynamic_message_type`
/// The structure of the provided `dynamic_message_type` must match the `dynamic_message
///
/// In this case, the user would have constructed the type support bototm-up (by creating the
/// respective dynamic members.)
RCLCPP_PUBLIC
DynamicMessageTypeSupport(
DynamicSerializationSupport::SharedPtr serialization_support,
DynamicMessageType::SharedPtr dynamic_message_type,
DynamicMessage::SharedPtr dynamic_message,
const rosidl_runtime_c__type_description__TypeDescription & description,
rcl_allocator_t allocator = rcl_get_default_allocator());
RCLCPP_PUBLIC
virtual ~DynamicMessageTypeSupport();
// GETTERS =======================================================================================
RCLCPP_PUBLIC
const std::string
get_serialization_library_identifier() const;
RCLCPP_PUBLIC
const rosidl_message_type_support_t &
get_const_rosidl_message_type_support();
RCLCPP_PUBLIC
const rosidl_message_type_support_t &
get_const_rosidl_message_type_support() const;
RCLCPP_PUBLIC
const rosidl_runtime_c__type_description__TypeDescription &
get_rosidl_runtime_c_type_description() const;
RCLCPP_PUBLIC
DynamicSerializationSupport::SharedPtr
get_shared_dynamic_serialization_support();
RCLCPP_PUBLIC
DynamicSerializationSupport::ConstSharedPtr
get_shared_dynamic_serialization_support() const;
RCLCPP_PUBLIC
DynamicMessageType::SharedPtr
get_shared_dynamic_message_type();
RCLCPP_PUBLIC
DynamicMessageType::ConstSharedPtr
get_shared_dynamic_message_type() const;
RCLCPP_PUBLIC
DynamicMessage::SharedPtr
get_shared_dynamic_message();
RCLCPP_PUBLIC
DynamicMessage::ConstSharedPtr
get_shared_dynamic_message() const;
protected:
RCLCPP_DISABLE_COPY(DynamicMessageTypeSupport)
RCLCPP_PUBLIC
rosidl_message_type_support_t &
get_rosidl_message_type_support();
private:
RCLCPP_PUBLIC
DynamicMessageTypeSupport();
DynamicSerializationSupport::SharedPtr serialization_support_;
DynamicMessageType::SharedPtr dynamic_message_type_;
DynamicMessage::SharedPtr dynamic_message_;
rosidl_message_type_support_t rosidl_message_type_support_;
private:
RCLCPP_DISABLE_COPY(DynamicMessageTypeSupport)
RCLCPP_PUBLIC
DynamicMessageTypeSupport();
};
} // namespace dynamic_typesupport

View File

@@ -31,27 +31,65 @@ namespace dynamic_typesupport
{
/// Utility wrapper class for rosidl_dynamic_typesupport_serialization_support_t
/**
* This class:
* - Exposes getter methods for the struct
* - Exposes the underlying serialization support API
*
* Ownership:
* - This class, similarly to the rosidl_dynamic_typesupport_serialization_support_t, must outlive
* all downstream usages of the serialization support.
*/
class DynamicSerializationSupport : public std::enable_shared_from_this<DynamicSerializationSupport>
{
public:
RCLCPP_SMART_PTR_ALIASES_ONLY(DynamicSerializationSupport)
// CONSTRUCTION ==================================================================================
RCLCPP_PUBLIC
explicit DynamicSerializationSupport(rcl_allocator_t allocator = rcl_get_default_allocator());
/// Get the rmw middleware implementation specific serialization support (configured by name)
RCLCPP_PUBLIC
DynamicSerializationSupport(
const std::string & serialization_library_name,
rcl_allocator_t allocator = rcl_get_default_allocator());
/// Assume ownership of struct
RCLCPP_PUBLIC
explicit DynamicSerializationSupport(
rosidl_dynamic_typesupport_serialization_support_t && rosidl_serialization_support);
/// Move constructor
RCLCPP_PUBLIC
DynamicSerializationSupport(DynamicSerializationSupport && other) noexcept;
/// Move assignment
RCLCPP_PUBLIC
DynamicSerializationSupport & operator=(DynamicSerializationSupport && other) noexcept;
RCLCPP_PUBLIC
virtual ~DynamicSerializationSupport();
// GETTERS =======================================================================================
RCLCPP_PUBLIC
const std::string
get_serialization_library_identifier() const;
RCLCPP_PUBLIC
rosidl_dynamic_typesupport_serialization_support_t &
get_rosidl_serialization_support();
RCLCPP_PUBLIC
const rosidl_dynamic_typesupport_serialization_support_t &
get_rosidl_serialization_support() const;
protected:
rosidl_dynamic_typesupport_serialization_support_t rosidl_serialization_support_;
RCLCPP_DISABLE_COPY(DynamicSerializationSupport)
private:
RCLCPP_DISABLE_COPY(DynamicSerializationSupport)
rosidl_dynamic_typesupport_serialization_support_t rosidl_serialization_support_;
};
} // namespace dynamic_typesupport

View File

@@ -19,7 +19,6 @@
#include <cassert>
#include <chrono>
#include <cstdlib>
#include <deque>
#include <iostream>
#include <list>
#include <map>
@@ -30,27 +29,28 @@
#include "rcl/guard_condition.h"
#include "rcl/wait.h"
#include "rclcpp/executors/executor_notify_waitable.hpp"
#include "rcpputils/scope_exit.hpp"
#include "rclcpp/context.hpp"
#include "rclcpp/contexts/default_context.hpp"
#include "rclcpp/guard_condition.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/memory_strategies.hpp"
#include "rclcpp/memory_strategy.hpp"
#include "rclcpp/node_interfaces/node_base_interface.hpp"
#include "rclcpp/utilities.hpp"
#include "rclcpp/visibility_control.hpp"
#include "rclcpp/wait_set.hpp"
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.
class Node;
class ExecutorImplementation;
/// Coordinate the order and timing of available communication tasks.
/**
@@ -402,6 +402,17 @@ public:
void
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.
/**
* This function can be called asynchronously from any thread.
@@ -486,11 +497,6 @@ protected:
static void
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.
/**
* Builds a set of waitable entities, which are passed to the middleware.
@@ -502,6 +508,62 @@ protected:
void
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.
/**
* \param[out] any_executable populated union structure of ready executable
@@ -512,6 +574,33 @@ protected:
bool
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.
/**
* If an executable is ready, it will return immediately, otherwise
@@ -529,17 +618,39 @@ protected:
AnyExecutable & any_executable,
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.
std::atomic_bool spinning;
/// Guard condition for signaling the rmw layer to wake up for special events.
std::shared_ptr<rclcpp::GuardCondition> interrupt_guard_condition_;
rclcpp::GuardCondition interrupt_guard_condition_;
/// Guard condition for signaling the rmw layer to wake up for system shutdown.
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_;
/// 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.
std::shared_ptr<rclcpp::Context> context_;
@@ -549,39 +660,42 @@ protected:
virtual void
spin_once_impl(std::chrono::nanoseconds timeout);
/// Waitable containing guard conditions controlling the executor flow.
/**
* This waitable contains the interrupt and shutdown guard condition, as well
* 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::node_interfaces::NodeBaseInterface::WeakPtr,
const rclcpp::GuardCondition *,
std::owner_less<rclcpp::node_interfaces::NodeBaseInterface::WeakPtr>>
WeakNodesToGuardConditionsMap;
std::atomic_bool entities_need_rebuild_;
typedef std::map<rclcpp::CallbackGroup::WeakPtr,
const rclcpp::GuardCondition *,
std::owner_less<rclcpp::CallbackGroup::WeakPtr>>
WeakCallbackGroupsToGuardConditionsMap;
/// Collector used to associate executable entities from nodes and guard conditions
rclcpp::executors::ExecutorEntitiesCollector collector_;
/// maps nodes to guard conditions
WeakNodesToGuardConditionsMap
weak_nodes_to_guard_conditions_ RCPPUTILS_TSA_GUARDED_BY(mutex_);
/// Waitset to be waited on.
rclcpp::WaitSet wait_set_ RCPPUTILS_TSA_GUARDED_BY(mutex_);
/// maps callback groups to guard conditions
WeakCallbackGroupsToGuardConditionsMap
weak_groups_to_guard_conditions_ RCPPUTILS_TSA_GUARDED_BY(mutex_);
/// Hold the current state of the collection being waited on by the waitset
rclcpp::executors::ExecutorEntitiesCollection current_collection_ RCPPUTILS_TSA_GUARDED_BY(
mutex_);
/// maps callback groups associated to nodes
WeakCallbackGroupsToNodesMap
weak_groups_associated_with_executor_to_nodes_ RCPPUTILS_TSA_GUARDED_BY(mutex_);
/// Hold the current state of the notify waitable being waited on by the waitset
std::shared_ptr<rclcpp::executors::ExecutorNotifyWaitable> current_notify_waitable_
RCPPUTILS_TSA_GUARDED_BY(mutex_);
/// maps callback groups to nodes associated with executor
WeakCallbackGroupsToNodesMap
weak_groups_to_nodes_associated_with_executor_ RCPPUTILS_TSA_GUARDED_BY(mutex_);
/// Hold the list of executables currently available to be executed.
std::deque<rclcpp::AnyExecutable> ready_executables_ RCPPUTILS_TSA_GUARDED_BY(mutex_);
/// maps all callback groups to nodes
WeakCallbackGroupsToNodesMap
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
rclcpp::OnShutdownCallbackHandle shutdown_callback_handle_;
/// Pointer to implementation
std::unique_ptr<ExecutorImplementation> impl_;
};
} // namespace rclcpp

View File

@@ -21,7 +21,6 @@
#include "rclcpp/executors/multi_threaded_executor.hpp"
#include "rclcpp/executors/single_threaded_executor.hpp"
#include "rclcpp/executors/static_single_threaded_executor.hpp"
#include "rclcpp/experimental/executors/events_executor/events_executor.hpp"
#include "rclcpp/node.hpp"
#include "rclcpp/utilities.hpp"
#include "rclcpp/visibility_control.hpp"

View File

@@ -1,213 +0,0 @@
// Copyright 2023 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__EXECUTOR_ENTITIES_COLLECTION_HPP_
#define RCLCPP__EXECUTORS__EXECUTOR_ENTITIES_COLLECTION_HPP_
#include <deque>
#include <functional>
#include <unordered_map>
#include <vector>
#include <rclcpp/any_executable.hpp>
#include <rclcpp/node_interfaces/node_base.hpp>
#include <rclcpp/callback_group.hpp>
#include <rclcpp/executors/executor_notify_waitable.hpp>
#include <rclcpp/visibility_control.hpp>
#include <rclcpp/wait_result.hpp>
#include <rclcpp/wait_set.hpp>
namespace rclcpp
{
namespace executors
{
/// Structure to represent a single entity's entry in a collection
template<typename EntityValueType>
struct CollectionEntry
{
/// Weak pointer to entity type
using EntityWeakPtr = typename EntityValueType::WeakPtr;
/// Shared pointer to entity type
using EntitySharedPtr = typename EntityValueType::SharedPtr;
/// The entity
EntityWeakPtr entity;
/// If relevant, the entity's corresponding callback_group
rclcpp::CallbackGroup::WeakPtr callback_group;
};
/// Update a collection based on another collection
/*
* Iterates update_from and update_to to see which entities have been added/removed between
* the two collections.
*
* For each new entry (in update_from, but not in update_to),
* add the entity and fire the on_added callback
* For each removed entry (in update_to, but not in update_from),
* remove the entity and fire the on_removed callback.
*
* \param[in] update_from The collection representing the next iteration's state
* \param[inout] update_to The collection representing the current iteration's state
* \param[in] on_added Callback fired when a new entity is detected
* \param[in] on_removed Callback fired when an entity is removed
*/
template<typename CollectionType>
void update_entities(
const CollectionType & update_from,
CollectionType & update_to,
std::function<void(const typename CollectionType::EntitySharedPtr &)> on_added,
std::function<void(const typename CollectionType::EntitySharedPtr &)> on_removed
)
{
for (auto it = update_to.begin(); it != update_to.end(); ) {
if (update_from.count(it->first) == 0) {
auto entity = it->second.entity.lock();
if (entity) {
on_removed(entity);
}
it = update_to.erase(it);
} else {
++it;
}
}
for (auto it = update_from.begin(); it != update_from.end(); ++it) {
if (update_to.count(it->first) == 0) {
auto entity = it->second.entity.lock();
if (entity) {
on_added(entity);
}
update_to.insert(*it);
}
}
}
/// A collection of entities, indexed by their corresponding handles
template<typename EntityKeyType, typename EntityValueType>
class EntityCollection
: public std::unordered_map<const EntityKeyType *, CollectionEntry<EntityValueType>>
{
public:
/// Key type of the map
using Key = const EntityKeyType *;
/// Weak pointer to entity type
using EntityWeakPtr = typename EntityValueType::WeakPtr;
/// Shared pointer to entity type
using EntitySharedPtr = typename EntityValueType::SharedPtr;
/// Update this collection based on the contents of another collection
/**
* Update the internal state of this collection, firing callbacks when entities have been
* added or removed.
*
* \param[in] other Collection to compare to
* \param[in] on_added Callback for when entities have been added
* \param[in] on_removed Callback for when entities have been removed
*/
void update(
const EntityCollection<EntityKeyType, EntityValueType> & other,
std::function<void(const EntitySharedPtr &)> on_added,
std::function<void(const EntitySharedPtr &)> on_removed)
{
update_entities(other, *this, on_added, on_removed);
}
};
/// Represent the total set of entities for a single executor
/**
* This allows the entities to be stored from ExecutorEntitiesCollector.
* The structure also makes in convenient to re-evaluate when entities have been added or removed.
*/
struct ExecutorEntitiesCollection
{
/// Collection type for timer entities
using TimerCollection = EntityCollection<rcl_timer_t, rclcpp::TimerBase>;
/// Collection type for subscription entities
using SubscriptionCollection = EntityCollection<rcl_subscription_t, rclcpp::SubscriptionBase>;
/// Collection type for client entities
using ClientCollection = EntityCollection<rcl_client_t, rclcpp::ClientBase>;
/// Collection type for service entities
using ServiceCollection = EntityCollection<rcl_service_t, rclcpp::ServiceBase>;
/// Collection type for waitable entities
using WaitableCollection = EntityCollection<rclcpp::Waitable, rclcpp::Waitable>;
/// Collection type for guard condition entities
using GuardConditionCollection = EntityCollection<rcl_guard_condition_t, rclcpp::GuardCondition>;
/// Collection of timers currently in use by the executor.
TimerCollection timers;
/// Collection of subscriptions currently in use by the executor.
SubscriptionCollection subscriptions;
/// Collection of clients currently in use by the executor.
ClientCollection clients;
/// Collection of services currently in use by the executor.
ServiceCollection services;
/// Collection of guard conditions currently in use by the executor.
GuardConditionCollection guard_conditions;
/// Collection of waitables currently in use by the executor.
WaitableCollection waitables;
/// Check if the entities collection is empty
/**
* \return true if all member collections are empty, false otherwise
*/
bool empty() const;
/// Clear the entities collection
void clear();
};
/// Build an entities collection from callback groups
/**
* Iterates a list of callback groups and adds entities from each valid group
*
* \param[in] callback_groups List of callback groups to check for entities
* \param[inout] colletion Entities collection to populate with found entities
*/
void
build_entities_collection(
const std::vector<rclcpp::CallbackGroup::WeakPtr> & callback_groups,
ExecutorEntitiesCollection & collection);
/// Build a queue of executables ready to be executed
/**
* Iterates a list of entities and adds them to a queue if they are ready.
*
* \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[inout] queue of executables to append new ready executables to
* \return number of new ready executables
*/
size_t
ready_executables(
const ExecutorEntitiesCollection & collection,
rclcpp::WaitResult<rclcpp::WaitSet> & wait_result,
std::deque<rclcpp::AnyExecutable> & executables
);
} // namespace executors
} // namespace rclcpp
#endif // RCLCPP__EXECUTORS__EXECUTOR_ENTITIES_COLLECTION_HPP_

View File

@@ -1,270 +0,0 @@
// Copyright 2023 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__EXECUTOR_ENTITIES_COLLECTOR_HPP_
#define RCLCPP__EXECUTORS__EXECUTOR_ENTITIES_COLLECTOR_HPP_
#include <map>
#include <memory>
#include <mutex>
#include <set>
#include <vector>
#include "rcpputils/thread_safety_annotations.hpp"
#include <rclcpp/any_executable.hpp>
#include <rclcpp/node_interfaces/node_base.hpp>
#include <rclcpp/callback_group.hpp>
#include <rclcpp/executors/executor_notify_waitable.hpp>
#include <rclcpp/visibility_control.hpp>
#include <rclcpp/wait_set.hpp>
#include <rclcpp/wait_result.hpp>
namespace rclcpp
{
namespace executors
{
/// Class to monitor a set of nodes and callback groups for changes in entity membership
/**
* This is to be used with an executor to track the membership of various nodes, groups,
* and entities (timers, subscriptions, clients, services, etc) and report status to the
* executor.
*
* In general, users will add either nodes or callback groups to an executor.
* Each node may have callback groups that are automatically associated with executors,
* or callback groups that must be manually associated with an executor.
*
* This object tracks both types of callback groups as well as nodes that have been
* previously added to the executor.
* When a new callback group is added/removed or new entities are added/removed, the
* corresponding node or callback group will signal this to the executor so that the
* entity collection may be rebuilt according to that executor's implementation.
*
*/
class ExecutorEntitiesCollector
{
public:
/// Constructor
/**
* \param[in] notify_waitable Waitable that is used to signal to the executor
* when nodes or callback groups have been added or removed.
*/
RCLCPP_PUBLIC
explicit ExecutorEntitiesCollector(
std::shared_ptr<ExecutorNotifyWaitable> notify_waitable);
/// Destructor
RCLCPP_PUBLIC
~ExecutorEntitiesCollector();
/// Indicate if the entities collector has pending additions or removals.
/**
* \return true if there are pending additions or removals
*/
bool has_pending() const;
/// Add a node to the entity collector
/**
* \param[in] node_ptr a shared pointer that points to a node base interface
* \throw std::runtime_error if the node is associated with an executor
*/
RCLCPP_PUBLIC
void
add_node(rclcpp::node_interfaces::NodeBaseInterface::SharedPtr node_ptr);
/// Remove a node from the entity collector
/**
* \param[in] node_ptr a shared pointer that points to a node base interface
* \throw std::runtime_error if the node is associated with an executor
* \throw std::runtime_error if the node is associated with this executor
*/
RCLCPP_PUBLIC
void
remove_node(rclcpp::node_interfaces::NodeBaseInterface::SharedPtr node_ptr);
/// Add a callback group to the entity collector
/**
* \param[in] group_ptr a shared pointer that points to a callback group
* \throw std::runtime_error if the callback_group is associated with an executor
*/
RCLCPP_PUBLIC
void
add_callback_group(rclcpp::CallbackGroup::SharedPtr group_ptr);
/// Remove a callback group from the entity collector
/**
* \param[in] group_ptr a shared pointer that points to a callback group
* \throw std::runtime_error if the callback_group is not associated with an executor
* \throw std::runtime_error if the callback_group is not associated with this executor
*/
RCLCPP_PUBLIC
void
remove_callback_group(rclcpp::CallbackGroup::SharedPtr group_ptr);
/// Get all callback groups known to this entity collector
/**
* This will include manually added and automatically added (node associated) groups
* \return vector of all callback groups
*/
RCLCPP_PUBLIC
std::vector<rclcpp::CallbackGroup::WeakPtr>
get_all_callback_groups() const;
/// Get manually-added callback groups known to this entity collector
/**
* This will include callback groups that have been added via add_callback_group
* \return vector of manually-added callback groups
*/
RCLCPP_PUBLIC
std::vector<rclcpp::CallbackGroup::WeakPtr>
get_manually_added_callback_groups() const;
/// Get automatically-added callback groups known to this entity collector
/**
* This will include callback groups that are associated with nodes added via add_node
* \return vector of automatically-added callback groups
*/
RCLCPP_PUBLIC
std::vector<rclcpp::CallbackGroup::WeakPtr>
get_automatically_added_callback_groups() const;
/// Update the underlying collections
/**
* This will prune nodes and callback groups that are no longer valid as well
* as add new callback groups from any associated nodes.
*/
RCLCPP_PUBLIC
void
update_collections();
protected:
using NodeCollection = std::set<
rclcpp::node_interfaces::NodeBaseInterface::WeakPtr,
std::owner_less<rclcpp::node_interfaces::NodeBaseInterface::WeakPtr>>;
using CallbackGroupCollection = std::set<
rclcpp::CallbackGroup::WeakPtr,
std::owner_less<rclcpp::CallbackGroup::WeakPtr>>;
using WeakNodesToGuardConditionsMap = std::map<
rclcpp::node_interfaces::NodeBaseInterface::WeakPtr,
rclcpp::GuardCondition::WeakPtr,
std::owner_less<rclcpp::node_interfaces::NodeBaseInterface::WeakPtr>>;
using WeakGroupsToGuardConditionsMap = std::map<
rclcpp::CallbackGroup::WeakPtr,
rclcpp::GuardCondition::WeakPtr,
std::owner_less<rclcpp::CallbackGroup::WeakPtr>>;
/// Implementation of removing a node from the collector.
/**
* This will disassociate the node from the collector and remove any
* automatically-added callback groups
*
* This takes and returns an iterator so it may be used as:
*
* it = remove_weak_node(it);
*
* \param[in] weak_node iterator to the weak node to be removed
* \return Valid updated iterator in the same collection
*/
RCLCPP_PUBLIC
NodeCollection::iterator
remove_weak_node(NodeCollection::iterator weak_node) RCPPUTILS_TSA_REQUIRES(mutex_);
/// Implementation of removing a callback group from the collector.
/**
* This will disassociate the callback group from the collector
*
* This takes and returns an iterator so it may be used as:
*
* it = remove_weak_callback_group(it);
*
* \param[in] weak_group_it iterator to the weak group to be removed
* \param[in] collection the collection to remove the group from
* (manually or automatically added)
* \return Valid updated iterator in the same collection
*/
RCLCPP_PUBLIC
CallbackGroupCollection::iterator
remove_weak_callback_group(
CallbackGroupCollection::iterator weak_group_it,
CallbackGroupCollection & collection) RCPPUTILS_TSA_REQUIRES(mutex_);
/// Implementation of adding a callback group
/**
* \param[in] group_ptr the group to add
* \param[in] collection the collection to add the group to
*/
RCLCPP_PUBLIC
void
add_callback_group_to_collection(
rclcpp::CallbackGroup::SharedPtr group_ptr,
CallbackGroupCollection & collection) RCPPUTILS_TSA_REQUIRES(mutex_);
/// Iterate over queued added/remove nodes and callback_groups
RCLCPP_PUBLIC
void
process_queues() RCPPUTILS_TSA_REQUIRES(mutex_);
/// Check a collection of nodes and add any new callback_groups that
/// are set to be automatically associated via the node.
RCLCPP_PUBLIC
void
add_automatically_associated_callback_groups(
const NodeCollection & nodes_to_check) RCPPUTILS_TSA_REQUIRES(mutex_);
/// Check all nodes and group for expired weak pointers and remove them.
RCLCPP_PUBLIC
void
prune_invalid_nodes_and_groups() RCPPUTILS_TSA_REQUIRES(mutex_);
/// mutex to protect collections and pending queues
mutable std::mutex mutex_;
/// Callback groups that were added via `add_callback_group`
CallbackGroupCollection manually_added_groups_ RCPPUTILS_TSA_GUARDED_BY(mutex_);
/// Callback groups that were added by their association with added nodes
CallbackGroupCollection automatically_added_groups_ RCPPUTILS_TSA_GUARDED_BY(mutex_);
/// nodes that are associated with the executor
NodeCollection weak_nodes_ RCPPUTILS_TSA_GUARDED_BY(mutex_);
/// Track guard conditions associated with added nodes
WeakNodesToGuardConditionsMap weak_nodes_to_guard_conditions_ RCPPUTILS_TSA_GUARDED_BY(mutex_);
/// Track guard conditions associated with added callback groups
WeakGroupsToGuardConditionsMap weak_groups_to_guard_conditions_ RCPPUTILS_TSA_GUARDED_BY(mutex_);
/// nodes that have been added since the last update.
NodeCollection pending_added_nodes_ RCPPUTILS_TSA_GUARDED_BY(mutex_);
/// nodes that have been removed since the last update.
NodeCollection pending_removed_nodes_ RCPPUTILS_TSA_GUARDED_BY(mutex_);
/// callback groups that have been added since the last update.
CallbackGroupCollection pending_manually_added_groups_ RCPPUTILS_TSA_GUARDED_BY(mutex_);
/// callback groups that have been removed since the last update.
CallbackGroupCollection pending_manually_removed_groups_ RCPPUTILS_TSA_GUARDED_BY(mutex_);
/// Waitable to add guard conditions to
std::shared_ptr<ExecutorNotifyWaitable> notify_waitable_;
};
} // namespace executors
} // namespace rclcpp
//
#endif // RCLCPP__EXECUTORS__EXECUTOR_ENTITIES_COLLECTOR_HPP_

View File

@@ -1,158 +0,0 @@
// Copyright 2023 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__EXECUTOR_NOTIFY_WAITABLE_HPP_
#define RCLCPP__EXECUTORS__EXECUTOR_NOTIFY_WAITABLE_HPP_
#include <functional>
#include <memory>
#include <mutex>
#include <set>
#include "rclcpp/guard_condition.hpp"
#include "rclcpp/waitable.hpp"
namespace rclcpp
{
namespace executors
{
/// Maintain a collection of guard conditions from associated nodes and callback groups
/// to signal to the executor when associated entities have changed.
class ExecutorNotifyWaitable : public rclcpp::Waitable
{
public:
RCLCPP_SMART_PTR_DEFINITIONS(ExecutorNotifyWaitable)
// Constructor
/**
* \param[in] on_execute_callback Callback to execute when one of the conditions
* of this waitable has signaled the wait_set.
*/
RCLCPP_PUBLIC
explicit ExecutorNotifyWaitable(std::function<void(void)> on_execute_callback = {});
// Destructor
RCLCPP_PUBLIC
~ExecutorNotifyWaitable() override = default;
RCLCPP_PUBLIC
ExecutorNotifyWaitable(const ExecutorNotifyWaitable & other);
RCLCPP_PUBLIC
ExecutorNotifyWaitable & operator=(const ExecutorNotifyWaitable & other);
/// Add conditions to the wait set
/**
* \param[inout] wait_set structure that conditions will be added to
*/
RCLCPP_PUBLIC
void
add_to_wait_set(rcl_wait_set_t * wait_set) override;
/// Check conditions against the wait set
/**
* \param[inout] wait_set structure that internal elements will be checked against.
* \return true if this waitable is ready to be executed, false otherwise.
*/
RCLCPP_PUBLIC
bool
is_ready(rcl_wait_set_t * wait_set) override;
/// Perform work associated with the waitable.
/**
* This will call the callback provided in the constructor.
* \param[in] data Data to be use for the execute, if available, else nullptr.
*/
RCLCPP_PUBLIC
void
execute(std::shared_ptr<void> & data) override;
/// Retrieve data to be used in the next execute call.
/**
* \return If available, data to be used, otherwise nullptr
*/
RCLCPP_PUBLIC
std::shared_ptr<void>
take_data() override;
/// Take the data from an entity ID so that it can be consumed with `execute`.
/**
* \param[in] id ID of the entity to take data from.
* \return If available, data to be used, otherwise nullptr
* \sa rclcpp::Waitable::take_data_by_entity_id
*/
RCLCPP_PUBLIC
std::shared_ptr<void>
take_data_by_entity_id(size_t id) override;
/// Set a callback to be called whenever the waitable becomes ready.
/**
* \param[in] callback callback to set
* \sa rclcpp::Waitable::set_on_ready_callback
*/
RCLCPP_PUBLIC
void
set_on_ready_callback(std::function<void(size_t, int)> callback) override;
/// Add a guard condition to be waited on.
/**
* \param[in] guard_condition The guard condition to add.
*/
RCLCPP_PUBLIC
void
add_guard_condition(rclcpp::GuardCondition::WeakPtr guard_condition);
/// Unset any callback registered via set_on_ready_callback.
/**
* \sa rclcpp::Waitable::clear_on_ready_callback
*/
RCLCPP_PUBLIC
void
clear_on_ready_callback() override;
/// Remove a guard condition from being waited on.
/**
* \param[in] weak_guard_condition The guard condition to remove.
*/
RCLCPP_PUBLIC
void
remove_guard_condition(rclcpp::GuardCondition::WeakPtr weak_guard_condition);
/// Get the number of ready guard_conditions
/**
* \return The number of guard_conditions associated with the Waitable.
*/
RCLCPP_PUBLIC
size_t
get_number_of_ready_guard_conditions() override;
private:
/// Callback to run when waitable executes
std::function<void(void)> execute_callback_;
std::mutex guard_condition_mutex_;
std::function<void(size_t)> on_ready_callback_;
/// The collection of guard conditions to be waited on.
std::set<rclcpp::GuardCondition::WeakPtr,
std::owner_less<rclcpp::GuardCondition::WeakPtr>> notify_guard_conditions_;
};
} // namespace executors
} // namespace rclcpp
#endif // RCLCPP__EXECUTORS__EXECUTOR_NOTIFY_WAITABLE_HPP_

View File

@@ -0,0 +1,357 @@
// 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_

View File

@@ -15,13 +15,24 @@
#ifndef RCLCPP__EXECUTORS__STATIC_SINGLE_THREADED_EXECUTOR_HPP_
#define RCLCPP__EXECUTORS__STATIC_SINGLE_THREADED_EXECUTOR_HPP_
#include <atomic>
#include <chrono>
#include <cassert>
#include <cstdlib>
#include <memory>
#include <vector>
#include <string>
#include "rmw/rmw.h"
#include "rclcpp/executor.hpp"
#include "rclcpp/executors/executor_entities_collection.hpp"
#include "rclcpp/executors/single_threaded_executor.hpp"
#include "rclcpp/executors/static_executor_entities_collector.hpp"
#include "rclcpp/experimental/executable_list.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
{
@@ -54,7 +65,7 @@ public:
explicit StaticSingleThreadedExecutor(
const rclcpp::ExecutorOptions & options = rclcpp::ExecutorOptions());
/// Default destructor.
/// Default destrcutor.
RCLCPP_PUBLIC
virtual ~StaticSingleThreadedExecutor();
@@ -105,20 +116,92 @@ public:
void
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:
/**
* @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.
* @return true if any executable was ready.
*/
RCLCPP_PUBLIC
bool
execute_ready_executables(
const rclcpp::executors::ExecutorEntitiesCollection & collection,
rclcpp::WaitResult<rclcpp::WaitSet> & wait_result,
bool spin_once);
execute_ready_executables(bool spin_once = false);
RCLCPP_PUBLIC
void
@@ -130,6 +213,8 @@ protected:
private:
RCLCPP_DISABLE_COPY(StaticSingleThreadedExecutor)
StaticExecutorEntitiesCollector::SharedPtr entities_collector_;
};
} // namespace executors

View File

@@ -24,7 +24,6 @@
#include "rclcpp/allocator/allocator_deleter.hpp"
#include "rclcpp/experimental/buffers/buffer_implementation_base.hpp"
#include "rclcpp/macros.hpp"
#include "tracetools/tracetools.h"
namespace rclcpp
{
@@ -95,10 +94,6 @@ public:
buffer_ = std::move(buffer_impl);
TRACEPOINT(
rclcpp_buffer_to_ipb,
static_cast<const void *>(buffer_.get()),
static_cast<const void *>(this));
if (!allocator) {
message_allocator_ = std::make_shared<MessageAlloc>();
} else {

View File

@@ -25,7 +25,6 @@
#include "rclcpp/logging.hpp"
#include "rclcpp/macros.hpp"
#include "rclcpp/visibility_control.hpp"
#include "tracetools/tracetools.h"
namespace rclcpp
{
@@ -52,7 +51,6 @@ public:
if (capacity == 0) {
throw std::invalid_argument("capacity must be a positive, non-zero value");
}
TRACEPOINT(rclcpp_construct_ring_buffer, static_cast<const void *>(this), capacity_);
}
virtual ~RingBufferImplementation() {}
@@ -69,12 +67,6 @@ public:
write_index_ = next_(write_index_);
ring_buffer_[write_index_] = std::move(request);
TRACEPOINT(
rclcpp_ring_buffer_enqueue,
static_cast<const void *>(this),
write_index_,
size_ + 1,
is_full_());
if (is_full_()) {
read_index_ = next_(read_index_);
@@ -98,11 +90,6 @@ public:
}
auto request = std::move(ring_buffer_[read_index_]);
TRACEPOINT(
rclcpp_ring_buffer_dequeue,
static_cast<const void *>(this),
read_index_,
size_ - 1);
read_index_ = next_(read_index_);
size_--;
@@ -148,10 +135,7 @@ public:
return is_full_();
}
void clear()
{
TRACEPOINT(rclcpp_ring_buffer_clear, static_cast<const void *>(this));
}
void clear() {}
private:
/// Get the next index value for the ring buffer

View File

@@ -1,286 +0,0 @@
// Copyright 2023 iRobot Corporation.
//
// 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__EXPERIMENTAL__EXECUTORS__EVENTS_EXECUTOR__EVENTS_EXECUTOR_HPP_
#define RCLCPP__EXPERIMENTAL__EXECUTORS__EVENTS_EXECUTOR__EVENTS_EXECUTOR_HPP_
#include <atomic>
#include <chrono>
#include <memory>
#include <vector>
#include "rclcpp/executor.hpp"
#include "rclcpp/executors/executor_entities_collection.hpp"
#include "rclcpp/executors/executor_entities_collector.hpp"
#include "rclcpp/experimental/executors/events_executor/events_executor_event_types.hpp"
#include "rclcpp/experimental/executors/events_executor/events_queue.hpp"
#include "rclcpp/experimental/executors/events_executor/simple_events_queue.hpp"
#include "rclcpp/experimental/timers_manager.hpp"
#include "rclcpp/node.hpp"
namespace rclcpp
{
namespace experimental
{
namespace executors
{
/// Events executor implementation
/**
* This executor uses an events queue and a timers manager to execute entities from its
* associated nodes and callback groups.
* ROS 2 entities allow to set callback functions that are invoked when the entity is triggered
* or has work to do. The events-executor sets these callbacks such that they push an
* event into its queue.
*
* This executor tries to reduce as much as possible the amount of maintenance operations.
* This allows to use customized `EventsQueue` classes to achieve different goals such
* as very low CPU usage, bounded memory requirement, determinism, etc.
*
* The executor uses a weak ownership model and it locks entities only while executing
* their related events.
*
* To run this executor:
* rclcpp::experimental::executors::EventsExecutor executor;
* executor.add_node(node);
* executor.spin();
* executor.remove_node(node);
*/
class EventsExecutor : public rclcpp::Executor
{
friend class EventsExecutorEntitiesCollector;
public:
RCLCPP_SMART_PTR_DEFINITIONS(EventsExecutor)
/// Default constructor. See the default constructor for Executor.
/**
* \param[in] events_queue The queue used to store events.
* \param[in] execute_timers_separate_thread If true, timers are executed in a separate
* thread. If false, timers are executed in the same thread as all other entities.
* \param[in] options Options used to configure the executor.
*/
RCLCPP_PUBLIC
explicit EventsExecutor(
rclcpp::experimental::executors::EventsQueue::UniquePtr events_queue = std::make_unique<
rclcpp::experimental::executors::SimpleEventsQueue>(),
bool execute_timers_separate_thread = false,
const rclcpp::ExecutorOptions & options = rclcpp::ExecutorOptions());
/// Default destructor.
RCLCPP_PUBLIC
virtual ~EventsExecutor();
/// Events executor implementation of spin.
/**
* This function will block until work comes in, execute it, and keep blocking.
* It will only be interrupted by a CTRL-C (managed by the global signal handler).
* \throws std::runtime_error when spin() called while already spinning
*/
RCLCPP_PUBLIC
void
spin() override;
/// Events executor implementation of spin some
/**
* This non-blocking function will execute the timers and events
* that were ready when this API was called, until timeout or no
* more work available. New ready-timers/events arrived while
* executing work, won't be taken into account here.
*
* Example:
* while(condition) {
* spin_some();
* sleep(); // User should have some sync work or
* // sleep to avoid a 100% CPU usage
* }
*/
RCLCPP_PUBLIC
void
spin_some(std::chrono::nanoseconds max_duration = std::chrono::nanoseconds(0)) override;
/// Events executor implementation of spin all
/**
* This non-blocking function will execute timers and events
* until timeout or no more work available. If new ready-timers/events
* arrive while executing work available, they will be executed
* as long as the timeout hasn't expired.
*
* Example:
* while(condition) {
* spin_all();
* sleep(); // User should have some sync work or
* // sleep to avoid a 100% CPU usage
* }
*/
RCLCPP_PUBLIC
void
spin_all(std::chrono::nanoseconds max_duration) 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::EventsExecutor::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;
/// 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;
/// Get callback groups that belong to executor.
/**
* \sa rclcpp::Executor::get_all_callback_groups()
*/
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:
/// Internal implementation of spin_once
RCLCPP_PUBLIC
void
spin_once_impl(std::chrono::nanoseconds timeout) override;
/// Internal implementation of spin_some
RCLCPP_PUBLIC
void
spin_some_impl(std::chrono::nanoseconds max_duration, bool exhaustive);
private:
RCLCPP_DISABLE_COPY(EventsExecutor)
/// Execute a provided executor event if its associated entities are available
void
execute_event(const ExecutorEvent & event);
/// Collect entities from callback groups and refresh the current collection with them
void
refresh_current_collection_from_callback_groups();
/// Refresh the current collection using the provided new_collection
void
refresh_current_collection(const rclcpp::executors::ExecutorEntitiesCollection & new_collection);
/// Create a listener callback function for the provided entity
std::function<void(size_t)>
create_entity_callback(void * entity_key, ExecutorEventType type);
/// Create a listener callback function for the provided waitable entity
std::function<void(size_t, int)>
create_waitable_callback(const rclcpp::Waitable * waitable_id);
/// Searches for the provided entity_id in the collection and returns the entity if valid
template<typename CollectionType>
typename CollectionType::EntitySharedPtr
retrieve_entity(typename CollectionType::Key entity_id, CollectionType & collection)
{
// Check if the entity_id is in the collection
auto it = collection.find(entity_id);
if (it == collection.end()) {
return nullptr;
}
// Check if the entity associated with the entity_id is valid
// and remove it from the collection if it isn't
auto entity = it->second.entity.lock();
if (!entity) {
collection.erase(it);
}
// Return the retrieved entity (this can be a nullptr if the entity was not valid)
return entity;
}
/// Queue where entities can push events
rclcpp::experimental::executors::EventsQueue::UniquePtr events_queue_;
std::shared_ptr<rclcpp::executors::ExecutorEntitiesCollector> entities_collector_;
std::shared_ptr<rclcpp::executors::ExecutorEntitiesCollection> current_entities_collection_;
std::shared_ptr<rclcpp::executors::ExecutorNotifyWaitable> notify_waitable_;
/// Flag used to reduce the number of unnecessary waitable events
std::atomic<bool> notify_waitable_event_pushed_ {false};
/// Timers manager used to track and/or execute associated timers
std::shared_ptr<rclcpp::experimental::TimersManager> timers_manager_;
};
} // namespace executors
} // namespace experimental
} // namespace rclcpp
#endif // RCLCPP__EXPERIMENTAL__EXECUTORS__EVENTS_EXECUTOR__EVENTS_EXECUTOR_HPP_

View File

@@ -1,46 +0,0 @@
// Copyright 2023 iRobot Corporation.
//
// 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__EXPERIMENTAL__EXECUTORS__EVENTS_EXECUTOR__EVENTS_EXECUTOR_EVENT_TYPES_HPP_
#define RCLCPP__EXPERIMENTAL__EXECUTORS__EVENTS_EXECUTOR__EVENTS_EXECUTOR_EVENT_TYPES_HPP_
namespace rclcpp
{
namespace experimental
{
namespace executors
{
enum ExecutorEventType
{
CLIENT_EVENT,
SUBSCRIPTION_EVENT,
SERVICE_EVENT,
TIMER_EVENT,
WAITABLE_EVENT
};
struct ExecutorEvent
{
const void * entity_key;
int waitable_data;
ExecutorEventType type;
size_t num_events;
};
} // namespace executors
} // namespace experimental
} // namespace rclcpp
#endif // RCLCPP__EXPERIMENTAL__EXECUTORS__EVENTS_EXECUTOR__EVENTS_EXECUTOR_EVENT_TYPES_HPP_

View File

@@ -1,100 +0,0 @@
// Copyright 2023 iRobot Corporation.
//
// 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__EXPERIMENTAL__EXECUTORS__EVENTS_EXECUTOR__EVENTS_QUEUE_HPP_
#define RCLCPP__EXPERIMENTAL__EXECUTORS__EVENTS_EXECUTOR__EVENTS_QUEUE_HPP_
#include <queue>
#include "rclcpp/macros.hpp"
#include "rclcpp/visibility_control.hpp"
#include "rclcpp/experimental/executors/events_executor/events_executor_event_types.hpp"
namespace rclcpp
{
namespace experimental
{
namespace executors
{
/**
* @brief This abstract class can be used to implement different types of queues
* where `ExecutorEvent` can be stored.
* The derived classes should choose which underlying container to use and
* the strategy for pushing and popping events.
* For example a queue implementation may be bounded or unbounded and have
* different pruning strategies.
* Implementations may or may not check the validity of events and decide how to handle
* the situation where an event is not valid anymore (e.g. a subscription history cache overruns)
*/
class EventsQueue
{
public:
RCLCPP_SMART_PTR_ALIASES_ONLY(EventsQueue)
RCLCPP_PUBLIC
EventsQueue() = default;
/**
* @brief Destruct the object.
*/
RCLCPP_PUBLIC
virtual ~EventsQueue() = default;
/**
* @brief push event into the queue
* @param event The event to push into the queue
*/
RCLCPP_PUBLIC
virtual
void
enqueue(const rclcpp::experimental::executors::ExecutorEvent & event) = 0;
/**
* @brief Extracts an event from the queue, eventually waiting until timeout
* if none is available.
* @return true if event has been found, false if timeout
*/
RCLCPP_PUBLIC
virtual
bool
dequeue(
rclcpp::experimental::executors::ExecutorEvent & event,
std::chrono::nanoseconds timeout = std::chrono::nanoseconds::max()) = 0;
/**
* @brief Test whether queue is empty
* @return true if the queue's size is 0, false otherwise.
*/
RCLCPP_PUBLIC
virtual
bool
empty() const = 0;
/**
* @brief Returns the number of elements in the queue.
* @return the number of elements in the queue.
*/
RCLCPP_PUBLIC
virtual
size_t
size() const = 0;
};
} // namespace executors
} // namespace experimental
} // namespace rclcpp
#endif // RCLCPP__EXPERIMENTAL__EXECUTORS__EVENTS_EXECUTOR__EVENTS_QUEUE_HPP_

View File

@@ -1,134 +0,0 @@
// Copyright 2023 iRobot Corporation.
//
// 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__EXPERIMENTAL__EXECUTORS__EVENTS_EXECUTOR__SIMPLE_EVENTS_QUEUE_HPP_
#define RCLCPP__EXPERIMENTAL__EXECUTORS__EVENTS_EXECUTOR__SIMPLE_EVENTS_QUEUE_HPP_
#include <condition_variable>
#include <mutex>
#include <queue>
#include <utility>
#include "rclcpp/experimental/executors/events_executor/events_queue.hpp"
namespace rclcpp
{
namespace experimental
{
namespace executors
{
/**
* @brief This class implements an EventsQueue as a simple wrapper around a std::queue.
* It does not perform any checks about the size of queue, which can grow
* unbounded without being pruned.
* The simplicity of this implementation makes it suitable for optimizing CPU usage.
*/
class SimpleEventsQueue : public EventsQueue
{
public:
RCLCPP_PUBLIC
~SimpleEventsQueue() override = default;
/**
* @brief enqueue event into the queue
* Thread safe
* @param event The event to enqueue into the queue
*/
RCLCPP_PUBLIC
void
enqueue(const rclcpp::experimental::executors::ExecutorEvent & event) override
{
rclcpp::experimental::executors::ExecutorEvent single_event = event;
single_event.num_events = 1;
{
std::unique_lock<std::mutex> lock(mutex_);
for (size_t ev = 0; ev < event.num_events; ev++) {
event_queue_.push(single_event);
}
}
events_queue_cv_.notify_one();
}
/**
* @brief waits for an event until timeout, gets a single event
* Thread safe
* @return true if event, false if timeout
*/
RCLCPP_PUBLIC
bool
dequeue(
rclcpp::experimental::executors::ExecutorEvent & event,
std::chrono::nanoseconds timeout = std::chrono::nanoseconds::max()) override
{
std::unique_lock<std::mutex> lock(mutex_);
// Initialize to true because it's only needed if we have a valid timeout
bool has_data = true;
if (timeout != std::chrono::nanoseconds::max()) {
has_data =
events_queue_cv_.wait_for(lock, timeout, [this]() {return !event_queue_.empty();});
} else {
events_queue_cv_.wait(lock, [this]() {return !event_queue_.empty();});
}
if (has_data) {
event = event_queue_.front();
event_queue_.pop();
return true;
}
return false;
}
/**
* @brief Test whether queue is empty
* Thread safe
* @return true if the queue's size is 0, false otherwise.
*/
RCLCPP_PUBLIC
bool
empty() const override
{
std::unique_lock<std::mutex> lock(mutex_);
return event_queue_.empty();
}
/**
* @brief Returns the number of elements in the queue.
* Thread safe
* @return the number of elements in the queue.
*/
RCLCPP_PUBLIC
size_t
size() const override
{
std::unique_lock<std::mutex> lock(mutex_);
return event_queue_.size();
}
private:
// The underlying queue implementation
std::queue<rclcpp::experimental::executors::ExecutorEvent> event_queue_;
// Mutex to protect read/write access to the queue
mutable std::mutex mutex_;
// Variable used to notify when an event is added to the queue
std::condition_variable events_queue_cv_;
};
} // namespace executors
} // namespace experimental
} // namespace rclcpp
#endif // RCLCPP__EXPERIMENTAL__EXECUTORS__EVENTS_EXECUTOR__SIMPLE_EVENTS_QUEUE_HPP_

View File

@@ -118,13 +118,6 @@ public:
return nullptr;
}
}
if (this->buffer_->has_data()) {
// If there is data still to be processed, indicate to the
// executor or waitset by triggering the guard condition.
this->trigger_guard_condition();
}
return std::static_pointer_cast<void>(
std::make_shared<std::pair<ConstMessageSharedPtr, MessageUniquePtr>>(
std::pair<ConstMessageSharedPtr, MessageUniquePtr>(

View File

@@ -31,8 +31,6 @@
#include "rclcpp/qos.hpp"
#include "rclcpp/type_support_decl.hpp"
#include "tracetools/tracetools.h"
namespace rclcpp
{
namespace experimental
@@ -93,10 +91,6 @@ public:
buffer_type,
qos_profile,
std::make_shared<Alloc>(subscribed_type_allocator_));
TRACEPOINT(
rclcpp_ipb_to_subscription,
static_cast<const void *>(buffer_.get()),
static_cast<const void *>(this));
}
bool

View File

@@ -1,553 +0,0 @@
// Copyright 2023 iRobot Corporation.
//
// 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__EXPERIMENTAL__TIMERS_MANAGER_HPP_
#define RCLCPP__EXPERIMENTAL__TIMERS_MANAGER_HPP_
#include <algorithm>
#include <atomic>
#include <chrono>
#include <condition_variable>
#include <functional>
#include <memory>
#include <mutex>
#include <thread>
#include <utility>
#include <vector>
#include "rclcpp/context.hpp"
#include "rclcpp/timer.hpp"
namespace rclcpp
{
namespace experimental
{
/**
* @brief This class provides a way for storing and executing timer objects.
* It provides APIs to suit the needs of different applications and execution models.
* All public APIs provided by this class are thread-safe.
*
* Timers management
* This class provides APIs to add/remove timers to/from an internal storage.
* It keeps a list of weak pointers from added timers, and locks them only when
* they need to be executed or modified.
* Timers are kept ordered in a binary-heap priority queue.
* Calls to add/remove APIs will temporarily block the execution of the timers and
* will require to reorder the internal priority queue.
* Because of this, they have a not-negligible impact on the performance.
*
* Timers execution
* The most efficient use of this class consists in letting a TimersManager object
* to spawn a thread where timers are monitored and optionally executed.
* This can be controlled via the `start` and `stop` methods.
* Ready timers can either be executed or an on_ready_callback can be used to notify
* other entities that they are ready and need to be executed.
* Other APIs allow to directly execute a given timer.
*
* This class assumes that the `execute_callback()` API of the stored timers is never
* called by other entities, but it can only be called from here.
* If this assumption is not respected, the heap property may be invalidated,
* so timers may be executed out of order, without this object noticing it.
*
*/
class TimersManager
{
public:
RCLCPP_SMART_PTR_DEFINITIONS_NOT_COPYABLE(TimersManager)
/**
* @brief Construct a new TimersManager object
*
* @param context custom context to be used.
* Shared ownership of the context is held until destruction.
* @param on_ready_callback The timers on ready callback. The presence of this function
* indicates what to do when the TimersManager is running and a timer becomes ready.
* The TimersManager is considered "running" when the `start` method has been called.
* If it's callable, it will be invoked instead of the timer callback.
* If it's not callable, then the TimersManager will
* directly execute timers when they are ready.
* All the methods that execute a given timer (e.g. `execute_head_timer`
* or `execute_ready_timer`) without the TimersManager being `running`, i.e.
* without actually explicitly waiting for the timer to become ready, will ignore this
* callback.
*/
RCLCPP_PUBLIC
TimersManager(
std::shared_ptr<rclcpp::Context> context,
std::function<void(const rclcpp::TimerBase *)> on_ready_callback = nullptr);
/**
* @brief Destruct the TimersManager object making sure to stop thread and release memory.
*/
RCLCPP_PUBLIC
~TimersManager();
/**
* @brief Adds a new timer to the storage, maintaining weak ownership of it.
* Function is thread safe and it can be called regardless of the state of the timers thread.
*
* @param timer the timer to add.
* @throws std::invalid_argument if timer is a nullptr.
*/
RCLCPP_PUBLIC
void add_timer(rclcpp::TimerBase::SharedPtr timer);
/**
* @brief Remove a single timer from the object storage.
* Will do nothing if the timer was not being stored here.
* Function is thread safe and it can be called regardless of the state of the timers thread.
*
* @param timer the timer to remove.
*/
RCLCPP_PUBLIC
void remove_timer(rclcpp::TimerBase::SharedPtr timer);
/**
* @brief Remove all the timers stored in the object.
* Function is thread safe and it can be called regardless of the state of the timers thread.
*/
RCLCPP_PUBLIC
void clear();
/**
* @brief Starts a thread that takes care of executing the timers stored in this object.
* Function will throw an error if the timers thread was already running.
*/
RCLCPP_PUBLIC
void start();
/**
* @brief Stops the timers thread.
* Will do nothing if the timer thread was not running.
*/
RCLCPP_PUBLIC
void stop();
/**
* @brief Get the number of timers that are currently ready.
* This function is thread safe.
*
* @return size_t number of ready timers.
* @throws std::runtime_error if the timers thread was already running.
*/
RCLCPP_PUBLIC
size_t get_number_ready_timers();
/**
* @brief Executes head timer if ready.
* This function is thread safe.
* This function will try to execute the timer callback regardless of whether
* the TimersManager on_ready_callback was passed during construction.
*
* @return true if head timer was ready.
* @throws std::runtime_error if the timers thread was already running.
*/
RCLCPP_PUBLIC
bool execute_head_timer();
/**
* @brief Executes timer identified by its ID.
* This function is thread safe.
* This function will try to execute the timer callback regardless of whether
* the TimersManager on_ready_callback was passed during construction.
*
* @param timer_id the timer ID of the timer to execute
*/
RCLCPP_PUBLIC
void execute_ready_timer(const rclcpp::TimerBase * timer_id);
/**
* @brief Get the amount of time before the next timer triggers.
* This function is thread safe.
*
* @return std::chrono::nanoseconds to wait,
* the returned value could be negative if the timer is already expired
* or std::chrono::nanoseconds::max() if there are no timers stored in the object.
* @throws std::runtime_error if the timers thread was already running.
*/
RCLCPP_PUBLIC
std::chrono::nanoseconds get_head_timeout();
private:
RCLCPP_DISABLE_COPY(TimersManager)
using TimerPtr = rclcpp::TimerBase::SharedPtr;
using WeakTimerPtr = rclcpp::TimerBase::WeakPtr;
// Forward declaration
class TimersHeap;
/**
* @brief This class allows to store weak pointers to timers in a heap-like data structure.
* The root of the heap is the timer that triggers first.
* Since this class uses weak ownership, it is not guaranteed that it represents a valid heap
* at any point in time as timers could go out of scope, thus invalidating it.
* The "validate_and_lock" API allows to restore the heap property and also returns a locked version
* of the timers heap.
* This class is not thread safe and requires external mutexes to protect its usage.
*/
class WeakTimersHeap
{
public:
/**
* @brief Add a new timer to the heap. After the addition, the heap property is enforced.
*
* @param timer new timer to add.
* @return true if timer has been added, false if it was already there.
*/
bool add_timer(TimerPtr timer)
{
TimersHeap locked_heap = this->validate_and_lock();
bool added = locked_heap.add_timer(std::move(timer));
if (added) {
// Re-create the weak heap with the new timer added
this->store(locked_heap);
}
return added;
}
/**
* @brief Remove a timer from the heap. After the removal, the heap property is enforced.
*
* @param timer timer to remove.
* @return true if timer has been removed, false if it was not there.
*/
bool remove_timer(TimerPtr timer)
{
TimersHeap locked_heap = this->validate_and_lock();
bool removed = locked_heap.remove_timer(std::move(timer));
if (removed) {
// Re-create the weak heap with the timer removed
this->store(locked_heap);
}
return removed;
}
/**
* @brief Retrieve the timer identified by the key
* @param timer_id The ID of the timer to retrieve.
* @return TimerPtr if there's a timer associated with the ID, nullptr otherwise
*/
TimerPtr get_timer(const rclcpp::TimerBase * timer_id)
{
for (auto & weak_timer : weak_heap_) {
auto timer = weak_timer.lock();
if (timer.get() == timer_id) {
return timer;
}
}
return nullptr;
}
/**
* @brief Returns a const reference to the front element.
*/
const WeakTimerPtr & front() const
{
return weak_heap_.front();
}
/**
* @brief Returns whether the heap is empty or not.
*/
bool empty() const
{
return weak_heap_.empty();
}
/**
* @brief This function restores the current object as a valid heap
* and it returns a locked version of it.
* Timers that went out of scope are removed from the container.
* It is the only public API to access and manipulate the stored timers.
*
* @return TimersHeap owned timers corresponding to the current object
*/
TimersHeap validate_and_lock()
{
TimersHeap locked_heap;
bool any_timer_destroyed = false;
for (auto weak_timer : weak_heap_) {
auto timer = weak_timer.lock();
if (timer) {
// This timer is valid, so add it to the locked heap
// Note: we access friend private `owned_heap_` member field.
locked_heap.owned_heap_.push_back(std::move(timer));
} else {
// This timer went out of scope, so we don't add it to locked heap
// and we mark the corresponding flag.
// It's not needed to erase it from weak heap, as we are going to re-heapify.
// Note: we can't exit from the loop here, as we need to find all valid timers.
any_timer_destroyed = true;
}
}
// If a timer has gone out of scope, then the remaining elements do not represent
// a valid heap anymore. We need to re-heapify the timers heap.
if (any_timer_destroyed) {
locked_heap.heapify();
// Re-create the weak heap now that elements have been heapified again
this->store(locked_heap);
}
return locked_heap;
}
/**
* @brief This function allows to recreate the heap of weak pointers
* from an heap of owned pointers.
* It is required to be called after a locked TimersHeap generated from this object
* has been modified in any way (e.g. timers triggered, added, removed).
*
* @param heap timers heap to store as weak pointers
*/
void store(const TimersHeap & heap)
{
weak_heap_.clear();
// Note: we access friend private `owned_heap_` member field.
for (auto t : heap.owned_heap_) {
weak_heap_.push_back(t);
}
}
/**
* @brief Remove all timers from the heap.
*/
void clear()
{
weak_heap_.clear();
}
private:
std::vector<WeakTimerPtr> weak_heap_;
};
/**
* @brief This class is the equivalent of WeakTimersHeap but with ownership of the timers.
* It can be generated by locking the weak version.
* It provides operations to manipulate the heap.
* This class is not thread safe and requires external mutexes to protect its usage.
*/
class TimersHeap
{
public:
/**
* @brief Try to add a new timer to the heap.
* After the addition, the heap property is preserved.
* @param timer new timer to add.
* @return true if timer has been added, false if it was already there.
*/
bool add_timer(TimerPtr timer)
{
// Nothing to do if the timer is already stored here
auto it = std::find(owned_heap_.begin(), owned_heap_.end(), timer);
if (it != owned_heap_.end()) {
return false;
}
owned_heap_.push_back(std::move(timer));
std::push_heap(owned_heap_.begin(), owned_heap_.end(), timer_greater);
return true;
}
/**
* @brief Try to remove a timer from the heap.
* After the removal, the heap property is preserved.
* @param timer timer to remove.
* @return true if timer has been removed, false if it was not there.
*/
bool remove_timer(TimerPtr timer)
{
// Nothing to do if the timer is not stored here
auto it = std::find(owned_heap_.begin(), owned_heap_.end(), timer);
if (it == owned_heap_.end()) {
return false;
}
owned_heap_.erase(it);
this->heapify();
return true;
}
/**
* @brief Returns a reference to the front element.
* @return reference to front element.
*/
TimerPtr & front()
{
return owned_heap_.front();
}
/**
* @brief Returns a const reference to the front element.
* @return const reference to front element.
*/
const TimerPtr & front() const
{
return owned_heap_.front();
}
/**
* @brief Returns whether the heap is empty or not.
* @return true if the heap is empty.
*/
bool empty() const
{
return owned_heap_.empty();
}
/**
* @brief Returns the size of the heap.
* @return the number of valid timers in the heap.
*/
size_t size() const
{
return owned_heap_.size();
}
/**
* @brief Get the number of timers that are currently ready.
* @return size_t number of ready timers.
*/
size_t get_number_ready_timers() const
{
size_t ready_timers = 0;
for (TimerPtr t : owned_heap_) {
if (t->is_ready()) {
ready_timers++;
}
}
return ready_timers;
}
/**
* @brief Restore a valid heap after the root value has been replaced (e.g. timer triggered).
*/
void heapify_root()
{
// The following code is a more efficient version than doing
// pop_heap, pop_back, push_back, push_heap
// as it removes the need for the last push_heap
// Push the modified element (i.e. the current root) at the bottom of the heap
owned_heap_.push_back(owned_heap_[0]);
// Exchange first and last-1 elements and reheapify
std::pop_heap(owned_heap_.begin(), owned_heap_.end(), timer_greater);
// Remove last element
owned_heap_.pop_back();
}
/**
* @brief Completely restores the structure to a valid heap
*/
void heapify()
{
std::make_heap(owned_heap_.begin(), owned_heap_.end(), timer_greater);
}
/**
* @brief Helper function to clear the "on_reset_callback" on all associated timers.
*/
void clear_timers_on_reset_callbacks()
{
for (TimerPtr & t : owned_heap_) {
t->clear_on_reset_callback();
}
}
/**
* @brief Friend declaration to allow the `validate_and_lock()` function to access the
* underlying heap container
*/
friend TimersHeap WeakTimersHeap::validate_and_lock();
/**
* @brief Friend declaration to allow the `store()` function to access the
* underlying heap container
*/
friend void WeakTimersHeap::store(const TimersHeap & heap);
private:
/**
* @brief Comparison function between timers.
* @return true if `a` triggers after `b`.
*/
static bool timer_greater(TimerPtr a, TimerPtr b)
{
// TODO(alsora): this can cause an error if timers are using different clocks
return a->time_until_trigger() > b->time_until_trigger();
}
std::vector<TimerPtr> owned_heap_;
};
/**
* @brief Implements a loop that keeps executing ready timers.
* This function is executed in the timers thread.
*/
void run_timers();
/**
* @brief Get the amount of time before the next timer triggers.
* This function is not thread safe, acquire a mutex before calling it.
*
* @return std::chrono::nanoseconds to wait,
* the returned value could be negative if the timer is already expired
* or std::chrono::nanoseconds::max() if the heap is empty.
* This function is not thread safe, acquire the timers_mutex_ before calling it.
*/
std::chrono::nanoseconds get_head_timeout_unsafe();
/**
* @brief Executes all the timers currently ready when the function is invoked
* while keeping the heap correctly sorted.
* This function is not thread safe, acquire the timers_mutex_ before calling it.
*/
void execute_ready_timers_unsafe();
// Callback to be called when timer is ready
std::function<void(const rclcpp::TimerBase *)> on_ready_callback_ = nullptr;
// Thread used to run the timers execution task
std::thread timers_thread_;
// Protects access to timers
std::mutex timers_mutex_;
// Protects access to stop()
std::mutex stop_mutex_;
// Notifies the timers thread whenever timers are added/removed
std::condition_variable timers_cv_;
// Flag used as predicate by timers_cv_ that denotes one or more timers being added/removed
bool timers_updated_ {false};
// Indicates whether the timers thread is currently running or not
std::atomic<bool> running_ {false};
// Parent context used to understand if ROS is still active
std::shared_ptr<rclcpp::Context> context_;
// Timers heap storage with weak ownership
WeakTimersHeap weak_timers_heap_;
};
} // namespace experimental
} // namespace rclcpp
#endif // RCLCPP__EXPERIMENTAL__TIMERS_MANAGER_HPP_

View File

@@ -84,7 +84,7 @@ public:
options.to_rcl_subscription_options(qos),
options.event_callbacks,
options.use_default_callbacks,
DeliveredMessageKind::SERIALIZED_MESSAGE),
SubscriptionType::SERIALIZED_MESSAGE),
callback_(callback),
ts_lib_(ts_lib)
{}
@@ -125,6 +125,7 @@ public:
// DYNAMIC TYPE ==================================================================================
// TODO(methylDragon): Reorder later
RCLCPP_PUBLIC
rclcpp::dynamic_typesupport::DynamicMessageType::SharedPtr get_shared_dynamic_message_type()
override;

View File

@@ -121,19 +121,10 @@ public:
std::atomic_bool &
get_associated_with_executor_atomic() override;
[[deprecated("Use get_shared_notify_guard_condition or trigger_notify_guard_condition instead")]]
RCLCPP_PUBLIC
rclcpp::GuardCondition &
get_notify_guard_condition() override;
RCLCPP_PUBLIC
rclcpp::GuardCondition::SharedPtr
get_shared_notify_guard_condition() override;
RCLCPP_PUBLIC
void
trigger_notify_guard_condition() override;
RCLCPP_PUBLIC
bool
get_use_intra_process_default() const override;
@@ -162,7 +153,7 @@ private:
/// Guard condition for notifying the Executor of changes to this node.
mutable std::recursive_mutex notify_guard_condition_mutex_;
std::shared_ptr<rclcpp::GuardCondition> notify_guard_condition_;
rclcpp::GuardCondition notify_guard_condition_;
bool notify_guard_condition_is_valid_;
};

View File

@@ -148,33 +148,13 @@ public:
/**
* For example, this should be notified when a publisher is added or removed.
*
* \return the GuardCondition if it is valid, else throw runtime error
* \return the GuardCondition if it is valid, else thow runtime error
*/
RCLCPP_PUBLIC
virtual
rclcpp::GuardCondition &
get_notify_guard_condition() = 0;
/// Return a guard condition that should be notified when the internal node state changes.
/**
* For example, this should be notified when a publisher is added or removed.
*
* \return the GuardCondition if it is valid, else nullptr
*/
RCLCPP_PUBLIC
virtual
rclcpp::GuardCondition::SharedPtr
get_shared_notify_guard_condition() = 0;
/// Trigger the guard condition that notifies of internal node state changes.
/**
* For example, this should be notified when a publisher is added or removed.
*/
RCLCPP_PUBLIC
virtual
void
trigger_notify_guard_condition() = 0;
/// Return the default preference for using intra process communication.
RCLCPP_PUBLIC
virtual

View File

@@ -57,8 +57,7 @@ public:
node_namespace_(info.node_namespace),
topic_type_(info.topic_type),
endpoint_type_(static_cast<rclcpp::EndpointType>(info.endpoint_type)),
qos_profile_({info.qos_profile.history, info.qos_profile.depth}, info.qos_profile),
topic_type_hash_(info.topic_type_hash)
qos_profile_({info.qos_profile.history, info.qos_profile.depth}, info.qos_profile)
{
std::copy(info.endpoint_gid, info.endpoint_gid + RMW_GID_STORAGE_SIZE, endpoint_gid_.begin());
}
@@ -123,16 +122,6 @@ public:
const rclcpp::QoS &
qos_profile() const;
/// Get a mutable reference to the type hash of the topic endpoint.
RCLCPP_PUBLIC
rosidl_type_hash_t &
topic_type_hash();
/// Get a const reference to the type hash of the topic endpoint.
RCLCPP_PUBLIC
const rosidl_type_hash_t &
topic_type_hash() const;
private:
std::string node_name_;
std::string node_namespace_;
@@ -140,7 +129,6 @@ private:
rclcpp::EndpointType endpoint_type_;
std::array<uint8_t, RMW_GID_STORAGE_SIZE> endpoint_gid_;
rclcpp::QoS qos_profile_;
rosidl_type_hash_t topic_type_hash_;
};
namespace node_interfaces

View File

@@ -484,10 +484,6 @@ protected:
if (!msg) {
throw std::runtime_error("cannot publish msg which is a null pointer");
}
TRACEPOINT(
rclcpp_intra_publish,
static_cast<const void *>(publisher_handle_.get()),
msg.get());
ipm->template do_intra_process_publish<PublishedType, ROSMessageType, AllocatorT>(
intra_process_publisher_id_,
@@ -506,10 +502,6 @@ protected:
if (!msg) {
throw std::runtime_error("cannot publish msg which is a null pointer");
}
TRACEPOINT(
rclcpp_intra_publish,
static_cast<const void *>(publisher_handle_.get()),
msg.get());
ipm->template do_intra_process_publish<ROSMessageType, ROSMessageType, AllocatorT>(
intra_process_publisher_id_,
@@ -529,10 +521,6 @@ protected:
if (!msg) {
throw std::runtime_error("cannot publish msg which is a null pointer");
}
TRACEPOINT(
rclcpp_intra_publish,
static_cast<const void *>(publisher_handle_.get()),
msg.get());
return ipm->template do_intra_process_publish_and_return_shared<ROSMessageType, ROSMessageType,
AllocatorT>(

View File

@@ -129,6 +129,9 @@
* - Dynamic typesupport
* - rclcpp::dynamic_typesupport::DynamicMessageTypeSupport
* - rclcpp/dynamic_typesupport/dynamic_message_type_support.hpp
* - Dynamic subscription
* - rclcpp::DynamicSubscription
* - rclcpp/dynamic_subscription.hpp
* - Generic publisher
* - rclcpp::Node::create_generic_publisher()
* - rclcpp::GenericPublisher
@@ -179,4 +182,6 @@
#include "rclcpp/waitable.hpp"
#include "rclcpp/wait_set.hpp"
#include "rclcpp/dynamic_subscription.hpp"
#endif // RCLCPP__RCLCPP_HPP_

View File

@@ -144,7 +144,7 @@ public:
// NOTE(methylDragon): Passing these args separately is necessary for event binding
options.event_callbacks,
options.use_default_callbacks,
callback.is_serialized_message_callback() ? DeliveredMessageKind::SERIALIZED_MESSAGE : DeliveredMessageKind::ROS_MESSAGE), // NOLINT
callback.is_serialized_message_callback() ? SubscriptionType::SERIALIZED_MESSAGE : SubscriptionType::ROS_MESSAGE), // NOLINT
any_callback_(callback),
options_(options),
message_memory_strategy_(message_memory_strategy)

View File

@@ -63,25 +63,12 @@ namespace experimental
class IntraProcessManager;
} // namespace experimental
/// The kind of message that the subscription delivers in its callback, used by the executor
/**
* This enum needs to exist because the callback handle is not accessible to the executor's scope.
*
* "Kind" is used since what is being delivered is a category of messages, for example, there are
* different ROS message types that can be delivered, but they're all ROS messages.
*
* As a concrete example, all of the following callbacks will be considered ROS_MESSAGE for
* DeliveredMessageKind:
* - void callback(const std_msgs::msg::String &)
* - void callback(const std::string &) // type adaption
* - void callback(std::unique_ptr<std_msgs::msg::String>)
*/
enum class DeliveredMessageKind : uint8_t
enum class SubscriptionType : uint8_t
{
INVALID = 0,
ROS_MESSAGE = 1, // The subscription delivers a ROS message to its callback
SERIALIZED_MESSAGE = 2, // The subscription delivers a serialized message to its callback
DYNAMIC_MESSAGE = 3, // The subscription delivers a dynamic message to its callback
ROS_MESSAGE = 1, // take message as ROS message and handle as ROS message
SERIALIZED_MESSAGE = 2, // take message as serialized and handle as serialized
DYNAMIC_MESSAGE_DIRECT = 3, // take message as DynamicMessage and handle as DynamicMessage
DYNAMIC_MESSAGE_FROM_SERIALIZED = 4 // take message as serialized and handle as DynamicMessage
};
/// Virtual base class for subscriptions. This pattern allows us to iterate over different template
@@ -100,8 +87,7 @@ public:
* \param[in] type_support_handle rosidl type support struct, for the Message type of the topic.
* \param[in] topic_name Name of the topic to subscribe to.
* \param[in] subscription_options Options for the subscription.
* \param[in] delivered_message_kind Enum flag to change how the message will be received and
* delivered
* \param[in] subscription_type Enum flag to change how the message will be received and delivered
*/
RCLCPP_PUBLIC
SubscriptionBase(
@@ -111,7 +97,7 @@ public:
const rcl_subscription_options_t & subscription_options,
const SubscriptionEventCallbacks & event_callbacks,
bool use_default_callbacks,
DeliveredMessageKind delivered_message_kind = DeliveredMessageKind::ROS_MESSAGE);
SubscriptionType subscription_type = SubscriptionType::ROS_MESSAGE);
/// Destructor.
RCLCPP_PUBLIC
@@ -252,20 +238,12 @@ public:
const rosidl_message_type_support_t &
get_message_type_support_handle() const;
/// Return if the subscription is serialized
/**
* \return `true` if the subscription is serialized, `false` otherwise
*/
RCLCPP_PUBLIC
bool
is_serialized() const;
/// Return the type of the subscription.
/**
* \return `DeliveredMessageKind`, which adjusts how messages are received and delivered.
* \return `SubscriptionType`, which adjusts how messages are received and delivered.
*/
RCLCPP_PUBLIC
DeliveredMessageKind
SubscriptionType
get_subscription_type() const;
/// Get matching publisher count.
@@ -644,6 +622,10 @@ protected:
rclcpp::node_interfaces::NodeBaseInterface * const node_base_;
// TODO(methylDragon): Remove if we don't need this
// rclcpp::node_interfaces::NodeGraphInterface * const node_graph_;
// rclcpp::node_interfaces::NodeServicesInterface * const node_services_;
std::shared_ptr<rcl_node_t> node_handle_;
std::shared_ptr<rcl_subscription_t> subscription_handle_;
std::shared_ptr<rcl_subscription_t> intra_process_subscription_handle_;
@@ -663,7 +645,7 @@ private:
RCLCPP_DISABLE_COPY(SubscriptionBase)
rosidl_message_type_support_t type_support_;
DeliveredMessageKind delivered_message_type_;
SubscriptionType subscription_type_;
std::atomic<bool> subscription_in_use_by_wait_set_{false};
std::atomic<bool> intra_process_subscription_waitable_in_use_by_wait_set_{false};

View File

@@ -104,7 +104,7 @@ protected:
// TODO(wjwwood): support custom allocator, maybe restrict to polymorphic allocator
rcl_get_default_allocator());
if (RCL_RET_OK != ret) {
rclcpp::exceptions::throw_from_rcl_error(ret, "Failed to create wait set");
rclcpp::exceptions::throw_from_rcl_error(ret);
}
// (Re)build the wait set for the first time.
@@ -192,7 +192,8 @@ protected:
size_t services_from_waitables = 0;
size_t events_from_waitables = 0;
for (const auto & waitable_entry : waitables) {
if (!waitable_entry.waitable) {
auto waitable_ptr_pair = get_raw_pointer_from_smart_pointer(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.
if (HasStrongOwnership) {
// This will not happen in fixed sized storage, as it holds
@@ -203,13 +204,13 @@ protected:
needs_pruning_ = true;
continue;
}
const auto & waitable = waitable_entry.waitable;
subscriptions_from_waitables += waitable->get_number_of_ready_subscriptions();
guard_conditions_from_waitables += waitable->get_number_of_ready_guard_conditions();
timers_from_waitables += waitable->get_number_of_ready_timers();
clients_from_waitables += waitable->get_number_of_ready_clients();
services_from_waitables += waitable->get_number_of_ready_services();
events_from_waitables += waitable->get_number_of_ready_events();
rclcpp::Waitable & waitable = *waitable_ptr_pair.second;
subscriptions_from_waitables += waitable.get_number_of_ready_subscriptions();
guard_conditions_from_waitables += waitable.get_number_of_ready_guard_conditions();
timers_from_waitables += waitable.get_number_of_ready_timers();
clients_from_waitables += waitable.get_number_of_ready_clients();
services_from_waitables += waitable.get_number_of_ready_services();
events_from_waitables += waitable.get_number_of_ready_events();
}
rcl_ret_t ret = rcl_wait_set_resize(
&rcl_wait_set_,
@@ -221,7 +222,7 @@ protected:
events_from_waitables
);
if (RCL_RET_OK != ret) {
rclcpp::exceptions::throw_from_rcl_error(ret, "Couldn't resize the wait set");
rclcpp::exceptions::throw_from_rcl_error(ret);
}
was_resized = true;
// Assumption: the calling code ensures this function is not called
@@ -237,13 +238,15 @@ protected:
if (!was_resized) {
rcl_ret_t ret = rcl_wait_set_clear(&rcl_wait_set_);
if (RCL_RET_OK != ret) {
rclcpp::exceptions::throw_from_rcl_error(ret, "Couldn't clear the wait set");
rclcpp::exceptions::throw_from_rcl_error(ret);
}
}
// Add subscriptions.
for (const auto & subscription_entry : subscriptions) {
if (!subscription_entry.subscription) {
auto subscription_ptr_pair =
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.
if (HasStrongOwnership) {
// This will not happen in fixed sized storage, as it holds
@@ -254,13 +257,12 @@ protected:
needs_pruning_ = true;
continue;
}
rcl_ret_t ret = rcl_wait_set_add_subscription(
&rcl_wait_set_,
subscription_entry.subscription->get_subscription_handle().get(),
subscription_ptr_pair.second->get_subscription_handle().get(),
nullptr);
if (RCL_RET_OK != ret) {
rclcpp::exceptions::throw_from_rcl_error(ret, "Couldn't fill wait set");
rclcpp::exceptions::throw_from_rcl_error(ret);
}
}
@@ -269,7 +271,8 @@ protected:
[this](const auto & inner_guard_conditions)
{
for (const auto & guard_condition : inner_guard_conditions) {
if (!guard_condition) {
auto guard_condition_ptr_pair = get_raw_pointer_from_smart_pointer(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.
if (HasStrongOwnership) {
// This will not happen in fixed sized storage, as it holds
@@ -282,10 +285,10 @@ protected:
}
rcl_ret_t ret = rcl_wait_set_add_guard_condition(
&rcl_wait_set_,
&guard_condition->get_rcl_guard_condition(),
&guard_condition_ptr_pair.second->get_rcl_guard_condition(),
nullptr);
if (RCL_RET_OK != ret) {
rclcpp::exceptions::throw_from_rcl_error(ret, "Couldn't fill wait set");
rclcpp::exceptions::throw_from_rcl_error(ret);
}
}
};
@@ -298,7 +301,8 @@ protected:
// Add timers.
for (const auto & timer : timers) {
if (!timer) {
auto timer_ptr_pair = get_raw_pointer_from_smart_pointer(timer);
if (nullptr == timer_ptr_pair.second) {
// In this case it was probably stored as a weak_ptr, but is now locking to nullptr.
if (HasStrongOwnership) {
// This will not happen in fixed sized storage, as it holds
@@ -311,16 +315,17 @@ protected:
}
rcl_ret_t ret = rcl_wait_set_add_timer(
&rcl_wait_set_,
timer->get_timer_handle().get(),
timer_ptr_pair.second->get_timer_handle().get(),
nullptr);
if (RCL_RET_OK != ret) {
rclcpp::exceptions::throw_from_rcl_error(ret, "Couldn't fill wait set");
rclcpp::exceptions::throw_from_rcl_error(ret);
}
}
// Add clients.
for (const auto & client : clients) {
if (!client) {
auto client_ptr_pair = get_raw_pointer_from_smart_pointer(client);
if (nullptr == client_ptr_pair.second) {
// In this case it was probably stored as a weak_ptr, but is now locking to nullptr.
if (HasStrongOwnership) {
// This will not happen in fixed sized storage, as it holds
@@ -333,17 +338,17 @@ protected:
}
rcl_ret_t ret = rcl_wait_set_add_client(
&rcl_wait_set_,
client->get_client_handle().get(),
client_ptr_pair.second->get_client_handle().get(),
nullptr);
if (RCL_RET_OK != ret) {
rclcpp::exceptions::throw_from_rcl_error(ret);
}
}
// Add services.
for (const auto & service : services) {
if (!service) {
auto service_ptr_pair = get_raw_pointer_from_smart_pointer(service);
if (nullptr == service_ptr_pair.second) {
// In this case it was probably stored as a weak_ptr, but is now locking to nullptr.
if (HasStrongOwnership) {
// This will not happen in fixed sized storage, as it holds
@@ -356,16 +361,17 @@ protected:
}
rcl_ret_t ret = rcl_wait_set_add_service(
&rcl_wait_set_,
service->get_service_handle().get(),
service_ptr_pair.second->get_service_handle().get(),
nullptr);
if (RCL_RET_OK != ret) {
rclcpp::exceptions::throw_from_rcl_error(ret, "Couldn't fill wait set");
rclcpp::exceptions::throw_from_rcl_error(ret);
}
}
// Add waitables.
for (auto & waitable_entry : waitables) {
if (!waitable_entry.waitable) {
auto waitable_ptr_pair = get_raw_pointer_from_smart_pointer(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.
if (HasStrongOwnership) {
// This will not happen in fixed sized storage, as it holds
@@ -376,7 +382,8 @@ protected:
needs_pruning_ = true;
continue;
}
waitable_entry.waitable->add_to_wait_set(&rcl_wait_set_);
rclcpp::Waitable & waitable = *waitable_ptr_pair.second;
waitable.add_to_wait_set(&rcl_wait_set_);
}
}

View File

@@ -204,19 +204,15 @@ public:
void
storage_rebuild_rcl_wait_set(const ArrayOfExtraGuardConditions & extra_guard_conditions)
{
this->storage_acquire_ownerships();
this->storage_rebuild_rcl_wait_set_with_sets(
shared_subscriptions_,
shared_guard_conditions_,
subscriptions_,
guard_conditions_,
extra_guard_conditions,
shared_timers_,
shared_clients_,
shared_services_,
shared_waitables_
timers_,
clients_,
services_,
waitables_
);
this->storage_release_ownerships();
}
template<class EntityT, class SequenceOfEntitiesT>
@@ -411,7 +407,6 @@ public:
}
};
// Lock all the weak pointers and hold them until released.
lock_all(subscriptions_, shared_subscriptions_);
lock_all(guard_conditions_, shared_guard_conditions_);
lock_all(timers_, shared_timers_);
lock_all(clients_, shared_clients_);
@@ -443,7 +438,6 @@ public:
shared_ptr.reset();
}
};
reset_all(shared_subscriptions_);
reset_all(shared_guard_conditions_);
reset_all(shared_timers_);
reset_all(shared_clients_);

View File

@@ -290,7 +290,7 @@ protected:
return create_wait_result(WaitResultKind::Empty);
} else {
// Some other error case, throw.
rclcpp::exceptions::throw_from_rcl_error(ret, "rcl_wait() failed");
rclcpp::exceptions::throw_from_rcl_error(ret);
}
} while (should_loop());

View File

@@ -2,7 +2,7 @@
<?xml-model href="http://download.ros.org/schema/package_format2.xsd" schematypens="http://www.w3.org/2001/XMLSchema"?>
<package format="2">
<name>rclcpp</name>
<version>20.0.0</version>
<version>19.3.0</version>
<description>The ROS client library in C++.</description>
<maintainer email="ivanpauno@ekumenlabs.com">Ivan Paunovic</maintainer>

View File

@@ -16,16 +16,13 @@
using rclcpp::AnyExecutable;
RCLCPP_PUBLIC
AnyExecutable::AnyExecutable()
: subscription(nullptr),
timer(nullptr),
service(nullptr),
client(nullptr),
waitable(nullptr),
callback_group(nullptr),
node_base(nullptr),
data(nullptr)
node_base(nullptr)
{}
AnyExecutable::~AnyExecutable()

View File

@@ -31,12 +31,10 @@ using rclcpp::CallbackGroupType;
CallbackGroup::CallbackGroup(
CallbackGroupType group_type,
std::function<rclcpp::Context::SharedPtr(void)> get_context,
bool automatically_add_to_executor_with_node)
: type_(group_type), associated_with_executor_(false),
can_be_taken_from_(true),
automatically_add_to_executor_with_node_(automatically_add_to_executor_with_node),
get_context_(get_context)
automatically_add_to_executor_with_node_(automatically_add_to_executor_with_node)
{}
CallbackGroup::~CallbackGroup()
@@ -56,17 +54,6 @@ CallbackGroup::type() const
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(
std::function<void(const rclcpp::SubscriptionBase::SharedPtr &)> sub_func,
std::function<void(const rclcpp::ServiceBase::SharedPtr &)> service_func,
@@ -124,7 +111,6 @@ CallbackGroup::automatically_add_to_executor_with_node() const
return automatically_add_to_executor_with_node_;
}
// \TODO(mjcarroll) Deprecated, remove on tock
rclcpp::GuardCondition::SharedPtr
CallbackGroup::get_notify_guard_condition(const rclcpp::Context::SharedPtr context_ptr)
{
@@ -143,29 +129,6 @@ CallbackGroup::get_notify_guard_condition(const rclcpp::Context::SharedPtr conte
return notify_guard_condition_;
}
rclcpp::GuardCondition::SharedPtr
CallbackGroup::get_notify_guard_condition()
{
std::lock_guard<std::recursive_mutex> lock(notify_guard_condition_mutex_);
if (!this->get_context_) {
throw std::runtime_error("Callback group was created without context and not passed context");
}
auto context_ptr = this->get_context_();
if (context_ptr && context_ptr->is_valid()) {
if (notify_guard_condition_ && context_ptr != notify_guard_condition_->get_context()) {
if (associated_with_executor_) {
trigger_notify_guard_condition();
}
notify_guard_condition_ = nullptr;
}
if (!notify_guard_condition_) {
notify_guard_condition_ = std::make_shared<rclcpp::GuardCondition>(context_ptr);
}
return notify_guard_condition_;
}
return nullptr;
}
void
CallbackGroup::trigger_notify_guard_condition()
{

View File

@@ -0,0 +1,113 @@
// Copyright 2022 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/dynamic_subscription.hpp"
#include <memory>
#include <string>
#include "rcl/subscription.h"
#include "rclcpp/exceptions.hpp"
namespace rclcpp
{
std::shared_ptr<void> DynamicSubscription::create_message()
{
return create_serialized_message();
}
std::shared_ptr<rclcpp::SerializedMessage> DynamicSubscription::create_serialized_message()
{
return std::make_shared<rclcpp::SerializedMessage>(0);
}
void DynamicSubscription::handle_message(std::shared_ptr<void> &, const rclcpp::MessageInfo &)
{
throw rclcpp::exceptions::UnimplementedError(
"handle_message is not implemented for DynamicSubscription");
}
void DynamicSubscription::handle_serialized_message(
const std::shared_ptr<rclcpp::SerializedMessage> &, const rclcpp::MessageInfo &)
{
throw rclcpp::exceptions::UnimplementedError(
"handle_serialized_message is not implemented for DynamicSubscription");
}
void DynamicSubscription::handle_loaned_message(void *, const rclcpp::MessageInfo &)
{
throw rclcpp::exceptions::UnimplementedError(
"handle_loaned_message is not implemented for DynamicSubscription");
}
void DynamicSubscription::return_message(std::shared_ptr<void> & message)
{
auto typed_message = std::static_pointer_cast<rclcpp::SerializedMessage>(message);
return_serialized_message(typed_message);
}
void DynamicSubscription::return_serialized_message(
std::shared_ptr<rclcpp::SerializedMessage> & message)
{
message.reset();
}
// DYNAMIC TYPE ====================================================================================
// TODO(methylDragon): Re-order later
// Does not clone
rclcpp::dynamic_typesupport::DynamicMessageType::SharedPtr
DynamicSubscription::get_shared_dynamic_message_type()
{
return dynamic_message_type_;
}
// Does not clone
rclcpp::dynamic_typesupport::DynamicMessage::SharedPtr
DynamicSubscription::get_shared_dynamic_message()
{
return dynamic_message_;
}
rclcpp::dynamic_typesupport::DynamicSerializationSupport::SharedPtr
DynamicSubscription::get_shared_dynamic_serialization_support()
{
return serialization_support_;
}
rclcpp::dynamic_typesupport::DynamicMessage::SharedPtr
DynamicSubscription::create_dynamic_message()
{
return dynamic_message_->init_from_type_shared(*dynamic_message_type_);
}
void
DynamicSubscription::return_dynamic_message(
rclcpp::dynamic_typesupport::DynamicMessage::SharedPtr & message)
{
message.reset();
}
void DynamicSubscription::handle_dynamic_message(
const rclcpp::dynamic_typesupport::DynamicMessage::SharedPtr & message,
const rclcpp::MessageInfo & message_info)
{
(void) message_info;
callback_(message, ts_->get_rosidl_runtime_c_type_description());
}
} // namespace rclcpp

View File

@@ -36,5 +36,734 @@ using rclcpp::dynamic_typesupport::DynamicMessageType;
using rclcpp::dynamic_typesupport::DynamicMessageTypeBuilder;
using rclcpp::dynamic_typesupport::DynamicSerializationSupport;
#ifndef RCLCPP__DYNAMIC_TYPESUPPORT__DETAIL__DYNAMIC_MESSAGE_IMPL_HPP_
// Template specialization implementations
#include "rclcpp/dynamic_typesupport/detail/dynamic_message_impl.hpp"
#endif
// CONSTRUCTION ==================================================================================
DynamicMessage::DynamicMessage(const DynamicMessageTypeBuilder::SharedPtr dynamic_type_builder)
: DynamicMessage::DynamicMessage(
dynamic_type_builder, dynamic_type_builder->get_rosidl_dynamic_type_builder().allocator) {}
DynamicMessage::DynamicMessage(
const DynamicMessageTypeBuilder::SharedPtr dynamic_type_builder,
rcl_allocator_t allocator)
: serialization_support_(dynamic_type_builder->get_shared_dynamic_serialization_support()),
rosidl_dynamic_data_(rosidl_dynamic_typesupport_get_zero_initialized_dynamic_data()),
is_loaned_(false),
parent_data_(nullptr)
{
if (!serialization_support_) {
throw std::runtime_error("dynamic message could not bind serialization support!");
}
if (!dynamic_type_builder) {
throw std::runtime_error("dynamic message type builder cannot be nullptr!");
}
rosidl_dynamic_typesupport_dynamic_type_builder_t rosidl_dynamic_type_builder =
dynamic_type_builder->get_rosidl_dynamic_type_builder();
rcutils_ret_t ret = rosidl_dynamic_typesupport_dynamic_data_init_from_dynamic_type_builder(
&rosidl_dynamic_type_builder, &allocator, &get_rosidl_dynamic_data());
if (ret != RCUTILS_RET_OK) {
throw std::runtime_error("could not init new dynamic data object from dynamic type builder");
}
}
DynamicMessage::DynamicMessage(const DynamicMessageType::SharedPtr dynamic_type)
: DynamicMessage::DynamicMessage(
dynamic_type, dynamic_type->get_rosidl_dynamic_type().allocator) {}
DynamicMessage::DynamicMessage(
const DynamicMessageType::SharedPtr dynamic_type,
rcl_allocator_t allocator)
: serialization_support_(dynamic_type->get_shared_dynamic_serialization_support()),
rosidl_dynamic_data_(rosidl_dynamic_typesupport_get_zero_initialized_dynamic_data()),
is_loaned_(false),
parent_data_(nullptr)
{
if (!serialization_support_) {
throw std::runtime_error("dynamic type could not bind serialization support!");
}
if (!dynamic_type) {
throw std::runtime_error("dynamic message type cannot be nullptr!");
}
rosidl_dynamic_typesupport_dynamic_type_t rosidl_dynamic_type =
dynamic_type->get_rosidl_dynamic_type();
rosidl_dynamic_typesupport_dynamic_data_t rosidl_dynamic_data =
rosidl_dynamic_typesupport_get_zero_initialized_dynamic_data();
rcutils_ret_t ret = rosidl_dynamic_typesupport_dynamic_data_init_from_dynamic_type(
&rosidl_dynamic_type, &allocator, &rosidl_dynamic_data);
if (ret != RCUTILS_RET_OK) {
throw std::runtime_error(
std::string("could not init new dynamic data object from dynamic type") +
rcl_get_error_string().str);
}
}
DynamicMessage::DynamicMessage(
DynamicSerializationSupport::SharedPtr serialization_support,
rosidl_dynamic_typesupport_dynamic_data_t && rosidl_dynamic_data)
: serialization_support_(serialization_support),
rosidl_dynamic_data_(std::move(rosidl_dynamic_data)),
is_loaned_(false),
parent_data_(nullptr)
{
if (serialization_support) {
if (!match_serialization_support_(*serialization_support, rosidl_dynamic_data)) {
throw std::runtime_error(
"serialization support library identifier does not match dynamic data's!");
}
}
}
DynamicMessage::DynamicMessage(
DynamicMessage::SharedPtr parent_data,
rosidl_dynamic_typesupport_dynamic_data_t && rosidl_loaned_data)
: serialization_support_(parent_data->get_shared_dynamic_serialization_support()),
rosidl_dynamic_data_(std::move(rosidl_loaned_data)),
is_loaned_(true),
parent_data_(nullptr)
{
if (!parent_data) {
throw std::runtime_error("parent dynamic data cannot be nullptr!");
}
if (serialization_support_) {
if (!match_serialization_support_(*serialization_support_, rosidl_loaned_data)) {
throw std::runtime_error(
"serialization support library identifier does not match loaned dynamic data's!");
}
}
parent_data_ = parent_data;
}
DynamicMessage::DynamicMessage(DynamicMessage && other) noexcept
: serialization_support_(std::exchange(other.serialization_support_, nullptr)),
rosidl_dynamic_data_(std::exchange(
other.rosidl_dynamic_data_,
rosidl_dynamic_typesupport_get_zero_initialized_dynamic_data())),
is_loaned_(other.is_loaned_),
parent_data_(std::exchange(other.parent_data_, nullptr))
{}
DynamicMessage &
DynamicMessage::operator=(DynamicMessage && other) noexcept
{
std::swap(serialization_support_, other.serialization_support_);
std::swap(rosidl_dynamic_data_, other.rosidl_dynamic_data_);
is_loaned_ = other.is_loaned_;
std::swap(parent_data_, other.parent_data_);
return *this;
}
DynamicMessage::~DynamicMessage()
{} // STUBBED
{
if (!is_loaned_) {
if (rosidl_dynamic_typesupport_dynamic_data_fini(&get_rosidl_dynamic_data()) !=
RCUTILS_RET_OK)
{
RCUTILS_LOG_ERROR("could not fini rosidl dynamic data");
}
return;
}
// Loaned case
if (!parent_data_) {
RCUTILS_LOG_ERROR("dynamic data is loaned, but parent is missing!!");
} else {
rosidl_dynamic_typesupport_dynamic_data_return_loaned_value(
&parent_data_->get_rosidl_dynamic_data(), &get_rosidl_dynamic_data());
}
}
bool
DynamicMessage::match_serialization_support_(
const DynamicSerializationSupport & serialization_support,
const rosidl_dynamic_typesupport_dynamic_data_t & rosidl_dynamic_type_data)
{
if (serialization_support.get_serialization_library_identifier() != std::string(
rosidl_dynamic_type_data.serialization_support->serialization_library_identifier))
{
RCUTILS_LOG_ERROR("serialization support library identifier does not match dynamic data's");
return false;
}
return true;
}
// GETTERS =======================================================================================
const std::string
DynamicMessage::get_serialization_library_identifier() const
{
return std::string(
get_rosidl_dynamic_data().serialization_support->serialization_library_identifier);
}
const std::string
DynamicMessage::get_name() const
{
size_t buf_length;
const char * buf;
if (
rosidl_dynamic_typesupport_dynamic_data_get_name(
&get_rosidl_dynamic_data(), &buf,
&buf_length) !=
RCUTILS_RET_OK)
{
throw std::runtime_error(
std::string("could not get name for dynamic data") + rcl_get_error_string().str);
}
return std::string(buf, buf_length);
}
rosidl_dynamic_typesupport_dynamic_data_t &
DynamicMessage::get_rosidl_dynamic_data()
{
return rosidl_dynamic_data_;
}
const rosidl_dynamic_typesupport_dynamic_data_t &
DynamicMessage::get_rosidl_dynamic_data() const
{
return rosidl_dynamic_data_;
}
DynamicSerializationSupport::SharedPtr
DynamicMessage::get_shared_dynamic_serialization_support()
{
return serialization_support_;
}
DynamicSerializationSupport::ConstSharedPtr
DynamicMessage::get_shared_dynamic_serialization_support() const
{
return serialization_support_;
}
size_t
DynamicMessage::get_item_count() const
{
size_t item_count;
rcutils_ret_t ret = rosidl_dynamic_typesupport_dynamic_data_get_item_count(
&get_rosidl_dynamic_data(), &item_count);
if (ret != RCUTILS_RET_OK) {
throw std::runtime_error("could not get item count of dynamic data");
}
return item_count;
}
rosidl_dynamic_typesupport_member_id_t
DynamicMessage::get_member_id(size_t index) const
{
rosidl_dynamic_typesupport_member_id_t member_id;
rcutils_ret_t ret = rosidl_dynamic_typesupport_dynamic_data_get_member_id_at_index(
&get_rosidl_dynamic_data(), index, &member_id);
if (ret != RCUTILS_RET_OK) {
throw std::runtime_error("could not member id of dynamic data element by index");
}
return member_id;
}
rosidl_dynamic_typesupport_member_id_t
DynamicMessage::get_member_id(const std::string & name) const
{
rosidl_dynamic_typesupport_member_id_t member_id;
rcutils_ret_t ret = rosidl_dynamic_typesupport_dynamic_data_get_member_id_by_name(
&get_rosidl_dynamic_data(), name.c_str(), name.size(), &member_id);
if (ret != RCUTILS_RET_OK) {
throw std::runtime_error("could not member id of dynamic data element by name");
}
return member_id;
}
rosidl_dynamic_typesupport_member_id_t
DynamicMessage::get_array_index(size_t index) const
{
rosidl_dynamic_typesupport_member_id_t array_index;
rcutils_ret_t ret = rosidl_dynamic_typesupport_dynamic_data_get_array_index(
&get_rosidl_dynamic_data(), index, &array_index);
if (ret != RCUTILS_RET_OK) {
throw std::runtime_error("could not array index of dynamic data element by index");
}
return array_index;
}
rosidl_dynamic_typesupport_member_id_t
DynamicMessage::get_array_index(const std::string & name) const
{
return get_array_index(get_member_id(name));
}
// METHODS =======================================================================================
DynamicMessage
DynamicMessage::clone(rcl_allocator_t allocator)
{
rosidl_dynamic_typesupport_dynamic_data_t rosidl_dynamic_data =
rosidl_dynamic_typesupport_get_zero_initialized_dynamic_data();
rcutils_ret_t ret = rosidl_dynamic_typesupport_dynamic_data_clone(
&get_rosidl_dynamic_data(), &allocator, &rosidl_dynamic_data);
if (ret != RCUTILS_RET_OK) {
throw std::runtime_error(
std::string("could not clone dynamic data: ") + rcl_get_error_string().str);
}
return DynamicMessage(get_shared_dynamic_serialization_support(), std::move(rosidl_dynamic_data));
}
DynamicMessage::SharedPtr
DynamicMessage::clone_shared(rcl_allocator_t allocator)
{
rosidl_dynamic_typesupport_dynamic_data_t rosidl_dynamic_data =
rosidl_dynamic_typesupport_get_zero_initialized_dynamic_data();
rcutils_ret_t ret = rosidl_dynamic_typesupport_dynamic_data_clone(
&get_rosidl_dynamic_data(), &allocator, &rosidl_dynamic_data);
if (ret != RCUTILS_RET_OK) {
throw std::runtime_error(
std::string("could not clone dynamic data: ") + rcl_get_error_string().str);
}
return DynamicMessage::make_shared(
get_shared_dynamic_serialization_support(), std::move(rosidl_dynamic_data));
}
DynamicMessage
DynamicMessage::init_from_type(DynamicMessageType & type, rcl_allocator_t allocator) const
{
rosidl_dynamic_typesupport_dynamic_data_t rosidl_dynamic_data =
rosidl_dynamic_typesupport_get_zero_initialized_dynamic_data();
rcutils_ret_t ret = rosidl_dynamic_typesupport_dynamic_data_init_from_dynamic_type(
&type.get_rosidl_dynamic_type(), &allocator, &rosidl_dynamic_data);
if (ret != RCUTILS_RET_OK) {
throw std::runtime_error("could not init new dynamic data object from dynamic type");
}
return DynamicMessage(serialization_support_, std::move(rosidl_dynamic_data));
}
DynamicMessage::SharedPtr
DynamicMessage::init_from_type_shared(DynamicMessageType & type, rcl_allocator_t allocator) const
{
rosidl_dynamic_typesupport_dynamic_data_t rosidl_dynamic_data =
rosidl_dynamic_typesupport_get_zero_initialized_dynamic_data();
rcutils_ret_t ret = rosidl_dynamic_typesupport_dynamic_data_init_from_dynamic_type(
&type.get_rosidl_dynamic_type(), &allocator, &rosidl_dynamic_data);
if (ret != RCUTILS_RET_OK) {
throw std::runtime_error("could not init new dynamic data object from dynamic type");
}
return DynamicMessage::make_shared(serialization_support_, std::move(rosidl_dynamic_data));
}
bool
DynamicMessage::equals(const DynamicMessage & other) const
{
if (get_serialization_library_identifier() != other.get_serialization_library_identifier()) {
throw std::runtime_error("library identifiers don't match");
}
bool equals;
rcutils_ret_t ret = rosidl_dynamic_typesupport_dynamic_data_equals(
&get_rosidl_dynamic_data(), &other.get_rosidl_dynamic_data(), &equals);
if (ret != RCUTILS_RET_OK) {
throw std::runtime_error("could not equate dynamic messages");
}
return equals;
}
DynamicMessage::SharedPtr
DynamicMessage::loan_value(
rosidl_dynamic_typesupport_member_id_t id,
rcl_allocator_t allocator)
{
rosidl_dynamic_typesupport_dynamic_data_t rosidl_dynamic_data =
rosidl_dynamic_typesupport_get_zero_initialized_dynamic_data();
rcutils_ret_t ret = rosidl_dynamic_typesupport_dynamic_data_loan_value(
&get_rosidl_dynamic_data(), id, &allocator, &rosidl_dynamic_data);
if (ret != RCUTILS_RET_OK) {
throw std::runtime_error(
std::string("could not loan dynamic data: ") + rcl_get_error_string().str);
}
return DynamicMessage::make_shared(shared_from_this(), std::move(rosidl_dynamic_data));
}
DynamicMessage::SharedPtr
DynamicMessage::loan_value(const std::string & name, rcl_allocator_t allocator)
{
return loan_value(get_member_id(name), allocator);
}
void
DynamicMessage::clear_all_values()
{
rosidl_dynamic_typesupport_dynamic_data_clear_all_values(&get_rosidl_dynamic_data());
}
void
DynamicMessage::clear_nonkey_values()
{
rosidl_dynamic_typesupport_dynamic_data_clear_nonkey_values(&get_rosidl_dynamic_data());
}
void
DynamicMessage::clear_value(rosidl_dynamic_typesupport_member_id_t id)
{
rosidl_dynamic_typesupport_dynamic_data_clear_value(&get_rosidl_dynamic_data(), id);
}
void
DynamicMessage::clear_value(const std::string & name)
{
clear_value(get_member_id(name));
}
void
DynamicMessage::clear_sequence()
{
rosidl_dynamic_typesupport_dynamic_data_clear_sequence_data(&get_rosidl_dynamic_data());
}
rosidl_dynamic_typesupport_member_id_t
DynamicMessage::insert_sequence_data()
{
rosidl_dynamic_typesupport_member_id_t out;
rosidl_dynamic_typesupport_dynamic_data_insert_sequence_data(&get_rosidl_dynamic_data(), &out);
return out;
}
void
DynamicMessage::remove_sequence_data(rosidl_dynamic_typesupport_member_id_t index)
{
rosidl_dynamic_typesupport_dynamic_data_remove_sequence_data(
&get_rosidl_dynamic_data(), index);
}
bool
DynamicMessage::serialize(rcl_serialized_message_t & buffer)
{
rcutils_ret_t ret = rosidl_dynamic_typesupport_dynamic_data_serialize(
&get_rosidl_dynamic_data(), &buffer);
if (ret != RCUTILS_RET_OK) {
throw std::runtime_error(
std::string("could serialize loan dynamic data: ") + rcl_get_error_string().str);
}
return true;
}
bool
DynamicMessage::deserialize(rcl_serialized_message_t & buffer)
{
rcutils_ret_t ret = rosidl_dynamic_typesupport_dynamic_data_deserialize(
&get_rosidl_dynamic_data(), &buffer);
if (ret != RCUTILS_RET_OK) {
throw std::runtime_error(
std::string("could deserialize loan dynamic data: ") + rcl_get_error_string().str);
}
return true;
}
// MEMBER ACCESS ===================================================================================
// Defined in "detail/dynamic_message_impl.hpp"
// FIXED STRING MEMBER ACCESS ======================================================================
const std::string
DynamicMessage::get_fixed_string_value(
rosidl_dynamic_typesupport_member_id_t id, size_t string_length)
{
size_t buf_length;
char * buf = nullptr;
rosidl_dynamic_typesupport_dynamic_data_get_fixed_string_value(
&get_rosidl_dynamic_data(), id, &buf, &buf_length, string_length);
auto out = std::string(buf, buf_length);
delete buf;
return out;
}
const std::string
DynamicMessage::get_fixed_string_value(const std::string & name, size_t string_length)
{
return get_fixed_string_value(get_member_id(name), string_length);
}
const std::u16string
DynamicMessage::get_fixed_wstring_value(
rosidl_dynamic_typesupport_member_id_t id, size_t wstring_length)
{
size_t buf_length;
char16_t * buf = nullptr;
rosidl_dynamic_typesupport_dynamic_data_get_fixed_wstring_value(
&get_rosidl_dynamic_data(), id, &buf, &buf_length, wstring_length);
auto out = std::u16string(buf, buf_length);
delete buf;
return out;
}
const std::u16string
DynamicMessage::get_fixed_wstring_value(const std::string & name, size_t wstring_length)
{
return get_fixed_wstring_value(get_member_id(name), wstring_length);
}
void
DynamicMessage::set_fixed_string_value(
rosidl_dynamic_typesupport_member_id_t id, const std::string value, size_t string_length)
{
rosidl_dynamic_typesupport_dynamic_data_set_fixed_string_value(
&get_rosidl_dynamic_data(), id, value.c_str(), value.size(), string_length);
}
void
DynamicMessage::set_fixed_string_value(
const std::string & name, const std::string value, size_t string_length)
{
set_fixed_string_value(get_member_id(name), value, string_length);
}
void
DynamicMessage::set_fixed_wstring_value(
rosidl_dynamic_typesupport_member_id_t id, const std::u16string value, size_t wstring_length)
{
rosidl_dynamic_typesupport_dynamic_data_set_fixed_wstring_value(
&get_rosidl_dynamic_data(), id, value.c_str(), value.size(), wstring_length);
}
void
DynamicMessage::set_fixed_wstring_value(
const std::string & name, const std::u16string value, size_t wstring_length)
{
set_fixed_wstring_value(get_member_id(name), value, wstring_length);
}
rosidl_dynamic_typesupport_member_id_t
DynamicMessage::insert_fixed_string_value(const std::string value, size_t string_length)
{
rosidl_dynamic_typesupport_member_id_t out;
rosidl_dynamic_typesupport_dynamic_data_insert_fixed_string_value(
&get_rosidl_dynamic_data(), value.c_str(), value.size(), string_length, &out);
return out;
}
rosidl_dynamic_typesupport_member_id_t
DynamicMessage::insert_fixed_wstring_value(const std::u16string value, size_t wstring_length)
{
rosidl_dynamic_typesupport_member_id_t out;
rosidl_dynamic_typesupport_dynamic_data_insert_fixed_wstring_value(
&get_rosidl_dynamic_data(), value.c_str(), value.size(), wstring_length, &out);
return out;
}
// BOUNDED STRING MEMBER ACCESS ====================================================================
const std::string
DynamicMessage::get_bounded_string_value(
rosidl_dynamic_typesupport_member_id_t id, size_t string_bound)
{
size_t buf_length;
char * buf = nullptr;
rosidl_dynamic_typesupport_dynamic_data_get_bounded_string_value(
&get_rosidl_dynamic_data(), id, &buf, &buf_length, string_bound);
auto out = std::string(buf, buf_length);
delete buf;
return out;
}
const std::string
DynamicMessage::get_bounded_string_value(const std::string & name, size_t string_bound)
{
return get_bounded_string_value(get_member_id(name), string_bound);
}
const std::u16string
DynamicMessage::get_bounded_wstring_value(
rosidl_dynamic_typesupport_member_id_t id, size_t wstring_bound)
{
size_t buf_length;
char16_t * buf = nullptr;
rosidl_dynamic_typesupport_dynamic_data_get_bounded_wstring_value(
&get_rosidl_dynamic_data(), id, &buf, &buf_length, wstring_bound);
auto out = std::u16string(buf, buf_length);
delete buf;
return out;
}
const std::u16string
DynamicMessage::get_bounded_wstring_value(const std::string & name, size_t wstring_bound)
{
return get_bounded_wstring_value(get_member_id(name), wstring_bound);
}
void
DynamicMessage::set_bounded_string_value(
rosidl_dynamic_typesupport_member_id_t id, const std::string value, size_t string_bound)
{
rosidl_dynamic_typesupport_dynamic_data_set_bounded_string_value(
&get_rosidl_dynamic_data(), id, value.c_str(), value.size(), string_bound);
}
void
DynamicMessage::set_bounded_string_value(
const std::string & name, const std::string value, size_t string_bound)
{
set_bounded_string_value(get_member_id(name), value, string_bound);
}
void
DynamicMessage::set_bounded_wstring_value(
rosidl_dynamic_typesupport_member_id_t id, const std::u16string value, size_t wstring_bound)
{
rosidl_dynamic_typesupport_dynamic_data_set_bounded_wstring_value(
&get_rosidl_dynamic_data(), id, value.c_str(), value.size(), wstring_bound);
}
void
DynamicMessage::set_bounded_wstring_value(
const std::string & name, const std::u16string value, size_t wstring_bound)
{
set_bounded_wstring_value(get_member_id(name), value, wstring_bound);
}
rosidl_dynamic_typesupport_member_id_t
DynamicMessage::insert_bounded_string_value(const std::string value, size_t string_bound)
{
rosidl_dynamic_typesupport_member_id_t out;
rosidl_dynamic_typesupport_dynamic_data_insert_bounded_string_value(
&get_rosidl_dynamic_data(), value.c_str(), value.size(), string_bound, &out);
return out;
}
rosidl_dynamic_typesupport_member_id_t
DynamicMessage::insert_bounded_wstring_value(const std::u16string value, size_t wstring_bound)
{
rosidl_dynamic_typesupport_member_id_t out;
rosidl_dynamic_typesupport_dynamic_data_insert_bounded_wstring_value(
&get_rosidl_dynamic_data(), value.c_str(), value.size(), wstring_bound, &out);
return out;
}
// NESTED MEMBER ACCESS ============================================================================
DynamicMessage
DynamicMessage::get_complex_value(
rosidl_dynamic_typesupport_member_id_t id, rcl_allocator_t allocator)
{
rosidl_dynamic_typesupport_dynamic_data_t out =
rosidl_dynamic_typesupport_get_zero_initialized_dynamic_data();
rosidl_dynamic_typesupport_dynamic_data_get_complex_value(
&get_rosidl_dynamic_data(), id, &allocator, &out);
return DynamicMessage(get_shared_dynamic_serialization_support(), std::move(out));
}
DynamicMessage
DynamicMessage::get_complex_value(const std::string & name, rcl_allocator_t allocator)
{
return get_complex_value(get_member_id(name), allocator);
}
DynamicMessage::SharedPtr
DynamicMessage::get_complex_value_shared(
rosidl_dynamic_typesupport_member_id_t id, rcl_allocator_t allocator)
{
rosidl_dynamic_typesupport_dynamic_data_t out =
rosidl_dynamic_typesupport_get_zero_initialized_dynamic_data();
rosidl_dynamic_typesupport_dynamic_data_get_complex_value(
&get_rosidl_dynamic_data(), id, &allocator, &out);
return DynamicMessage::make_shared(get_shared_dynamic_serialization_support(), std::move(out));
}
DynamicMessage::SharedPtr
DynamicMessage::get_complex_value_shared(const std::string & name, rcl_allocator_t allocator)
{
return get_complex_value_shared(get_member_id(name), allocator);
}
void
DynamicMessage::set_complex_value(
rosidl_dynamic_typesupport_member_id_t id, DynamicMessage & value)
{
rosidl_dynamic_typesupport_dynamic_data_set_complex_value(
&get_rosidl_dynamic_data(), id, &value.get_rosidl_dynamic_data());
}
void
DynamicMessage::set_complex_value(const std::string & name, DynamicMessage & value)
{
set_complex_value(get_member_id(name), value);
}
rosidl_dynamic_typesupport_member_id_t
DynamicMessage::insert_complex_value_copy(const DynamicMessage & value)
{
rosidl_dynamic_typesupport_member_id_t out;
rosidl_dynamic_typesupport_dynamic_data_insert_complex_value_copy(
&get_rosidl_dynamic_data(), &value.get_rosidl_dynamic_data(), &out);
return out;
}
rosidl_dynamic_typesupport_member_id_t
DynamicMessage::insert_complex_value(DynamicMessage & value)
{
rosidl_dynamic_typesupport_member_id_t out;
rosidl_dynamic_typesupport_dynamic_data_insert_complex_value(
&get_rosidl_dynamic_data(), &value.get_rosidl_dynamic_data(), &out);
return out;
}

View File

@@ -34,5 +34,248 @@ using rclcpp::dynamic_typesupport::DynamicMessageType;
using rclcpp::dynamic_typesupport::DynamicMessageTypeBuilder;
using rclcpp::dynamic_typesupport::DynamicSerializationSupport;
// CONSTRUCTION ====================================================================================
DynamicMessageType::DynamicMessageType(DynamicMessageTypeBuilder::SharedPtr dynamic_type_builder)
: DynamicMessageType::DynamicMessageType(
dynamic_type_builder, dynamic_type_builder->get_rosidl_dynamic_type_builder().allocator) {}
DynamicMessageType::DynamicMessageType(
DynamicMessageTypeBuilder::SharedPtr dynamic_type_builder,
rcl_allocator_t allocator)
: serialization_support_(dynamic_type_builder->get_shared_dynamic_serialization_support()),
rosidl_dynamic_type_(rosidl_dynamic_typesupport_get_zero_initialized_dynamic_type())
{
if (!serialization_support_) {
throw std::runtime_error("dynamic type could not bind serialization support!");
}
rosidl_dynamic_typesupport_dynamic_type_builder_t rosidl_dynamic_type_builder =
dynamic_type_builder->get_rosidl_dynamic_type_builder();
rcutils_ret_t ret = rosidl_dynamic_typesupport_dynamic_type_init_from_dynamic_type_builder(
&rosidl_dynamic_type_builder, &allocator, &get_rosidl_dynamic_type());
if (ret != RCUTILS_RET_OK) {
throw std::runtime_error("could not init new dynamic type object");
}
}
DynamicMessageType::DynamicMessageType(
DynamicSerializationSupport::SharedPtr serialization_support,
rosidl_dynamic_typesupport_dynamic_type_t && rosidl_dynamic_type)
: serialization_support_(serialization_support),
rosidl_dynamic_type_(std::move(rosidl_dynamic_type))
{
if (serialization_support) {
if (!match_serialization_support_(*serialization_support, rosidl_dynamic_type)) {
throw std::runtime_error(
"serialization support library identifier does not match dynamic type's!");
}
}
}
DynamicMessageType::DynamicMessageType(
DynamicSerializationSupport::SharedPtr serialization_support,
const rosidl_runtime_c__type_description__TypeDescription & description,
rcl_allocator_t allocator)
: serialization_support_(serialization_support),
rosidl_dynamic_type_(rosidl_dynamic_typesupport_get_zero_initialized_dynamic_type())
{
init_from_description(description, allocator, serialization_support);
}
DynamicMessageType::DynamicMessageType(DynamicMessageType && other) noexcept
: serialization_support_(std::exchange(other.serialization_support_, nullptr)),
rosidl_dynamic_type_(std::exchange(
other.rosidl_dynamic_type_, rosidl_dynamic_typesupport_get_zero_initialized_dynamic_type()))
{}
DynamicMessageType &
DynamicMessageType::operator=(DynamicMessageType && other) noexcept
{
std::swap(serialization_support_, other.serialization_support_);
std::swap(rosidl_dynamic_type_, other.rosidl_dynamic_type_);
return *this;
}
DynamicMessageType::~DynamicMessageType()
{} // STUBBED
{
if (rosidl_dynamic_typesupport_dynamic_type_fini(&get_rosidl_dynamic_type()) != RCUTILS_RET_OK) {
RCUTILS_LOG_ERROR("could not fini rosidl dynamic type");
}
}
void
DynamicMessageType::init_from_description(
const rosidl_runtime_c__type_description__TypeDescription & description,
rcl_allocator_t allocator,
DynamicSerializationSupport::SharedPtr serialization_support)
{
if (serialization_support) {
// Swap serialization support if serialization support is given
serialization_support_ = serialization_support;
}
rosidl_dynamic_typesupport_dynamic_type_t rosidl_dynamic_type =
rosidl_dynamic_typesupport_get_zero_initialized_dynamic_type();
rcutils_ret_t ret = rosidl_dynamic_typesupport_dynamic_type_init_from_description(
&serialization_support_->get_rosidl_serialization_support(),
&description,
&allocator,
&rosidl_dynamic_type);
if (ret != RCUTILS_RET_OK) {
throw std::runtime_error("could not init new dynamic type object");
}
rosidl_dynamic_type_ = std::move(rosidl_dynamic_type);
}
bool
DynamicMessageType::match_serialization_support_(
const DynamicSerializationSupport & serialization_support,
const rosidl_dynamic_typesupport_dynamic_type_t & rosidl_dynamic_type)
{
if (serialization_support.get_serialization_library_identifier() != std::string(
rosidl_dynamic_type.serialization_support->serialization_library_identifier))
{
RCUTILS_LOG_ERROR(
"serialization support library identifier does not match dynamic type's (%s vs %s)",
serialization_support.get_serialization_library_identifier().c_str(),
rosidl_dynamic_type.serialization_support->serialization_library_identifier);
return false;
}
return true;
}
// GETTERS =========================================================================================
const std::string
DynamicMessageType::get_serialization_library_identifier() const
{
return std::string(
get_rosidl_dynamic_type().serialization_support->serialization_library_identifier);
}
const std::string
DynamicMessageType::get_name() const
{
size_t buf_length;
const char * buf;
rosidl_dynamic_typesupport_dynamic_type_get_name(&get_rosidl_dynamic_type(), &buf, &buf_length);
return std::string(buf, buf_length);
}
size_t
DynamicMessageType::get_member_count() const
{
size_t out;
rcutils_ret_t ret = rosidl_dynamic_typesupport_dynamic_type_get_member_count(
&get_rosidl_dynamic_type(), &out);
if (ret != RCUTILS_RET_OK) {
throw std::runtime_error(
std::string("could not get member count: ") + rcl_get_error_string().str);
}
return out;
}
rosidl_dynamic_typesupport_dynamic_type_t &
DynamicMessageType::get_rosidl_dynamic_type()
{
return rosidl_dynamic_type_;
}
const rosidl_dynamic_typesupport_dynamic_type_t &
DynamicMessageType::get_rosidl_dynamic_type() const
{
return rosidl_dynamic_type_;
}
DynamicSerializationSupport::SharedPtr
DynamicMessageType::get_shared_dynamic_serialization_support()
{
return serialization_support_;
}
DynamicSerializationSupport::ConstSharedPtr
DynamicMessageType::get_shared_dynamic_serialization_support() const
{
return serialization_support_;
}
// METHODS =========================================================================================
DynamicMessageType
DynamicMessageType::clone(rcl_allocator_t allocator)
{
rosidl_dynamic_typesupport_dynamic_type_t rosidl_dynamic_type =
rosidl_dynamic_typesupport_get_zero_initialized_dynamic_type();
rcutils_ret_t ret = rosidl_dynamic_typesupport_dynamic_type_clone(
&get_rosidl_dynamic_type(), &allocator, &rosidl_dynamic_type);
if (ret != RCUTILS_RET_OK) {
throw std::runtime_error(
std::string("could not clone dynamic type: ") + rcl_get_error_string().str);
}
return DynamicMessageType(
get_shared_dynamic_serialization_support(), std::move(rosidl_dynamic_type));
}
DynamicMessageType::SharedPtr
DynamicMessageType::clone_shared(rcl_allocator_t allocator)
{
rosidl_dynamic_typesupport_dynamic_type_t rosidl_dynamic_type =
rosidl_dynamic_typesupport_get_zero_initialized_dynamic_type();
rcutils_ret_t ret = rosidl_dynamic_typesupport_dynamic_type_clone(
&get_rosidl_dynamic_type(), &allocator, &rosidl_dynamic_type);
if (ret != RCUTILS_RET_OK) {
throw std::runtime_error(
std::string("could not clone dynamic type: ") + rcl_get_error_string().str);
}
return DynamicMessageType::make_shared(
get_shared_dynamic_serialization_support(), std::move(rosidl_dynamic_type));
}
bool
DynamicMessageType::equals(const DynamicMessageType & other) const
{
if (get_serialization_library_identifier() != other.get_serialization_library_identifier()) {
throw std::runtime_error("library identifiers don't match");
}
bool out;
rcutils_ret_t ret = rosidl_dynamic_typesupport_dynamic_type_equals(
&get_rosidl_dynamic_type(), &other.get_rosidl_dynamic_type(), &out);
if (ret != RCUTILS_RET_OK) {
throw std::runtime_error(
std::string("could not equate dynamic message types: ") + rcl_get_error_string().str);
}
return out;
}
DynamicMessage
DynamicMessageType::build_dynamic_message(rcl_allocator_t allocator)
{
return DynamicMessage(shared_from_this(), allocator);
}
DynamicMessage::SharedPtr
DynamicMessageType::build_dynamic_message_shared(rcl_allocator_t allocator)
{
return DynamicMessage::make_shared(shared_from_this(), allocator);
}

View File

@@ -33,5 +33,582 @@ using rclcpp::dynamic_typesupport::DynamicMessageType;
using rclcpp::dynamic_typesupport::DynamicMessageTypeBuilder;
using rclcpp::dynamic_typesupport::DynamicSerializationSupport;
#ifndef RCLCPP__DYNAMIC_TYPESUPPORT__DETAIL__DYNAMIC_MESSAGE_TYPE_BUILDER_IMPL_HPP_
// Template specialization implementations
#include "rclcpp/dynamic_typesupport/detail/dynamic_message_type_builder_impl.hpp"
#endif
// CONSTRUCTION ====================================================================================
DynamicMessageTypeBuilder::DynamicMessageTypeBuilder(
DynamicSerializationSupport::SharedPtr serialization_support, const std::string & name)
: DynamicMessageTypeBuilder::DynamicMessageTypeBuilder(
serialization_support,
name,
serialization_support->get_rosidl_serialization_support().allocator) {}
DynamicMessageTypeBuilder::DynamicMessageTypeBuilder(
DynamicSerializationSupport::SharedPtr serialization_support,
const std::string & name,
rcl_allocator_t allocator)
: serialization_support_(serialization_support),
rosidl_dynamic_type_builder_(
rosidl_dynamic_typesupport_get_zero_initialized_dynamic_type_builder())
{
init_from_serialization_support_(serialization_support, name, allocator);
}
DynamicMessageTypeBuilder::DynamicMessageTypeBuilder(
DynamicSerializationSupport::SharedPtr serialization_support,
rosidl_dynamic_typesupport_dynamic_type_builder_t && rosidl_dynamic_type_builder)
: serialization_support_(serialization_support),
rosidl_dynamic_type_builder_(
rosidl_dynamic_typesupport_get_zero_initialized_dynamic_type_builder())
{
if (!serialization_support) {
throw std::runtime_error("serialization support cannot be nullptr!");
}
if (!match_serialization_support_(*serialization_support, rosidl_dynamic_type_builder)) {
throw std::runtime_error(
"serialization support library does not match dynamic type builder's!");
}
}
DynamicMessageTypeBuilder::DynamicMessageTypeBuilder(
DynamicSerializationSupport::SharedPtr serialization_support,
const rosidl_runtime_c__type_description__TypeDescription & description,
rcl_allocator_t allocator)
: serialization_support_(serialization_support),
rosidl_dynamic_type_builder_(
rosidl_dynamic_typesupport_get_zero_initialized_dynamic_type_builder())
{
if (!serialization_support) {
throw std::runtime_error("serialization support cannot be nullptr!");
}
init_from_description(description, allocator, serialization_support);
}
DynamicMessageTypeBuilder::DynamicMessageTypeBuilder(DynamicMessageTypeBuilder && other) noexcept
: serialization_support_(std::exchange(other.serialization_support_, nullptr)),
rosidl_dynamic_type_builder_(std::exchange(
other.rosidl_dynamic_type_builder_,
rosidl_dynamic_typesupport_get_zero_initialized_dynamic_type_builder())) {}
DynamicMessageTypeBuilder &
DynamicMessageTypeBuilder::operator=(DynamicMessageTypeBuilder && other) noexcept
{
std::swap(serialization_support_, other.serialization_support_);
std::swap(rosidl_dynamic_type_builder_, other.rosidl_dynamic_type_builder_);
return *this;
}
DynamicMessageTypeBuilder::~DynamicMessageTypeBuilder()
{} // STUBBED
{
if (rosidl_dynamic_typesupport_dynamic_type_builder_fini(&get_rosidl_dynamic_type_builder()) !=
RCUTILS_RET_OK)
{
RCUTILS_LOG_ERROR("could not fini rosidl dynamic type builder");
}
}
void
DynamicMessageTypeBuilder::init_from_description(
const rosidl_runtime_c__type_description__TypeDescription & description,
rcl_allocator_t allocator,
DynamicSerializationSupport::SharedPtr serialization_support)
{
if (serialization_support) {
// Swap serialization support if serialization support is given
serialization_support_ = serialization_support;
}
rosidl_dynamic_typesupport_dynamic_type_builder_t rosidl_dynamic_type_builder =
rosidl_dynamic_typesupport_get_zero_initialized_dynamic_type_builder();
rcutils_ret_t ret = rosidl_dynamic_typesupport_dynamic_type_builder_init_from_description(
&serialization_support_->get_rosidl_serialization_support(),
&description,
&allocator,
&rosidl_dynamic_type_builder);
if (ret != RCUTILS_RET_OK) {
throw std::runtime_error("could not init new dynamic type builder object");
}
rosidl_dynamic_type_builder_ = std::move(rosidl_dynamic_type_builder);
}
void
DynamicMessageTypeBuilder::init_from_serialization_support_(
DynamicSerializationSupport::SharedPtr serialization_support,
const std::string & name,
rcl_allocator_t allocator)
{
if (!serialization_support) {
throw std::runtime_error("serialization support cannot be nullptr!");
}
if (!&serialization_support->get_rosidl_serialization_support()) {
throw std::runtime_error("serialization support raw pointer cannot be nullptr!");
}
rosidl_dynamic_typesupport_dynamic_type_builder_t rosidl_dynamic_type_builder =
rosidl_dynamic_typesupport_get_zero_initialized_dynamic_type_builder();
rcutils_ret_t ret = rosidl_dynamic_typesupport_dynamic_type_builder_init(
&serialization_support->get_rosidl_serialization_support(),
name.c_str(), name.size(),
&allocator,
&rosidl_dynamic_type_builder);
if (ret != RCUTILS_RET_OK) {
throw std::runtime_error(
std::string("could not init dynamic type builder: ") + rcl_get_error_string().str);
}
}
bool
DynamicMessageTypeBuilder::match_serialization_support_(
const DynamicSerializationSupport & serialization_support,
const rosidl_dynamic_typesupport_dynamic_type_builder_t & rosidl_dynamic_type_builder)
{
if (serialization_support.get_serialization_library_identifier() != std::string(
rosidl_dynamic_type_builder.serialization_support->serialization_library_identifier))
{
RCUTILS_LOG_ERROR(
"serialization support library identifier does not match dynamic type builder's");
return false;
}
return true;
}
// GETTERS =======================================================================================
const std::string
DynamicMessageTypeBuilder::get_serialization_library_identifier() const
{
return std::string(
get_rosidl_dynamic_type_builder().serialization_support->serialization_library_identifier);
}
const std::string
DynamicMessageTypeBuilder::get_name() const
{
size_t buf_length;
const char * buf;
rosidl_dynamic_typesupport_dynamic_type_builder_get_name(
&get_rosidl_dynamic_type_builder(), &buf, &buf_length);
return std::string(buf, buf_length);
}
rosidl_dynamic_typesupport_dynamic_type_builder_t &
DynamicMessageTypeBuilder::get_rosidl_dynamic_type_builder()
{
return rosidl_dynamic_type_builder_;
}
const rosidl_dynamic_typesupport_dynamic_type_builder_t &
DynamicMessageTypeBuilder::get_rosidl_dynamic_type_builder() const
{
return rosidl_dynamic_type_builder_;
}
DynamicSerializationSupport::SharedPtr
DynamicMessageTypeBuilder::get_shared_dynamic_serialization_support()
{
return serialization_support_;
}
DynamicSerializationSupport::ConstSharedPtr
DynamicMessageTypeBuilder::get_shared_dynamic_serialization_support() const
{
return serialization_support_;
}
// METHODS =======================================================================================
void
DynamicMessageTypeBuilder::set_name(const std::string & name)
{
rosidl_dynamic_typesupport_dynamic_type_builder_set_name(
&get_rosidl_dynamic_type_builder(), name.c_str(), name.size());
}
DynamicMessageTypeBuilder
DynamicMessageTypeBuilder::clone(rcl_allocator_t allocator)
{
rosidl_dynamic_typesupport_dynamic_type_builder_t rosidl_dynamic_type_builder =
rosidl_dynamic_typesupport_get_zero_initialized_dynamic_type_builder();
rcutils_ret_t ret = rosidl_dynamic_typesupport_dynamic_type_builder_clone(
&get_rosidl_dynamic_type_builder(), &allocator, &rosidl_dynamic_type_builder);
if (ret != RCUTILS_RET_OK) {
throw std::runtime_error(
std::string("could not clone dynamic type builder: ") + rcl_get_error_string().str);
}
return DynamicMessageTypeBuilder(
get_shared_dynamic_serialization_support(), std::move(rosidl_dynamic_type_builder));
}
DynamicMessageTypeBuilder::SharedPtr
DynamicMessageTypeBuilder::clone_shared(rcl_allocator_t allocator)
{
rosidl_dynamic_typesupport_dynamic_type_builder_t rosidl_dynamic_type_builder =
rosidl_dynamic_typesupport_get_zero_initialized_dynamic_type_builder();
rcutils_ret_t ret = rosidl_dynamic_typesupport_dynamic_type_builder_clone(
&get_rosidl_dynamic_type_builder(), &allocator, &rosidl_dynamic_type_builder);
if (ret != RCUTILS_RET_OK) {
throw std::runtime_error(
std::string("could not clone dynamic type builder: ") + rcl_get_error_string().str);
}
return DynamicMessageTypeBuilder::make_shared(
get_shared_dynamic_serialization_support(), std::move(rosidl_dynamic_type_builder));
}
void
DynamicMessageTypeBuilder::clear(rcl_allocator_t allocator)
{
if (!serialization_support_) {
throw std::runtime_error(
"cannot call clear() on a dynamic type builder with uninitialized serialization support"
);
}
const std::string & name = get_name();
init_from_serialization_support_(serialization_support_, name, allocator);
}
DynamicMessage
DynamicMessageTypeBuilder::build_dynamic_message(rcl_allocator_t allocator)
{
return DynamicMessage(shared_from_this(), allocator);
}
DynamicMessage::SharedPtr
DynamicMessageTypeBuilder::build_dynamic_message_shared(rcl_allocator_t allocator)
{
return DynamicMessage::make_shared(shared_from_this(), allocator);
}
DynamicMessageType
DynamicMessageTypeBuilder::build_dynamic_message_type(rcl_allocator_t allocator)
{
return DynamicMessageType(shared_from_this(), allocator);
}
DynamicMessageType::SharedPtr
DynamicMessageTypeBuilder::build_dynamic_message_type_shared(rcl_allocator_t allocator)
{
return DynamicMessageType::make_shared(shared_from_this(), allocator);
}
// ADD MEMBERS =====================================================================================
// Defined in "detail/dynamic_message_type_builder_impl.hpp"
// ADD FIXED STRING MEMBERS ========================================================================
void
DynamicMessageTypeBuilder::add_fixed_string_member(
rosidl_dynamic_typesupport_member_id_t id, const std::string & name, size_t string_length,
const std::string & default_value)
{
rosidl_dynamic_typesupport_dynamic_type_builder_add_fixed_string_member(
&get_rosidl_dynamic_type_builder(),
id, name.c_str(), name.size(), default_value.c_str(), default_value.size(),
string_length);
}
void
DynamicMessageTypeBuilder::add_fixed_wstring_member(
rosidl_dynamic_typesupport_member_id_t id, const std::string & name, size_t wstring_length,
const std::string & default_value)
{
rosidl_dynamic_typesupport_dynamic_type_builder_add_fixed_wstring_member(
&get_rosidl_dynamic_type_builder(),
id, name.c_str(), name.size(), default_value.c_str(), default_value.size(),
wstring_length);
}
void
DynamicMessageTypeBuilder::add_fixed_string_array_member(
rosidl_dynamic_typesupport_member_id_t id, const std::string & name,
size_t string_length, size_t array_length, const std::string & default_value)
{
rosidl_dynamic_typesupport_dynamic_type_builder_add_fixed_string_array_member(
&get_rosidl_dynamic_type_builder(),
id, name.c_str(), name.size(), default_value.c_str(), default_value.size(),
string_length, array_length);
}
void
DynamicMessageTypeBuilder::add_fixed_wstring_array_member(
rosidl_dynamic_typesupport_member_id_t id, const std::string & name,
size_t wstring_length, size_t array_length, const std::string & default_value)
{
rosidl_dynamic_typesupport_dynamic_type_builder_add_fixed_wstring_array_member(
&get_rosidl_dynamic_type_builder(),
id, name.c_str(), name.size(), default_value.c_str(), default_value.size(),
wstring_length, array_length);
}
void
DynamicMessageTypeBuilder::add_fixed_string_unbounded_sequence_member(
rosidl_dynamic_typesupport_member_id_t id, const std::string & name, size_t string_length,
const std::string & default_value)
{
rosidl_dynamic_typesupport_dynamic_type_builder_add_fixed_string_unbounded_sequence_member(
&get_rosidl_dynamic_type_builder(),
id, name.c_str(), name.size(), default_value.c_str(), default_value.size(),
string_length);
}
void
DynamicMessageTypeBuilder::add_fixed_wstring_unbounded_sequence_member(
rosidl_dynamic_typesupport_member_id_t id, const std::string & name, size_t wstring_length,
const std::string & default_value)
{
rosidl_dynamic_typesupport_dynamic_type_builder_add_fixed_wstring_unbounded_sequence_member(
&get_rosidl_dynamic_type_builder(),
id, name.c_str(), name.size(), default_value.c_str(), default_value.size(),
wstring_length);
}
void
DynamicMessageTypeBuilder::add_fixed_string_bounded_sequence_member(
rosidl_dynamic_typesupport_member_id_t id, const std::string & name,
size_t string_length, size_t sequence_bound, const std::string & default_value)
{
rosidl_dynamic_typesupport_dynamic_type_builder_add_fixed_string_bounded_sequence_member(
&get_rosidl_dynamic_type_builder(),
id, name.c_str(), name.size(), default_value.c_str(), default_value.size(),
string_length, sequence_bound);
}
void
DynamicMessageTypeBuilder::add_fixed_wstring_bounded_sequence_member(
rosidl_dynamic_typesupport_member_id_t id, const std::string & name,
size_t wstring_length, size_t sequence_bound, const std::string & default_value)
{
rosidl_dynamic_typesupport_dynamic_type_builder_add_fixed_wstring_bounded_sequence_member(
&get_rosidl_dynamic_type_builder(),
id, name.c_str(), name.size(), default_value.c_str(), default_value.size(),
wstring_length, sequence_bound);
}
// ADD BOUNDED STRING MEMBERS ======================================================================
void
DynamicMessageTypeBuilder::add_bounded_string_member(
rosidl_dynamic_typesupport_member_id_t id, const std::string & name, size_t string_bound,
const std::string & default_value)
{
rosidl_dynamic_typesupport_dynamic_type_builder_add_bounded_string_member(
&get_rosidl_dynamic_type_builder(),
id, name.c_str(), name.size(), default_value.c_str(), default_value.size(),
string_bound);
}
void
DynamicMessageTypeBuilder::add_bounded_wstring_member(
rosidl_dynamic_typesupport_member_id_t id, const std::string & name, size_t wstring_bound,
const std::string & default_value)
{
rosidl_dynamic_typesupport_dynamic_type_builder_add_bounded_wstring_member(
&get_rosidl_dynamic_type_builder(),
id, name.c_str(), name.size(), default_value.c_str(), default_value.size(),
wstring_bound);
}
void
DynamicMessageTypeBuilder::add_bounded_string_array_member(
rosidl_dynamic_typesupport_member_id_t id, const std::string & name,
size_t string_bound, size_t array_length, const std::string & default_value)
{
rosidl_dynamic_typesupport_dynamic_type_builder_add_bounded_string_array_member(
&get_rosidl_dynamic_type_builder(),
id, name.c_str(), name.size(), default_value.c_str(), default_value.size(),
string_bound, array_length);
}
void
DynamicMessageTypeBuilder::add_bounded_wstring_array_member(
rosidl_dynamic_typesupport_member_id_t id, const std::string & name,
size_t wstring_bound, size_t array_length, const std::string & default_value)
{
rosidl_dynamic_typesupport_dynamic_type_builder_add_bounded_wstring_array_member(
&get_rosidl_dynamic_type_builder(),
id, name.c_str(), name.size(), default_value.c_str(), default_value.size(),
wstring_bound, array_length);
}
void
DynamicMessageTypeBuilder::add_bounded_string_unbounded_sequence_member(
rosidl_dynamic_typesupport_member_id_t id, const std::string & name, size_t string_bound,
const std::string & default_value)
{
rosidl_dynamic_typesupport_dynamic_type_builder_add_bounded_string_unbounded_sequence_member(
&get_rosidl_dynamic_type_builder(),
id, name.c_str(), name.size(), default_value.c_str(), default_value.size(),
string_bound);
}
void
DynamicMessageTypeBuilder::add_bounded_wstring_unbounded_sequence_member(
rosidl_dynamic_typesupport_member_id_t id, const std::string & name, size_t wstring_bound,
const std::string & default_value)
{
rosidl_dynamic_typesupport_dynamic_type_builder_add_bounded_wstring_unbounded_sequence_member(
&get_rosidl_dynamic_type_builder(),
id, name.c_str(), name.size(), default_value.c_str(), default_value.size(),
wstring_bound);
}
void
DynamicMessageTypeBuilder::add_bounded_string_bounded_sequence_member(
rosidl_dynamic_typesupport_member_id_t id, const std::string & name,
size_t string_bound, size_t sequence_bound, const std::string & default_value)
{
rosidl_dynamic_typesupport_dynamic_type_builder_add_bounded_string_bounded_sequence_member(
&get_rosidl_dynamic_type_builder(),
id, name.c_str(), name.size(), default_value.c_str(), default_value.size(),
string_bound, sequence_bound);
}
void
DynamicMessageTypeBuilder::add_bounded_wstring_bounded_sequence_member(
rosidl_dynamic_typesupport_member_id_t id, const std::string & name,
size_t wstring_bound, size_t sequence_bound, const std::string & default_value)
{
rosidl_dynamic_typesupport_dynamic_type_builder_add_bounded_wstring_bounded_sequence_member(
&get_rosidl_dynamic_type_builder(),
id, name.c_str(), name.size(), default_value.c_str(), default_value.size(),
wstring_bound, sequence_bound);
}
// ADD NESTED MEMBERS ==============================================================================
void
DynamicMessageTypeBuilder::add_complex_member(
rosidl_dynamic_typesupport_member_id_t id, const std::string & name,
DynamicMessageType & nested_type, const std::string & default_value)
{
rosidl_dynamic_typesupport_dynamic_type_builder_add_complex_member(
&get_rosidl_dynamic_type_builder(),
id, name.c_str(), name.size(), default_value.c_str(), default_value.size(),
&nested_type.get_rosidl_dynamic_type());
}
void
DynamicMessageTypeBuilder::add_complex_array_member(
rosidl_dynamic_typesupport_member_id_t id, const std::string & name,
DynamicMessageType & nested_type, size_t array_length, const std::string & default_value)
{
rosidl_dynamic_typesupport_dynamic_type_builder_add_complex_array_member(
&get_rosidl_dynamic_type_builder(),
id, name.c_str(), name.size(), default_value.c_str(), default_value.size(),
&nested_type.get_rosidl_dynamic_type(), array_length);
}
void
DynamicMessageTypeBuilder::add_complex_unbounded_sequence_member(
rosidl_dynamic_typesupport_member_id_t id, const std::string & name,
DynamicMessageType & nested_type, const std::string & default_value)
{
rosidl_dynamic_typesupport_dynamic_type_builder_add_complex_unbounded_sequence_member(
&get_rosidl_dynamic_type_builder(),
id, name.c_str(), name.size(), default_value.c_str(), default_value.size(),
&nested_type.get_rosidl_dynamic_type());
}
void
DynamicMessageTypeBuilder::add_complex_bounded_sequence_member(
rosidl_dynamic_typesupport_member_id_t id, const std::string & name,
DynamicMessageType & nested_type, size_t sequence_bound, const std::string & default_value)
{
rosidl_dynamic_typesupport_dynamic_type_builder_add_complex_bounded_sequence_member(
&get_rosidl_dynamic_type_builder(),
id, name.c_str(), name.size(), default_value.c_str(), default_value.size(),
&nested_type.get_rosidl_dynamic_type(), sequence_bound);
}
void
DynamicMessageTypeBuilder::add_complex_member_builder(
rosidl_dynamic_typesupport_member_id_t id, const std::string & name,
DynamicMessageTypeBuilder & nested_type_builder, const std::string & default_value)
{
rosidl_dynamic_typesupport_dynamic_type_builder_add_complex_member_builder(
&get_rosidl_dynamic_type_builder(),
id, name.c_str(), name.size(), default_value.c_str(), default_value.size(),
&nested_type_builder.get_rosidl_dynamic_type_builder());
}
void
DynamicMessageTypeBuilder::add_complex_array_member_builder(
rosidl_dynamic_typesupport_member_id_t id, const std::string & name,
DynamicMessageTypeBuilder & nested_type_builder, size_t array_length,
const std::string & default_value)
{
rosidl_dynamic_typesupport_dynamic_type_builder_add_complex_array_member_builder(
&get_rosidl_dynamic_type_builder(),
id, name.c_str(), name.size(), default_value.c_str(), default_value.size(),
&nested_type_builder.get_rosidl_dynamic_type_builder(), array_length);
}
void
DynamicMessageTypeBuilder::add_complex_unbounded_sequence_member_builder(
rosidl_dynamic_typesupport_member_id_t id, const std::string & name,
DynamicMessageTypeBuilder & nested_type_builder, const std::string & default_value)
{
rosidl_dynamic_typesupport_dynamic_type_builder_add_complex_unbounded_sequence_member_builder(
&get_rosidl_dynamic_type_builder(),
id, name.c_str(), name.size(), default_value.c_str(), default_value.size(),
&nested_type_builder.get_rosidl_dynamic_type_builder());
}
void
DynamicMessageTypeBuilder::add_complex_bounded_sequence_member_builder(
rosidl_dynamic_typesupport_member_id_t id, const std::string & name,
DynamicMessageTypeBuilder & nested_type_builder, size_t sequence_bound,
const std::string & default_value)
{
rosidl_dynamic_typesupport_dynamic_type_builder_add_complex_bounded_sequence_member_builder(
&get_rosidl_dynamic_type_builder(),
id, name.c_str(), name.size(), default_value.c_str(), default_value.size(),
&nested_type_builder.get_rosidl_dynamic_type_builder(), sequence_bound);
}

View File

@@ -45,5 +45,273 @@ using rclcpp::dynamic_typesupport::DynamicMessageType;
using rclcpp::dynamic_typesupport::DynamicMessageTypeSupport;
using rclcpp::dynamic_typesupport::DynamicSerializationSupport;
// CONSTRUCTION ====================================================================================
DynamicMessageTypeSupport::DynamicMessageTypeSupport(
const rosidl_runtime_c__type_description__TypeDescription & description,
const std::string & serialization_library_name,
rcl_allocator_t allocator)
: serialization_support_(nullptr),
dynamic_message_type_(nullptr),
dynamic_message_(nullptr),
rosidl_message_type_support_(rosidl_get_zero_initialized_message_type_support_handle())
{
rcl_ret_t ret;
if (serialization_library_name.empty()) {
ret = rcl_dynamic_message_type_support_handle_init(
nullptr, &description, &allocator, &rosidl_message_type_support_);
} else {
ret = rcl_dynamic_message_type_support_handle_init(
serialization_library_name.c_str(), &description, &allocator, &rosidl_message_type_support_);
}
if (ret != RCL_RET_OK) {
std::string error_msg =
std::string("error initializing rosidl message type support: ") + rcl_get_error_string().str;
rcl_reset_error();
throw std::runtime_error(error_msg);
}
if (rosidl_message_type_support_.typesupport_identifier !=
rosidl_get_dynamic_typesupport_identifier())
{
throw std::runtime_error("rosidl message type support is of the wrong type");
}
auto ts_impl = static_cast<rosidl_dynamic_message_type_support_impl_t *>(const_cast<void *>(
rosidl_message_type_support_.data));
serialization_support_ = DynamicSerializationSupport::make_shared(
std::move(ts_impl->serialization_support));
dynamic_message_type_ = DynamicMessageType::make_shared(
get_shared_dynamic_serialization_support(), std::move(*ts_impl->dynamic_message_type));
dynamic_message_ = DynamicMessage::make_shared(
get_shared_dynamic_serialization_support(), std::move(*ts_impl->dynamic_message));
}
DynamicMessageTypeSupport::DynamicMessageTypeSupport(
DynamicSerializationSupport::SharedPtr serialization_support,
const rosidl_runtime_c__type_description__TypeDescription & description,
rcl_allocator_t allocator)
: serialization_support_(serialization_support),
dynamic_message_type_(nullptr),
dynamic_message_(nullptr),
rosidl_message_type_support_(rosidl_get_zero_initialized_message_type_support_handle())
{
// Check null
if (!serialization_support) {
throw std::runtime_error("serialization_support cannot be nullptr.");
}
rosidl_type_hash_t type_hash;
rcutils_ret_t hash_ret = rcl_calculate_type_hash(
// TODO(methylDragon): Swap this out with the conversion function when it is ready
reinterpret_cast<const type_description_interfaces__msg__TypeDescription *>(&description),
&type_hash);
if (hash_ret != RCL_RET_OK) {
throw std::runtime_error("failed to get type hash");
}
rcutils_ret_t ret = rosidl_dynamic_message_type_support_handle_init(
&serialization_support->get_rosidl_serialization_support(),
&type_hash, // type_hash
&description, // type_description
nullptr, // type_description_sources (not implemented for dynamic types)
&allocator,
&rosidl_message_type_support_);
if (ret != RCUTILS_RET_OK) {
throw std::runtime_error("could not init rosidl message type support");
}
if (rosidl_message_type_support_.typesupport_identifier !=
rosidl_get_dynamic_typesupport_identifier())
{
throw std::runtime_error("rosidl message type support is of the wrong type");
}
auto ts_impl = static_cast<const rosidl_dynamic_message_type_support_impl_t *>(
rosidl_message_type_support_.data);
dynamic_message_type_ = DynamicMessageType::make_shared(
get_shared_dynamic_serialization_support(), std::move(*ts_impl->dynamic_message_type));
dynamic_message_ = DynamicMessage::make_shared(
get_shared_dynamic_serialization_support(), std::move(*ts_impl->dynamic_message));
}
DynamicMessageTypeSupport::DynamicMessageTypeSupport(
DynamicSerializationSupport::SharedPtr serialization_support,
DynamicMessageType::SharedPtr dynamic_message_type,
DynamicMessage::SharedPtr dynamic_message,
const rosidl_runtime_c__type_description__TypeDescription & description,
rcl_allocator_t allocator)
: serialization_support_(serialization_support),
dynamic_message_type_(dynamic_message_type),
dynamic_message_(dynamic_message),
rosidl_message_type_support_(rosidl_get_zero_initialized_message_type_support_handle())
{
// Check null
if (!serialization_support) {
throw std::runtime_error("serialization_support cannot be nullptr.");
}
if (!dynamic_message_type) {
throw std::runtime_error("dynamic_message_type cannot be nullptr.");
}
if (!dynamic_message) {
throw std::runtime_error("dynamic_message cannot be nullptr.");
}
// Check identifiers
if (serialization_support->get_serialization_library_identifier() !=
dynamic_message_type->get_serialization_library_identifier())
{
throw std::runtime_error(
"serialization support library identifier does not match "
"dynamic message type library identifier.");
}
if (dynamic_message_type->get_serialization_library_identifier() !=
dynamic_message->get_serialization_library_identifier())
{
throw std::runtime_error(
"dynamic message type library identifier does not match "
"dynamic message library identifier.");
}
rosidl_type_hash_t type_hash;
rcutils_ret_t hash_ret = rcl_calculate_type_hash(
// TODO(methylDragon): Swap this out with the conversion function when it is ready
// from https://github.com/ros2/rcl/pull/1052
reinterpret_cast<const type_description_interfaces__msg__TypeDescription *>(&description),
&type_hash);
if (hash_ret != RCL_RET_OK) {
std::string error_msg = std::string("failed to get type hash: ") + rcl_get_error_string().str;
rcl_reset_error();
throw std::runtime_error(error_msg);
}
auto ts_impl = static_cast<rosidl_dynamic_message_type_support_impl_t *>(
allocator.zero_allocate(1, sizeof(rosidl_dynamic_message_type_support_impl_t), allocator.state)
);
if (!ts_impl) {
throw std::runtime_error("could not allocate rosidl_message_type_support_t");
}
ts_impl->allocator = allocator;
ts_impl->type_hash = type_hash;
if (!rosidl_runtime_c__type_description__TypeDescription__copy(
&description, &ts_impl->type_description))
{
throw std::runtime_error("could not copy type description");
}
// ts_impl->type_description_sources = // Not used
ts_impl->serialization_support = serialization_support->get_rosidl_serialization_support();
ts_impl->dynamic_message_type = &dynamic_message_type->get_rosidl_dynamic_type();
ts_impl->dynamic_message = &dynamic_message->get_rosidl_dynamic_data();
rosidl_message_type_support_ = {
rosidl_get_dynamic_typesupport_identifier(), // typesupport_identifier
ts_impl, // data
get_message_typesupport_handle_function, // func
// get_type_hash_func
rosidl_get_dynamic_message_type_support_type_hash_function,
// get_type_description_func
rosidl_get_dynamic_message_type_support_type_description_function,
// get_type_description_sources_func
rosidl_get_dynamic_message_type_support_type_description_sources_function
};
}
DynamicMessageTypeSupport::~DynamicMessageTypeSupport()
{} // STUBBED
{
// These must go first
serialization_support_.reset();
dynamic_message_type_.reset();
dynamic_message_.reset();
// Early return if type support isn't populated to avoid segfaults
if (!rosidl_message_type_support_.data) {
return;
}
// We only partially finalize the rosidl_message_type_support->data since its pointer members are
// managed by their respective SharedPtr wrapper classes.
auto ts_impl = static_cast<rosidl_dynamic_message_type_support_impl_t *>(
const_cast<void *>(rosidl_message_type_support_.data)
);
rcutils_allocator_t allocator = ts_impl->allocator;
rosidl_runtime_c__type_description__TypeDescription__fini(&ts_impl->type_description);
rosidl_runtime_c__type_description__TypeSource__Sequence__fini(
&ts_impl->type_description_sources);
allocator.deallocate(static_cast<void *>(ts_impl), allocator.state); // Always C allocated
}
// GETTERS =========================================================================================
const std::string
DynamicMessageTypeSupport::get_serialization_library_identifier() const
{
return serialization_support_->get_serialization_library_identifier();
}
rosidl_message_type_support_t &
DynamicMessageTypeSupport::get_rosidl_message_type_support()
{
return rosidl_message_type_support_;
}
const rosidl_message_type_support_t &
DynamicMessageTypeSupport::get_const_rosidl_message_type_support()
{
return rosidl_message_type_support_;
}
const rosidl_message_type_support_t &
DynamicMessageTypeSupport::get_const_rosidl_message_type_support() const
{
return rosidl_message_type_support_;
}
const rosidl_runtime_c__type_description__TypeDescription &
DynamicMessageTypeSupport::get_rosidl_runtime_c_type_description() const
{
auto ts_impl = static_cast<const rosidl_dynamic_message_type_support_impl_t *>(
get_const_rosidl_message_type_support().data
);
return ts_impl->type_description;
}
DynamicSerializationSupport::SharedPtr
DynamicMessageTypeSupport::get_shared_dynamic_serialization_support()
{
return serialization_support_;
}
DynamicSerializationSupport::ConstSharedPtr
DynamicMessageTypeSupport::get_shared_dynamic_serialization_support() const
{
return serialization_support_;
}
DynamicMessageType::SharedPtr
DynamicMessageTypeSupport::get_shared_dynamic_message_type()
{
return dynamic_message_type_;
}
DynamicMessageType::ConstSharedPtr
DynamicMessageTypeSupport::get_shared_dynamic_message_type() const
{
return dynamic_message_type_;
}
DynamicMessage::SharedPtr
DynamicMessageTypeSupport::get_shared_dynamic_message()
{
return dynamic_message_;
}
DynamicMessage::ConstSharedPtr
DynamicMessageTypeSupport::get_shared_dynamic_message() const
{
return dynamic_message_;
}

View File

@@ -28,19 +28,71 @@ using rclcpp::dynamic_typesupport::DynamicSerializationSupport;
// CONSTRUCTION ====================================================================================
DynamicSerializationSupport::DynamicSerializationSupport(rcl_allocator_t allocator)
: DynamicSerializationSupport::DynamicSerializationSupport("", allocator)
{
throw std::runtime_error("Unimplemented");
}
: DynamicSerializationSupport::DynamicSerializationSupport("", allocator) {}
DynamicSerializationSupport::DynamicSerializationSupport(
const std::string & /*serialization_library_name*/,
rcl_allocator_t /*allocator*/)
const std::string & serialization_library_name,
rcl_allocator_t allocator)
: rosidl_serialization_support_(
rosidl_dynamic_typesupport_get_zero_initialized_serialization_support())
{
throw std::runtime_error("Unimplemented");
rmw_ret_t ret = RMW_RET_ERROR;
if (serialization_library_name.empty()) {
ret = rmw_serialization_support_init(NULL, &allocator, &rosidl_serialization_support_);
} else {
ret = rmw_serialization_support_init(
serialization_library_name.c_str(), &allocator, &rosidl_serialization_support_);
}
if (ret != RCL_RET_OK) {
std::string error_msg =
std::string("could not initialize new serialization support object: ") +
rcl_get_error_string().str;
rcl_reset_error();
throw std::runtime_error(error_msg);
}
}
DynamicSerializationSupport::DynamicSerializationSupport(
rosidl_dynamic_typesupport_serialization_support_t && rosidl_serialization_support)
: rosidl_serialization_support_(std::move(rosidl_serialization_support)) {}
DynamicSerializationSupport::DynamicSerializationSupport(
DynamicSerializationSupport && other) noexcept
: rosidl_serialization_support_(std::exchange(
other.rosidl_serialization_support_,
rosidl_dynamic_typesupport_get_zero_initialized_serialization_support())) {}
DynamicSerializationSupport &
DynamicSerializationSupport::operator=(DynamicSerializationSupport && other) noexcept
{
std::swap(rosidl_serialization_support_, other.rosidl_serialization_support_);
return *this;
}
DynamicSerializationSupport::~DynamicSerializationSupport()
{} // STUBBED
{
rosidl_dynamic_typesupport_serialization_support_fini(&rosidl_serialization_support_);
}
// GETTERS =========================================================================================
const std::string
DynamicSerializationSupport::get_serialization_library_identifier() const
{
return std::string(
rosidl_dynamic_typesupport_serialization_support_get_library_identifier(
&rosidl_serialization_support_));
}
rosidl_dynamic_typesupport_serialization_support_t &
DynamicSerializationSupport::get_rosidl_serialization_support()
{
return rosidl_serialization_support_;
}
const rosidl_dynamic_typesupport_serialization_support_t &
DynamicSerializationSupport::get_rosidl_serialization_support() const
{
return rosidl_serialization_support_;
}

View File

@@ -13,8 +13,6 @@
// limitations under the License.
#include <algorithm>
#include <chrono>
#include <iterator>
#include <memory>
#include <map>
#include <string>
@@ -24,14 +22,13 @@
#include "rcl/allocator.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 "rclcpp/dynamic_typesupport/dynamic_message.hpp"
#include "rclcpp/exceptions.hpp"
#include "rclcpp/executor.hpp"
#include "rclcpp/guard_condition.hpp"
#include "rclcpp/memory_strategy.hpp"
#include "rclcpp/node.hpp"
#include "rclcpp/utilities.hpp"
@@ -41,25 +38,17 @@
using namespace std::chrono_literals;
using rclcpp::exceptions::throw_from_rcl_error;
using rclcpp::AnyExecutable;
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};
using rclcpp::ExecutorOptions;
using rclcpp::FutureReturnCode;
Executor::Executor(const rclcpp::ExecutorOptions & options)
: spinning(false),
interrupt_guard_condition_(std::make_shared<rclcpp::GuardCondition>(options.context)),
interrupt_guard_condition_(options.context),
shutdown_guard_condition_(std::make_shared<rclcpp::GuardCondition>(options.context)),
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>())
memory_strategy_(options.memory_strategy)
{
// Store the context for later use.
context_ = options.context;
@@ -72,56 +61,74 @@ Executor::Executor(const rclcpp::ExecutorOptions & options)
}
});
notify_waitable_->add_guard_condition(interrupt_guard_condition_);
notify_waitable_->add_guard_condition(shutdown_guard_condition_);
// The number of guard conditions is always at least 2: 1 for the ctrl-c guard cond,
// and one for the executor's guard cond (interrupt_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_);
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()
{
std::lock_guard<std::mutex> guard(mutex_);
notify_waitable_->remove_guard_condition(interrupt_guard_condition_);
notify_waitable_->remove_guard_condition(shutdown_guard_condition_);
std::cout << "Executor::~Executor" << std::endl;
current_collection_.timers.update(
{}, {},
[this](auto timer) {
std::cout << "remove_timer(" << timer.get() << ")" << std::endl;
wait_set_.remove_timer(timer);
// Disassociate all callback groups.
for (auto & pair : weak_groups_to_nodes_) {
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.
std::for_each(
weak_nodes_.begin(), weak_nodes_.end(), []
(rclcpp::node_interfaces::NodeBaseInterface::WeakPtr weak_node_ptr) {
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();
current_collection_.subscriptions.update(
{}, {},
[this](auto subscription) {
std::cout << "remove_subscription(" << subscription.get() << ")" << std::endl;
wait_set_.remove_subscription(subscription, kDefaultSubscriptionMask);
});
for (const auto & pair : weak_nodes_to_guard_conditions_) {
auto guard_condition = pair.second;
memory_strategy_->remove_guard_condition(guard_condition);
}
weak_nodes_to_guard_conditions_.clear();
current_collection_.clients.update(
{}, {},
[this](auto client) {
std::cout << "remove_client(" << client.get() << ")" << std::endl;
wait_set_.remove_client(client);});
current_collection_.services.update(
{}, {},
[this](auto service) {
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);});
// Finalize the wait set.
if (rcl_wait_set_fini(&wait_set_) != RCL_RET_OK) {
RCUTILS_LOG_ERROR_NAMED(
"rclcpp",
"failed to destroy wait set: %s", rcl_get_error_string().str);
rcl_reset_error();
}
// Remove and release the sigint guard condition
memory_strategy_->remove_guard_condition(shutdown_guard_condition_.get());
memory_strategy_->remove_guard_condition(&interrupt_guard_condition_);
// Remove shutdown callback handle registered to Context
if (!context_->remove_on_shutdown_callback(shutdown_callback_handle_)) {
@@ -135,41 +142,98 @@ Executor::~Executor()
std::vector<rclcpp::CallbackGroup::WeakPtr>
Executor::get_all_callback_groups()
{
this->collector_.update_collections();
return this->collector_.get_all_callback_groups();
std::vector<rclcpp::CallbackGroup::WeakPtr> groups;
std::lock_guard<std::mutex> guard{mutex_};
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>
Executor::get_manually_added_callback_groups()
{
this->collector_.update_collections();
return this->collector_.get_manually_added_callback_groups();
std::vector<rclcpp::CallbackGroup::WeakPtr> groups;
std::lock_guard<std::mutex> guard{mutex_};
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>
Executor::get_automatically_added_callback_groups_from_nodes()
{
this->collector_.update_collections();
return this->collector_.get_automatically_added_callback_groups();
std::vector<rclcpp::CallbackGroup::WeakPtr> groups;
std::lock_guard<std::mutex> guard{mutex_};
for (auto const & group_node_ptr : weak_groups_to_nodes_associated_with_executor_) {
groups.push_back(group_node_ptr.first);
}
return groups;
}
void
Executor::add_callback_group(
Executor::add_callback_groups_from_nodes_associated_to_executor()
{
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::node_interfaces::NodeBaseInterface::SharedPtr node_ptr,
rclcpp::memory_strategy::MemoryStrategy::WeakCallbackGroupsToNodesMap & weak_groups_to_nodes,
bool notify)
{
(void) node_ptr;
this->collector_.add_callback_group(group_ptr);
// 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.");
}
if (!spinning.load()) {
std::lock_guard<std::mutex> guard(mutex_);
this->collect_entities();
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.");
}
// 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(node_ptr->get_context());
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) {
// Interrupt waiting to handle new node
try {
interrupt_guard_condition_->trigger();
interrupt_guard_condition_.trigger();
} catch (const rclcpp::exceptions::RCLError & ex) {
throw std::runtime_error(
std::string(
@@ -178,23 +242,91 @@ Executor::add_callback_group(
}
}
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
Executor::add_node(rclcpp::node_interfaces::NodeBaseInterface::SharedPtr node_ptr, bool notify)
{
this->collector_.add_node(node_ptr);
if (!spinning.load()) {
std::lock_guard<std::mutex> guard(mutex_);
this->collect_entities();
// 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(
std::string("Node '") + node_ptr->get_fully_qualified_name() +
"' 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);
}
});
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 node add: ") + ex.what());
const auto & gc = node_ptr->get_notify_guard_condition();
weak_nodes_to_guard_conditions_[node_ptr] = &gc;
// Add the node's notify condition to the guard condition handles
memory_strategy_->add_guard_condition(gc);
weak_nodes_.push_back(node_ptr);
}
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());
}
}
}
}
@@ -204,21 +336,11 @@ Executor::remove_callback_group(
rclcpp::CallbackGroup::SharedPtr group_ptr,
bool notify)
{
this->collector_.remove_callback_group(group_ptr);
if (!spinning.load()) {
std::lock_guard<std::mutex> guard(mutex_);
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());
}
}
std::lock_guard<std::mutex> guard{mutex_};
this->remove_callback_group_from_map(
group_ptr,
weak_groups_associated_with_executor_to_nodes_,
notify);
}
void
@@ -230,22 +352,48 @@ Executor::add_node(std::shared_ptr<rclcpp::Node> node_ptr, bool notify)
void
Executor::remove_node(rclcpp::node_interfaces::NodeBaseInterface::SharedPtr node_ptr, bool notify)
{
this->collector_.remove_node(node_ptr);
if (!spinning.load()) {
std::lock_guard<std::mutex> guard(mutex_);
this->collect_entities();
if (!node_ptr->get_associated_with_executor_atomic().load()) {
throw std::runtime_error("Node needs to be associated with an executor.");
}
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 node remove: ") + ex.what());
std::lock_guard<std::mutex> guard{mutex_};
bool found_node = false;
auto node_it = weak_nodes_.begin();
while (node_it != weak_nodes_.end()) {
bool matched = (node_it->lock() == node_ptr);
if (matched) {
found_node = true;
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_notify_guard_condition());
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
@@ -312,44 +460,21 @@ Executor::spin_some_impl(std::chrono::nanoseconds max_duration, bool exhaustive)
throw std::runtime_error("spin_some() called while already spinning");
}
RCPPUTILS_SCOPE_EXIT(this->spinning.store(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
bool work_available = false;
while (rclcpp::ok(context_) && spinning.load() && max_duration_not_elapsed()) {
AnyExecutable any_exec;
if (work_in_queue > 0) {
// If there is work in the queue, then execute it
// This covers the case that there are things left in the queue from a
// previous spin.
if (get_next_ready_executable(any_exec)) {
execute_any_executable(any_exec);
}
} else if (!has_waited && !work_in_queue) {
// 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;
}
if (!work_available) {
wait_for_work(std::chrono::milliseconds::zero());
}
if (get_next_ready_executable(any_exec)) {
execute_any_executable(any_exec);
work_available = true;
} else {
if (!work_available || !exhaustive) {
break;
}
work_available = false;
}
std::lock_guard<std::mutex> lock(mutex_);
work_in_queue = ready_executables_.size();
}
}
@@ -377,20 +502,29 @@ Executor::cancel()
{
spinning.store(false);
try {
interrupt_guard_condition_->trigger();
interrupt_guard_condition_.trigger();
} catch (const rclcpp::exceptions::RCLError & ex) {
throw std::runtime_error(
std::string("Failed to trigger guard condition in cancel: ") + ex.what());
}
}
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
Executor::execute_any_executable(AnyExecutable & any_exec)
{
if (!spinning.load()) {
return;
}
if (any_exec.timer) {
TRACEPOINT(
rclcpp_executor_execute,
@@ -412,10 +546,16 @@ Executor::execute_any_executable(AnyExecutable & any_exec)
if (any_exec.waitable) {
any_exec.waitable->execute(any_exec.data);
}
// Reset the callback_group, regardless of type
if (any_exec.callback_group) {
any_exec.callback_group->can_be_taken_from().store(true);
any_exec.callback_group->can_be_taken_from().store(true);
// Wake the wait, because it may need to be recalculated or work that
// 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());
}
}
@@ -465,8 +605,8 @@ Executor::execute_subscription(rclcpp::SubscriptionBase::SharedPtr subscription)
message_info.get_rmw_message_info().from_intra_process = false;
switch (subscription->get_subscription_type()) {
// Deliver ROS message
case rclcpp::DeliveredMessageKind::ROS_MESSAGE:
// Take ROS message
case rclcpp::SubscriptionType::ROS_MESSAGE:
{
if (subscription->can_loan_messages()) {
// This is the case where a loaned message is taken from the middleware via
@@ -519,8 +659,8 @@ Executor::execute_subscription(rclcpp::SubscriptionBase::SharedPtr subscription)
break;
}
// Deliver serialized message
case rclcpp::DeliveredMessageKind::SERIALIZED_MESSAGE:
// Take serialized message
case rclcpp::SubscriptionType::SERIALIZED_MESSAGE:
{
// This is the case where a copy of the serialized message is taken from
// the middleware via inter-process communication.
@@ -539,15 +679,53 @@ Executor::execute_subscription(rclcpp::SubscriptionBase::SharedPtr subscription)
}
// DYNAMIC SUBSCRIPTION ========================================================================
// Deliver dynamic message
case rclcpp::DeliveredMessageKind::DYNAMIC_MESSAGE:
// If a subscription is dynamic, then it will use its serialization-specific dynamic data.
//
// Two cases:
// - Dynamic type subscription using dynamic type stored in its own internal type support struct
// - Non-dynamic type subscription with no stored dynamic type
// - Subscriptions of this type must be able to lookup the local message description to
// generate a dynamic type at runtime!
// - TODO(methylDragon): I won't be handling this case yet
// Take dynamic message directly from the middleware
case rclcpp::SubscriptionType::DYNAMIC_MESSAGE_DIRECT:
{
throw std::runtime_error("Unimplemented");
DynamicMessage::SharedPtr dynamic_message = subscription->create_dynamic_message();
take_and_do_error_handling(
"taking a dynamic message from topic",
subscription->get_topic_name(),
// This modifies the stored dynamic data in the DynamicMessage in-place
[&]() {return subscription->take_dynamic_message(*dynamic_message, message_info);},
[&]() {subscription->handle_dynamic_message(dynamic_message, message_info);});
subscription->return_dynamic_message(dynamic_message);
break;
}
default:
// Take serialized and then convert to dynamic message
case rclcpp::SubscriptionType::DYNAMIC_MESSAGE_FROM_SERIALIZED:
{
throw std::runtime_error("Delivered message kind is not supported");
std::shared_ptr<SerializedMessage> serialized_msg =
subscription->create_serialized_message();
// NOTE(methylDragon): Is this clone necessary? If I'm following the pattern, it seems so.
DynamicMessage::SharedPtr dynamic_message = subscription->create_dynamic_message();
take_and_do_error_handling(
"taking a serialized message from topic",
subscription->get_topic_name(),
[&]() {return subscription->take_serialized(*serialized_msg.get(), message_info);},
[&]()
{
bool ret = dynamic_message->deserialize(serialized_msg->get_rcl_serialized_message());
if (!ret) {
throw_from_rcl_error(ret, "Couldn't convert serialized message to dynamic data!");
}
subscription->handle_dynamic_message(dynamic_message, message_info);
}
);
subscription->return_serialized_message(serialized_msg);
subscription->return_dynamic_message(dynamic_message);
break;
}
}
return;
@@ -584,117 +762,228 @@ Executor::execute_client(
[&]() {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
Executor::wait_for_work(std::chrono::nanoseconds timeout)
{
TRACEPOINT(rclcpp_executor_wait_for_work, timeout.count());
{
std::lock_guard<std::mutex> guard(mutex_);
std::lock_guard<std::mutex> guard(mutex_);
if (current_collection_.empty() || this->entities_need_rebuild_.load()) {
this->collect_entities();
// Check weak_nodes_ to find any callback group that is not owned
// by an executor and add it to the list of callbackgroups for
// collect entities. Also exchange to false so it is not
// 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");
}
}
auto wait_result = wait_set_.wait(timeout);
if (wait_result.kind() == WaitResultKind::Empty) {
rcl_ret_t status =
rcl_wait(&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 wait(). This should never happen.");
"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");
}
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
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);
std::lock_guard<std::mutex> guard(mutex_);
if (ready_executables_.size() == 0) {
return false;
bool success = false;
std::lock_guard<std::mutex> guard{mutex_};
// Check the timers to see if there are any that are ready
memory_strategy_->get_next_timer(any_executable, weak_groups_to_nodes);
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;
}
}
any_executable = ready_executables_.front();
ready_executables_.pop_front();
if (any_executable.callback_group &&
any_executable.callback_group->type() == CallbackGroupType::MutuallyExclusive)
{
assert(any_executable.callback_group->can_be_taken_from().load());
any_executable.callback_group->can_be_taken_from().store(false);
if (success) {
// If it is valid, check to see if the group is mutually exclusive or
// not, then mark it accordingly ..Check if the callback_group belongs to this executor
if (any_executable.callback_group && any_executable.callback_group->type() == \
CallbackGroupType::MutuallyExclusive)
{
// It should not have been taken otherwise
assert(any_executable.callback_group->can_be_taken_from().load());
// 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);
}
}
return true;
// If there is no ready executable, return false
return success;
}
bool
@@ -717,6 +1006,22 @@ Executor::get_next_executable(AnyExecutable & any_executable, std::chrono::nanos
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
Executor::is_spinning()
{

View File

@@ -1,228 +0,0 @@
// Copyright 2023 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/executor_entities_collection.hpp"
namespace rclcpp
{
namespace executors
{
bool ExecutorEntitiesCollection::empty() const
{
return
subscriptions.empty() &&
timers.empty() &&
guard_conditions.empty() &&
clients.empty() &&
services.empty() &&
waitables.empty();
}
void ExecutorEntitiesCollection::clear()
{
subscriptions.clear();
timers.clear();
guard_conditions.clear();
clients.clear();
services.clear();
waitables.clear();
}
void
build_entities_collection(
const std::vector<rclcpp::CallbackGroup::WeakPtr> & callback_groups,
ExecutorEntitiesCollection & collection)
{
collection.clear();
for (auto weak_group_ptr : callback_groups) {
auto group_ptr = weak_group_ptr.lock();
if (!group_ptr) {
continue;
}
if (group_ptr->can_be_taken_from().load()) {
group_ptr->collect_all_ptrs(
[&collection, weak_group_ptr](const rclcpp::SubscriptionBase::SharedPtr & subscription) {
collection.subscriptions.insert(
{
subscription->get_subscription_handle().get(),
{subscription, weak_group_ptr}
});
},
[&collection, weak_group_ptr](const rclcpp::ServiceBase::SharedPtr & service) {
collection.services.insert(
{
service->get_service_handle().get(),
{service, weak_group_ptr}
});
},
[&collection, weak_group_ptr](const rclcpp::ClientBase::SharedPtr & client) {
collection.clients.insert(
{
client->get_client_handle().get(),
{client, weak_group_ptr}
});
},
[&collection, weak_group_ptr](const rclcpp::TimerBase::SharedPtr & timer) {
collection.timers.insert(
{
timer->get_timer_handle().get(),
{timer, weak_group_ptr}
});
},
[&collection, weak_group_ptr](const rclcpp::Waitable::SharedPtr & waitable) {
collection.waitables.insert(
{
waitable.get(),
{waitable, weak_group_ptr}
});
}
);
}
}
}
size_t
ready_executables(
const ExecutorEntitiesCollection & collection,
rclcpp::WaitResult<rclcpp::WaitSet> & wait_result,
std::deque<rclcpp::AnyExecutable> & executables
)
{
size_t added = 0;
if (wait_result.kind() != rclcpp::WaitResultKind::Ready) {
return added;
}
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();
if (!entity) {
continue;
}
auto group_info = group_cache(entity_iter->second.callback_group);
if (group_info && !group_info->can_be_taken_from().load()) {
continue;
}
if (!entity->call()) {
continue;
}
rclcpp::AnyExecutable exec;
exec.timer = entity;
exec.callback_group = group_info;
executables.push_back(exec);
added++;
}
}
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;
}
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.subscription = entity;
exec.callback_group = group_info;
executables.push_back(exec);
added++;
}
}
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;
}
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) {
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;
}
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.client = entity;
exec.callback_group = group_info;
executables.push_back(exec);
added++;
}
}
for (auto & [handle, entry] : collection.waitables) {
auto waitable = entry.entity.lock();
if (!waitable) {
continue;
}
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 added;
}
} // namespace executors
} // namespace rclcpp

View File

@@ -1,420 +0,0 @@
// Copyright 2023 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 <set>
#include "rclcpp/executors/executor_entities_collector.hpp"
#include "rclcpp/executors/executor_notify_waitable.hpp"
#include "rclcpp/node_interfaces/node_base_interface.hpp"
namespace rclcpp
{
namespace executors
{
ExecutorEntitiesCollector::ExecutorEntitiesCollector(
std::shared_ptr<ExecutorNotifyWaitable> notify_waitable)
: notify_waitable_(notify_waitable)
{
}
ExecutorEntitiesCollector::~ExecutorEntitiesCollector()
{
for (auto weak_node_it = weak_nodes_.begin(); weak_node_it != weak_nodes_.end(); ) {
weak_node_it = remove_weak_node(weak_node_it);
}
for (auto weak_group_it = automatically_added_groups_.begin();
weak_group_it != automatically_added_groups_.end(); )
{
weak_group_it = remove_weak_callback_group(weak_group_it, automatically_added_groups_);
}
for (auto weak_group_it = manually_added_groups_.begin();
weak_group_it != manually_added_groups_.end(); )
{
weak_group_it = remove_weak_callback_group(weak_group_it, manually_added_groups_);
}
for (auto weak_node_ptr : pending_added_nodes_) {
auto node_ptr = weak_node_ptr.lock();
if (node_ptr) {
node_ptr->get_associated_with_executor_atomic().store(false);
}
}
pending_added_nodes_.clear();
pending_removed_nodes_.clear();
for (auto weak_group_ptr : pending_manually_added_groups_) {
auto group_ptr = weak_group_ptr.lock();
if (group_ptr) {
group_ptr->get_associated_with_executor_atomic().store(false);
}
}
pending_manually_added_groups_.clear();
pending_manually_removed_groups_.clear();
}
bool
ExecutorEntitiesCollector::has_pending() const
{
std::lock_guard<std::mutex> lock(mutex_);
return pending_manually_added_groups_.size() != 0 ||
pending_manually_removed_groups_.size() != 0 ||
pending_added_nodes_.size() != 0 ||
pending_removed_nodes_.size() != 0;
}
void
ExecutorEntitiesCollector::add_node(rclcpp::node_interfaces::NodeBaseInterface::SharedPtr node_ptr)
{
// 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(
std::string("Node '") + node_ptr->get_fully_qualified_name() +
"' has already been added to an executor.");
}
std::lock_guard<std::mutex> lock(mutex_);
bool associated = weak_nodes_.count(node_ptr) != 0;
bool add_queued = pending_added_nodes_.count(node_ptr) != 0;
bool remove_queued = pending_removed_nodes_.count(node_ptr) != 0;
if ((associated || add_queued) && !remove_queued) {
throw std::runtime_error(
std::string("Node '") + node_ptr->get_fully_qualified_name() +
"' has already been added to this executor.");
}
this->pending_added_nodes_.insert(node_ptr);
}
void
ExecutorEntitiesCollector::remove_node(
rclcpp::node_interfaces::NodeBaseInterface::SharedPtr node_ptr)
{
std::atomic_bool & has_executor = node_ptr->get_associated_with_executor_atomic();
if (!has_executor.exchange(false)) {
throw std::runtime_error(
std::string("Node '") + node_ptr->get_fully_qualified_name() +
"' needs to be associated with an executor.");
}
std::lock_guard<std::mutex> lock(mutex_);
bool associated = weak_nodes_.count(node_ptr) != 0;
bool add_queued = pending_added_nodes_.count(node_ptr) != 0;
bool remove_queued = pending_removed_nodes_.count(node_ptr) != 0;
if (!(associated || add_queued) || remove_queued) {
throw std::runtime_error(
std::string("Node '") + node_ptr->get_fully_qualified_name() +
"' needs to be associated with this executor.");
}
this->pending_removed_nodes_.insert(node_ptr);
}
void
ExecutorEntitiesCollector::add_callback_group(rclcpp::CallbackGroup::SharedPtr group_ptr)
{
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.");
}
std::lock_guard<std::mutex> lock(mutex_);
bool associated = manually_added_groups_.count(group_ptr) != 0;
bool add_queued = pending_manually_added_groups_.count(group_ptr) != 0;
bool remove_queued = pending_manually_removed_groups_.count(group_ptr) != 0;
if ((associated || add_queued) && !remove_queued) {
throw std::runtime_error("Callback group has already been added to this executor.");
}
this->pending_manually_added_groups_.insert(group_ptr);
}
void
ExecutorEntitiesCollector::remove_callback_group(rclcpp::CallbackGroup::SharedPtr group_ptr)
{
if (!group_ptr->get_associated_with_executor_atomic().load()) {
throw std::runtime_error("Callback group needs to be associated with an executor.");
}
/**
* TODO(mjcarroll): The callback groups, being created by a node, should never outlive
* the node. Since we haven't historically enforced this, turning this on may cause
* previously-functional code to fail.
* Consider re-enablng this check (along with corresponding CallbackGroup::has_valid_node),
* when we can guarantee node/group lifetimes.
if (!group_ptr->has_valid_node()) {
throw std::runtime_error("Node must not be deleted before its callback group(s).");
}
*/
auto weak_group_ptr = rclcpp::CallbackGroup::WeakPtr(group_ptr);
std::lock_guard<std::mutex> lock(mutex_);
bool associated = manually_added_groups_.count(group_ptr) != 0;
bool add_queued = pending_manually_added_groups_.count(group_ptr) != 0;
bool remove_queued = pending_manually_removed_groups_.count(group_ptr) != 0;
if (!(associated || add_queued) || remove_queued) {
throw std::runtime_error("Callback group needs to be associated with this executor.");
}
this->pending_manually_removed_groups_.insert(group_ptr);
}
std::vector<rclcpp::CallbackGroup::WeakPtr>
ExecutorEntitiesCollector::get_all_callback_groups() const
{
std::vector<rclcpp::CallbackGroup::WeakPtr> groups;
std::lock_guard<std::mutex> lock(mutex_);
for (const auto & group_ptr : manually_added_groups_) {
groups.push_back(group_ptr);
}
for (auto const & group_ptr : automatically_added_groups_) {
groups.push_back(group_ptr);
}
return groups;
}
std::vector<rclcpp::CallbackGroup::WeakPtr>
ExecutorEntitiesCollector::get_manually_added_callback_groups() const
{
std::vector<rclcpp::CallbackGroup::WeakPtr> groups;
std::lock_guard<std::mutex> lock(mutex_);
for (const auto & group_ptr : manually_added_groups_) {
groups.push_back(group_ptr);
}
return groups;
}
std::vector<rclcpp::CallbackGroup::WeakPtr>
ExecutorEntitiesCollector::get_automatically_added_callback_groups() const
{
std::vector<rclcpp::CallbackGroup::WeakPtr> groups;
std::lock_guard<std::mutex> lock(mutex_);
for (auto const & group_ptr : automatically_added_groups_) {
groups.push_back(group_ptr);
}
return groups;
}
void
ExecutorEntitiesCollector::update_collections()
{
std::lock_guard<std::mutex> lock(mutex_);
this->process_queues();
this->add_automatically_associated_callback_groups(this->weak_nodes_);
this->prune_invalid_nodes_and_groups();
}
ExecutorEntitiesCollector::NodeCollection::iterator
ExecutorEntitiesCollector::remove_weak_node(NodeCollection::iterator weak_node)
{
// Disassociate the guard condition from the executor notify waitable
auto guard_condition_it = weak_nodes_to_guard_conditions_.find(*weak_node);
if (guard_condition_it != weak_nodes_to_guard_conditions_.end()) {
this->notify_waitable_->remove_guard_condition(guard_condition_it->second);
weak_nodes_to_guard_conditions_.erase(guard_condition_it);
}
// Mark the node as disassociated (if the node is still valid)
auto node_ptr = weak_node->lock();
if (node_ptr) {
std::atomic_bool & has_executor = node_ptr->get_associated_with_executor_atomic();
has_executor.store(false);
}
// Remove the node from tracked nodes
return weak_nodes_.erase(weak_node);
}
ExecutorEntitiesCollector::CallbackGroupCollection::iterator
ExecutorEntitiesCollector::remove_weak_callback_group(
CallbackGroupCollection::iterator weak_group_it,
CallbackGroupCollection & collection
)
{
// Disassociate the guard condition from the executor notify waitable
auto guard_condition_it = weak_groups_to_guard_conditions_.find(*weak_group_it);
if (guard_condition_it != weak_groups_to_guard_conditions_.end()) {
this->notify_waitable_->remove_guard_condition(guard_condition_it->second);
weak_groups_to_guard_conditions_.erase(guard_condition_it);
}
// Mark the node as disassociated (if the group is still valid)
auto group_ptr = weak_group_it->lock();
if (group_ptr) {
/**
* TODO(mjcarroll): The callback groups, being created by a node, should never outlive
* the node. Since we haven't historically enforced this, turning this on may cause
* previously-functional code to fail.
* Consider re-enablng this check (along with corresponding CallbackGroup::has_valid_node),
* when we can guarantee node/group lifetimes.
if (!group_ptr->has_valid_node()) {
throw std::runtime_error("Node must not be deleted before its callback group(s).");
}
*/
std::atomic_bool & has_executor = group_ptr->get_associated_with_executor_atomic();
has_executor.store(false);
}
// Remove the node from tracked nodes
return collection.erase(weak_group_it);
}
void
ExecutorEntitiesCollector::add_callback_group_to_collection(
rclcpp::CallbackGroup::SharedPtr group_ptr,
CallbackGroupCollection & collection)
{
auto iter = collection.insert(group_ptr);
if (iter.second == false) {
throw std::runtime_error("Callback group has already been added to this executor.");
}
// Store node guard condition in map and add it to the notify waitable
auto group_guard_condition = group_ptr->get_notify_guard_condition();
weak_groups_to_guard_conditions_.insert({group_ptr, group_guard_condition});
this->notify_waitable_->add_guard_condition(group_guard_condition);
}
void
ExecutorEntitiesCollector::process_queues()
{
for (auto weak_node_ptr : pending_added_nodes_) {
auto node_ptr = weak_node_ptr.lock();
if (!node_ptr) {
continue;
}
weak_nodes_.insert(weak_node_ptr);
this->add_automatically_associated_callback_groups({weak_node_ptr});
// Store node guard condition in map and add it to the notify waitable
auto node_guard_condition = node_ptr->get_shared_notify_guard_condition();
weak_nodes_to_guard_conditions_.insert({weak_node_ptr, node_guard_condition});
this->notify_waitable_->add_guard_condition(node_guard_condition);
}
pending_added_nodes_.clear();
for (auto weak_node_ptr : pending_removed_nodes_) {
auto node_it = weak_nodes_.find(weak_node_ptr);
if (node_it != weak_nodes_.end()) {
remove_weak_node(node_it);
} else {
// 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();
if (node_ptr) {
for (auto group_it = automatically_added_groups_.begin();
group_it != automatically_added_groups_.end(); )
{
auto group_ptr = group_it->lock();
if (node_ptr->callback_group_in_node(group_ptr)) {
group_it = remove_weak_callback_group(group_it, automatically_added_groups_);
} else {
++group_it;
}
}
}
}
pending_removed_nodes_.clear();
for (auto weak_group_ptr : pending_manually_added_groups_) {
auto group_ptr = weak_group_ptr.lock();
if (group_ptr) {
this->add_callback_group_to_collection(group_ptr, manually_added_groups_);
}
}
pending_manually_added_groups_.clear();
for (auto weak_group_ptr : pending_manually_removed_groups_) {
auto group_ptr = weak_group_ptr.lock();
if (group_ptr) {
auto group_it = manually_added_groups_.find(group_ptr);
if (group_it != manually_added_groups_.end()) {
remove_weak_callback_group(group_it, manually_added_groups_);
} else {
throw std::runtime_error(
"Attempting to remove a callback group not added to this executor.");
}
}
}
pending_manually_removed_groups_.clear();
}
void
ExecutorEntitiesCollector::add_automatically_associated_callback_groups(
const NodeCollection & nodes_to_check)
{
for (auto & weak_node : nodes_to_check) {
auto node = weak_node.lock();
if (node) {
node->for_each_callback_group(
[this, node](rclcpp::CallbackGroup::SharedPtr group_ptr)
{
if (!group_ptr->get_associated_with_executor_atomic().load() &&
group_ptr->automatically_add_to_executor_with_node())
{
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.");
}
this->add_callback_group_to_collection(group_ptr, this->automatically_added_groups_);
}
});
}
}
}
void
ExecutorEntitiesCollector::prune_invalid_nodes_and_groups()
{
for (auto node_it = weak_nodes_.begin();
node_it != weak_nodes_.end(); )
{
if (node_it->expired()) {
node_it = remove_weak_node(node_it);
} else {
node_it++;
}
}
for (auto group_it = automatically_added_groups_.begin();
group_it != automatically_added_groups_.end(); )
{
if (group_it->expired()) {
group_it = remove_weak_callback_group(group_it, automatically_added_groups_);
} else {
group_it++;
}
}
for (auto group_it = manually_added_groups_.begin();
group_it != manually_added_groups_.end(); )
{
if (group_it->expired()) {
group_it = remove_weak_callback_group(group_it, manually_added_groups_);
} else {
group_it++;
}
}
}
} // namespace executors
} // namespace rclcpp

View File

@@ -1,181 +0,0 @@
// Copyright 2023 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 <iostream>
#include "rclcpp/exceptions.hpp"
#include "rclcpp/executors/executor_notify_waitable.hpp"
namespace rclcpp
{
namespace executors
{
ExecutorNotifyWaitable::ExecutorNotifyWaitable(std::function<void(void)> on_execute_callback)
: execute_callback_(on_execute_callback)
{
}
ExecutorNotifyWaitable::ExecutorNotifyWaitable(const ExecutorNotifyWaitable & other)
: ExecutorNotifyWaitable(other.execute_callback_)
{
this->notify_guard_conditions_ = other.notify_guard_conditions_;
}
ExecutorNotifyWaitable & ExecutorNotifyWaitable::operator=(const ExecutorNotifyWaitable & other)
{
if (this != &other) {
this->execute_callback_ = other.execute_callback_;
this->notify_guard_conditions_ = other.notify_guard_conditions_;
}
return *this;
}
void
ExecutorNotifyWaitable::add_to_wait_set(rcl_wait_set_t * wait_set)
{
std::lock_guard<std::mutex> lock(guard_condition_mutex_);
for (auto weak_guard_condition : this->notify_guard_conditions_) {
auto guard_condition = weak_guard_condition.lock();
if (!guard_condition) {continue;}
rcl_guard_condition_t * cond = &guard_condition->get_rcl_guard_condition();
rcl_ret_t ret = rcl_wait_set_add_guard_condition(
wait_set, cond, NULL);
if (RCL_RET_OK != ret) {
rclcpp::exceptions::throw_from_rcl_error(
ret, "failed to add guard condition to wait set");
}
}
}
bool
ExecutorNotifyWaitable::is_ready(rcl_wait_set_t * wait_set)
{
std::lock_guard<std::mutex> lock(guard_condition_mutex_);
bool any_ready = false;
for (size_t ii = 0; ii < wait_set->size_of_guard_conditions; ++ii) {
auto rcl_guard_condition = wait_set->guard_conditions[ii];
if (nullptr == rcl_guard_condition) {
continue;
}
for (auto weak_guard_condition : this->notify_guard_conditions_) {
auto guard_condition = weak_guard_condition.lock();
if (guard_condition && &guard_condition->get_rcl_guard_condition() == rcl_guard_condition) {
any_ready = true;
}
}
}
return any_ready;
}
void
ExecutorNotifyWaitable::execute(std::shared_ptr<void> & data)
{
(void) data;
this->execute_callback_();
}
std::shared_ptr<void>
ExecutorNotifyWaitable::take_data()
{
return nullptr;
}
std::shared_ptr<void>
ExecutorNotifyWaitable::take_data_by_entity_id(size_t id)
{
(void) id;
return nullptr;
}
void
ExecutorNotifyWaitable::set_on_ready_callback(std::function<void(size_t, int)> callback)
{
// The second argument of the callback could be used to identify which guard condition
// triggered the event.
// We could indicate which of the guard conditions was triggered, but the executor
// is already going to check that.
auto gc_callback = [callback](size_t count) {
callback(count, 0);
};
std::lock_guard<std::mutex> lock(guard_condition_mutex_);
on_ready_callback_ = gc_callback;
for (auto weak_gc : notify_guard_conditions_) {
auto gc = weak_gc.lock();
if (!gc) {
continue;
}
gc->set_on_trigger_callback(on_ready_callback_);
}
}
RCLCPP_PUBLIC
void
ExecutorNotifyWaitable::clear_on_ready_callback()
{
std::lock_guard<std::mutex> lock(guard_condition_mutex_);
on_ready_callback_ = nullptr;
for (auto weak_gc : notify_guard_conditions_) {
auto gc = weak_gc.lock();
if (!gc) {
continue;
}
gc->set_on_trigger_callback(nullptr);
}
}
void
ExecutorNotifyWaitable::add_guard_condition(rclcpp::GuardCondition::WeakPtr weak_guard_condition)
{
std::lock_guard<std::mutex> lock(guard_condition_mutex_);
auto guard_condition = weak_guard_condition.lock();
if (guard_condition && notify_guard_conditions_.count(weak_guard_condition) == 0) {
notify_guard_conditions_.insert(weak_guard_condition);
if (on_ready_callback_) {
guard_condition->set_on_trigger_callback(on_ready_callback_);
}
}
}
void
ExecutorNotifyWaitable::remove_guard_condition(rclcpp::GuardCondition::WeakPtr weak_guard_condition)
{
std::lock_guard<std::mutex> lock(guard_condition_mutex_);
if (notify_guard_conditions_.count(weak_guard_condition) != 0) {
notify_guard_conditions_.erase(weak_guard_condition);
auto guard_condition = weak_guard_condition.lock();
// If this notify waitable doesn't have an on_ready_callback, then there's nothing to unset
if (guard_condition && on_ready_callback_) {
guard_condition->set_on_trigger_callback(nullptr);
}
}
}
size_t
ExecutorNotifyWaitable::get_number_of_ready_guard_conditions()
{
std::lock_guard<std::mutex> lock(guard_condition_mutex_);
return notify_guard_conditions_.size();
}
} // namespace executors
} // namespace rclcpp

View File

@@ -99,19 +99,6 @@ MultiThreadedExecutor::run(size_t this_thread_number)
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
// resetting the callback group `can_be_taken_from`
any_exec.callback_group.reset();

View File

@@ -0,0 +1,524 @@
// 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()) {
const auto & gc = node_ptr->get_notify_guard_condition();
weak_nodes_to_guard_conditions_[node_ptr] = &gc;
}
}
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;
}

View File

@@ -12,21 +12,31 @@
// See the License for the specific language governing permissions and
// limitations under the License.
#include "rclcpp/executors/executor_entities_collection.hpp"
#include "rclcpp/executors/executor_notify_waitable.hpp"
#include "rclcpp/executors/static_single_threaded_executor.hpp"
#include <chrono>
#include <memory>
#include <utility>
#include <vector>
#include "rcpputils/scope_exit.hpp"
#include "rclcpp/executors/static_single_threaded_executor.hpp"
#include "rclcpp/any_executable.hpp"
using rclcpp::executors::StaticSingleThreadedExecutor;
using rclcpp::experimental::ExecutableList;
StaticSingleThreadedExecutor::StaticSingleThreadedExecutor(const rclcpp::ExecutorOptions & options)
StaticSingleThreadedExecutor::StaticSingleThreadedExecutor(
const rclcpp::ExecutorOptions & options)
: rclcpp::Executor(options)
{
entities_collector_ = std::make_shared<StaticExecutorEntitiesCollector>();
}
StaticSingleThreadedExecutor::~StaticSingleThreadedExecutor() {}
StaticSingleThreadedExecutor::~StaticSingleThreadedExecutor()
{
if (entities_collector_->is_init()) {
entities_collector_->fini();
}
}
void
StaticSingleThreadedExecutor::spin()
@@ -36,25 +46,14 @@ StaticSingleThreadedExecutor::spin()
}
RCPPUTILS_SCOPE_EXIT(this->spinning.store(false); );
// This is essentially the contents of the rclcpp::Executor::wait_for_work method,
// except we need to keep the wait result to reproduce the StaticSingleThreadedExecutor
// behavior.
// Set memory_strategy_ and exec_list_ based on weak_nodes_
// Prepare wait_set_ based on memory_strategy_
entities_collector_->init(&wait_set_, memory_strategy_);
while (rclcpp::ok(this->context_) && spinning.load()) {
std::deque<rclcpp::AnyExecutable> to_exec;
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);
// Refresh wait set and wait for work
entities_collector_->refresh_wait_set();
execute_ready_executables();
}
}
@@ -81,6 +80,11 @@ StaticSingleThreadedExecutor::spin_all(std::chrono::nanoseconds max_duration)
void
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 max_duration_not_elapsed = [max_duration, start]() {
if (std::chrono::nanoseconds(0) == max_duration) {
@@ -101,21 +105,9 @@ StaticSingleThreadedExecutor::spin_some_impl(std::chrono::nanoseconds max_durati
while (rclcpp::ok(context_) && spinning.load() && max_duration_not_elapsed()) {
// Get executables that are ready now
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;
}
entities_collector_->refresh_wait_set(std::chrono::milliseconds::zero());
// Execute ready executables
bool work_available = execute_ready_executables(current_collection_, wait_result, false);
bool work_available = execute_ready_executables();
if (!work_available || !exhaustive) {
break;
}
@@ -125,122 +117,164 @@ StaticSingleThreadedExecutor::spin_some_impl(std::chrono::nanoseconds max_durati
void
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()) {
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(timeout));
if (wait_result.kind() == WaitResultKind::Empty) {
RCUTILS_LOG_WARN_NAMED(
"rclcpp",
"empty wait set received in wait(). This should never happen.");
return;
}
// Wait until we have a ready entity or timeout expired
entities_collector_->refresh_wait_set(timeout);
// Execute ready executables
execute_ready_executables(current_collection_, wait_result, true);
execute_ready_executables(true);
}
}
// This preserves the "scheduling semantics" of the StaticSingleThreadedExecutor
// from the original implementation.
bool StaticSingleThreadedExecutor::execute_ready_executables(
const rclcpp::executors::ExecutorEntitiesCollection & collection,
rclcpp::WaitResult<rclcpp::WaitSet> & wait_result,
bool spin_once)
void
StaticSingleThreadedExecutor::add_callback_group(
rclcpp::CallbackGroup::SharedPtr group_ptr,
rclcpp::node_interfaces::NodeBaseInterface::SharedPtr node_ptr,
bool notify)
{
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;
if (wait_result.kind() != rclcpp::WaitResultKind::Ready) {
return any_ready_executable;
// Execute all the ready subscriptions
for (size_t i = 0; i < wait_set_.size_of_subscriptions; ++i) {
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;
}
}
}
auto rcl_wait_set = wait_result.get_wait_set().get_rcl_wait_set();
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();
if (!entity) {
continue;
// Execute all the ready timers
for (size_t i = 0; i < wait_set_.size_of_timers; ++i) {
if (i < entities_collector_->get_number_of_timers()) {
if (wait_set_.timers[i] && entities_collector_->get_timer(i)->is_ready()) {
auto timer = entities_collector_->get_timer(i);
timer->call();
execute_timer(std::move(timer));
if (spin_once) {
return true;
}
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) {
return 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;
}

View File

@@ -1,488 +0,0 @@
// Copyright 2023 iRobot Corporation.
//
// 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/experimental/executors/events_executor/events_executor.hpp"
#include <memory>
#include <utility>
#include <vector>
#include "rcpputils/scope_exit.hpp"
using namespace std::chrono_literals;
using rclcpp::experimental::executors::EventsExecutor;
EventsExecutor::EventsExecutor(
rclcpp::experimental::executors::EventsQueue::UniquePtr events_queue,
bool execute_timers_separate_thread,
const rclcpp::ExecutorOptions & options)
: rclcpp::Executor(options)
{
// Get ownership of the queue used to store events.
if (!events_queue) {
throw std::invalid_argument("events_queue can't be a null pointer");
}
events_queue_ = std::move(events_queue);
// Create timers manager
// The timers manager can be used either to only track timers (in this case an expired
// timer will generate an executor event and then it will be executed by the executor thread)
// or it can also take care of executing expired timers in its dedicated thread.
std::function<void(const rclcpp::TimerBase *)> timer_on_ready_cb = nullptr;
if (!execute_timers_separate_thread) {
timer_on_ready_cb = [this](const rclcpp::TimerBase * timer_id) {
ExecutorEvent event = {timer_id, -1, ExecutorEventType::TIMER_EVENT, 1};
this->events_queue_->enqueue(event);
};
}
timers_manager_ =
std::make_shared<rclcpp::experimental::TimersManager>(context_, timer_on_ready_cb);
notify_waitable_ = std::make_shared<rclcpp::executors::ExecutorNotifyWaitable>(
[this]() {
// This callback is invoked when:
// - the interrupt or shutdown guard condition is triggered:
// ---> we need to wake up the executor so that it can terminate
// - a node or callback group guard condition is triggered:
// ---> the entities collection is changed, we need to update callbacks
notify_waitable_event_pushed_ = false;
this->refresh_current_collection_from_callback_groups();
});
notify_waitable_->add_guard_condition(interrupt_guard_condition_);
notify_waitable_->add_guard_condition(shutdown_guard_condition_);
notify_waitable_->set_on_ready_callback(
this->create_waitable_callback(notify_waitable_.get()));
auto notify_waitable_entity_id = notify_waitable_.get();
notify_waitable_->set_on_ready_callback(
[this, notify_waitable_entity_id](size_t num_events, int waitable_data) {
// The notify waitable has a special callback.
// We don't care about how many events as when we wake up the executor we are going to
// process everything regardless.
// For the same reason, if an event of this type has already been pushed but it has not been
// processed yet, we avoid pushing additional events.
(void)num_events;
if (notify_waitable_event_pushed_.exchange(true)) {
return;
}
ExecutorEvent event =
{notify_waitable_entity_id, waitable_data, ExecutorEventType::WAITABLE_EVENT, 1};
this->events_queue_->enqueue(event);
});
this->entities_collector_ =
std::make_shared<rclcpp::executors::ExecutorEntitiesCollector>(notify_waitable_);
this->current_entities_collection_ =
std::make_shared<rclcpp::executors::ExecutorEntitiesCollection>();
}
EventsExecutor::~EventsExecutor()
{
spinning.store(false);
notify_waitable_->clear_on_ready_callback();
this->refresh_current_collection({});
}
void
EventsExecutor::spin()
{
if (spinning.exchange(true)) {
throw std::runtime_error("spin() called while already spinning");
}
RCPPUTILS_SCOPE_EXIT(this->spinning.store(false); );
timers_manager_->start();
RCPPUTILS_SCOPE_EXIT(timers_manager_->stop(); );
while (rclcpp::ok(context_) && spinning.load()) {
// Wait until we get an event
ExecutorEvent event;
bool has_event = events_queue_->dequeue(event);
if (has_event) {
this->execute_event(event);
}
}
}
void
EventsExecutor::spin_some(std::chrono::nanoseconds max_duration)
{
return this->spin_some_impl(max_duration, false);
}
void
EventsExecutor::spin_all(std::chrono::nanoseconds max_duration)
{
if (max_duration <= 0ns) {
throw std::invalid_argument("max_duration must be positive");
}
return this->spin_some_impl(max_duration, true);
}
void
EventsExecutor::spin_some_impl(std::chrono::nanoseconds max_duration, bool exhaustive)
{
if (spinning.exchange(true)) {
throw std::runtime_error("spin_some() called while already spinning");
}
RCPPUTILS_SCOPE_EXIT(this->spinning.store(false); );
auto start = std::chrono::steady_clock::now();
auto max_duration_not_elapsed = [max_duration, start]() {
if (std::chrono::nanoseconds(0) == max_duration) {
// told to spin forever if need be
return true;
} else if (std::chrono::steady_clock::now() - start < max_duration) {
// told to spin only for some maximum amount of time
return true;
}
// spun too long
return false;
};
// Get the number of events and timers ready at start
const size_t ready_events_at_start = events_queue_->size();
size_t executed_events = 0;
const size_t ready_timers_at_start = timers_manager_->get_number_ready_timers();
size_t executed_timers = 0;
while (rclcpp::ok(context_) && spinning.load() && max_duration_not_elapsed()) {
// Execute first ready event from queue if exists
if (exhaustive || (executed_events < ready_events_at_start)) {
bool has_event = !events_queue_->empty();
if (has_event) {
ExecutorEvent event;
bool ret = events_queue_->dequeue(event, std::chrono::nanoseconds(0));
if (ret) {
this->execute_event(event);
executed_events++;
continue;
}
}
}
// Execute first timer if it is ready
if (exhaustive || (executed_timers < ready_timers_at_start)) {
bool timer_executed = timers_manager_->execute_head_timer();
if (timer_executed) {
executed_timers++;
continue;
}
}
// If there's no more work available, exit
break;
}
}
void
EventsExecutor::spin_once_impl(std::chrono::nanoseconds timeout)
{
// In this context a negative input timeout means no timeout
if (timeout < 0ns) {
timeout = std::chrono::nanoseconds::max();
}
// Select the smallest between input timeout and timer timeout
bool is_timer_timeout = false;
auto next_timer_timeout = timers_manager_->get_head_timeout();
if (next_timer_timeout < timeout) {
timeout = next_timer_timeout;
is_timer_timeout = true;
}
ExecutorEvent event;
bool has_event = events_queue_->dequeue(event, timeout);
// If we wake up from the wait with an event, it means that it
// arrived before any of the timers expired.
if (has_event) {
this->execute_event(event);
} else if (is_timer_timeout) {
timers_manager_->execute_head_timer();
}
}
void
EventsExecutor::add_node(
rclcpp::node_interfaces::NodeBaseInterface::SharedPtr node_ptr, bool notify)
{
// This field is unused because we don't have to wake up the executor when a node is added.
(void) notify;
// Add node to entities collector
this->entities_collector_->add_node(node_ptr);
this->refresh_current_collection_from_callback_groups();
}
void
EventsExecutor::add_node(std::shared_ptr<rclcpp::Node> node_ptr, bool notify)
{
this->add_node(node_ptr->get_node_base_interface(), notify);
}
void
EventsExecutor::remove_node(
rclcpp::node_interfaces::NodeBaseInterface::SharedPtr node_ptr, bool notify)
{
// This field is unused because we don't have to wake up the executor when a node is removed.
(void)notify;
// Remove node from entities collector.
// This will result in un-setting all the event callbacks from its entities.
// After this function returns, this executor will not receive any more events associated
// to these entities.
this->entities_collector_->remove_node(node_ptr);
this->refresh_current_collection_from_callback_groups();
}
void
EventsExecutor::remove_node(std::shared_ptr<rclcpp::Node> node_ptr, bool notify)
{
this->remove_node(node_ptr->get_node_base_interface(), notify);
}
void
EventsExecutor::execute_event(const ExecutorEvent & event)
{
switch (event.type) {
case ExecutorEventType::CLIENT_EVENT:
{
auto client = this->retrieve_entity(
static_cast<const rcl_client_t *>(event.entity_key),
current_entities_collection_->clients);
if (client) {
for (size_t i = 0; i < event.num_events; i++) {
execute_client(client);
}
}
break;
}
case ExecutorEventType::SUBSCRIPTION_EVENT:
{
auto subscription = this->retrieve_entity(
static_cast<const rcl_subscription_t *>(event.entity_key),
current_entities_collection_->subscriptions);
if (subscription) {
for (size_t i = 0; i < event.num_events; i++) {
execute_subscription(subscription);
}
}
break;
}
case ExecutorEventType::SERVICE_EVENT:
{
auto service = this->retrieve_entity(
static_cast<const rcl_service_t *>(event.entity_key),
current_entities_collection_->services);
if (service) {
for (size_t i = 0; i < event.num_events; i++) {
execute_service(service);
}
}
break;
}
case ExecutorEventType::TIMER_EVENT:
{
timers_manager_->execute_ready_timer(
static_cast<const rclcpp::TimerBase *>(event.entity_key));
break;
}
case ExecutorEventType::WAITABLE_EVENT:
{
auto waitable = this->retrieve_entity(
static_cast<const rclcpp::Waitable *>(event.entity_key),
current_entities_collection_->waitables);
if (waitable) {
for (size_t i = 0; i < event.num_events; i++) {
auto data = waitable->take_data_by_entity_id(event.waitable_data);
waitable->execute(data);
}
}
break;
}
}
}
void
EventsExecutor::add_callback_group(
rclcpp::CallbackGroup::SharedPtr group_ptr,
rclcpp::node_interfaces::NodeBaseInterface::SharedPtr node_ptr,
bool notify)
{
// This field is unused because we don't have to wake up
// the executor when a callback group is added.
(void)notify;
(void)node_ptr;
this->entities_collector_->add_callback_group(group_ptr);
this->refresh_current_collection_from_callback_groups();
}
void
EventsExecutor::remove_callback_group(
rclcpp::CallbackGroup::SharedPtr group_ptr, bool notify)
{
// This field is unused because we don't have to wake up
// the executor when a callback group is removed.
(void)notify;
this->entities_collector_->remove_callback_group(group_ptr);
this->refresh_current_collection_from_callback_groups();
}
std::vector<rclcpp::CallbackGroup::WeakPtr>
EventsExecutor::get_all_callback_groups()
{
this->entities_collector_->update_collections();
return this->entities_collector_->get_all_callback_groups();
}
std::vector<rclcpp::CallbackGroup::WeakPtr>
EventsExecutor::get_manually_added_callback_groups()
{
this->entities_collector_->update_collections();
return this->entities_collector_->get_manually_added_callback_groups();
}
std::vector<rclcpp::CallbackGroup::WeakPtr>
EventsExecutor::get_automatically_added_callback_groups_from_nodes()
{
this->entities_collector_->update_collections();
return this->entities_collector_->get_automatically_added_callback_groups();
}
void
EventsExecutor::refresh_current_collection_from_callback_groups()
{
this->entities_collector_->update_collections();
auto callback_groups = this->entities_collector_->get_all_callback_groups();
rclcpp::executors::ExecutorEntitiesCollection new_collection;
rclcpp::executors::build_entities_collection(callback_groups, new_collection);
// TODO(alsora): this may be implemented in a better way.
// We need the notify waitable to be included in the executor "current_collection"
// because we need to be able to retrieve events for it.
// We could explicitly check for the notify waitable ID when we receive a waitable event
// but I think that it's better if the waitable was in the collection and it could be
// retrieved in the "standard" way.
// To do it, we need to add the notify waitable as an entry in both the new and
// current collections such that it's neither added or removed.
rclcpp::CallbackGroup::WeakPtr weak_group_ptr;
new_collection.waitables.insert(
{
this->notify_waitable_.get(),
{this->notify_waitable_, weak_group_ptr}
});
this->current_entities_collection_->waitables.insert(
{
this->notify_waitable_.get(),
{this->notify_waitable_, weak_group_ptr}
});
this->refresh_current_collection(new_collection);
}
void
EventsExecutor::refresh_current_collection(
const rclcpp::executors::ExecutorEntitiesCollection & new_collection)
{
current_entities_collection_->timers.update(
new_collection.timers,
[this](rclcpp::TimerBase::SharedPtr timer) {timers_manager_->add_timer(timer);},
[this](rclcpp::TimerBase::SharedPtr timer) {timers_manager_->remove_timer(timer);});
current_entities_collection_->subscriptions.update(
new_collection.subscriptions,
[this](auto subscription) {
subscription->set_on_new_message_callback(
this->create_entity_callback(
subscription->get_subscription_handle().get(), ExecutorEventType::SUBSCRIPTION_EVENT));
},
[](auto subscription) {subscription->clear_on_new_message_callback();});
current_entities_collection_->clients.update(
new_collection.clients,
[this](auto client) {
client->set_on_new_response_callback(
this->create_entity_callback(
client->get_client_handle().get(), ExecutorEventType::CLIENT_EVENT));
},
[](auto client) {client->clear_on_new_response_callback();});
current_entities_collection_->services.update(
new_collection.services,
[this](auto service) {
service->set_on_new_request_callback(
this->create_entity_callback(
service->get_service_handle().get(), ExecutorEventType::SERVICE_EVENT));
},
[](auto service) {service->clear_on_new_request_callback();});
// DO WE NEED THIS? WE ARE NOT DOING ANYTHING WITH GUARD CONDITIONS
/*
current_entities_collection_->guard_conditions.update(new_collection.guard_conditions,
[](auto guard_condition) {(void)guard_condition;},
[](auto guard_condition) {guard_condition->set_on_trigger_callback(nullptr);});
*/
current_entities_collection_->waitables.update(
new_collection.waitables,
[this](auto waitable) {
waitable->set_on_ready_callback(
this->create_waitable_callback(waitable.get()));
},
[](auto waitable) {waitable->clear_on_ready_callback();});
}
std::function<void(size_t)>
EventsExecutor::create_entity_callback(
void * entity_key, ExecutorEventType event_type)
{
std::function<void(size_t)>
callback = [this, entity_key, event_type](size_t num_events) {
ExecutorEvent event = {entity_key, -1, event_type, num_events};
this->events_queue_->enqueue(event);
};
return callback;
}
std::function<void(size_t, int)>
EventsExecutor::create_waitable_callback(const rclcpp::Waitable * entity_key)
{
std::function<void(size_t, int)>
callback = [this, entity_key](size_t num_events, int waitable_data) {
ExecutorEvent event =
{entity_key, waitable_data, ExecutorEventType::WAITABLE_EVENT, num_events};
this->events_queue_->enqueue(event);
};
return callback;
}

View File

@@ -1,299 +0,0 @@
// Copyright 2023 iRobot Corporation.
//
// 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/experimental/timers_manager.hpp"
#include <inttypes.h>
#include <ctime>
#include <iostream>
#include <memory>
#include <stdexcept>
#include "rcpputils/scope_exit.hpp"
using rclcpp::experimental::TimersManager;
TimersManager::TimersManager(
std::shared_ptr<rclcpp::Context> context,
std::function<void(const rclcpp::TimerBase *)> on_ready_callback)
{
context_ = context;
on_ready_callback_ = on_ready_callback;
}
TimersManager::~TimersManager()
{
// Remove all timers
this->clear();
// Make sure timers thread is stopped before destroying this object
this->stop();
}
void TimersManager::add_timer(rclcpp::TimerBase::SharedPtr timer)
{
if (!timer) {
throw std::invalid_argument("TimersManager::add_timer() trying to add nullptr timer");
}
bool added = false;
{
std::unique_lock<std::mutex> lock(timers_mutex_);
added = weak_timers_heap_.add_timer(timer);
timers_updated_ = timers_updated_ || added;
}
timer->set_on_reset_callback(
[this](size_t arg) {
{
(void)arg;
std::unique_lock<std::mutex> lock(timers_mutex_);
timers_updated_ = true;
}
timers_cv_.notify_one();
});
if (added) {
// Notify that a timer has been added
timers_cv_.notify_one();
}
}
void TimersManager::start()
{
// Make sure that the thread is not already running
if (running_.exchange(true)) {
throw std::runtime_error("TimersManager::start() can't start timers thread as already running");
}
timers_thread_ = std::thread(&TimersManager::run_timers, this);
}
void TimersManager::stop()
{
// Lock stop() function to prevent race condition in destructor
std::unique_lock<std::mutex> lock(stop_mutex_);
running_ = false;
// Notify the timers manager thread to wake up
{
std::unique_lock<std::mutex> lock(timers_mutex_);
timers_updated_ = true;
}
timers_cv_.notify_one();
// Join timers thread if it's running
if (timers_thread_.joinable()) {
timers_thread_.join();
}
}
std::chrono::nanoseconds TimersManager::get_head_timeout()
{
// Do not allow to interfere with the thread running
if (running_) {
throw std::runtime_error(
"get_head_timeout() can't be used while timers thread is running");
}
std::unique_lock<std::mutex> lock(timers_mutex_);
return this->get_head_timeout_unsafe();
}
size_t TimersManager::get_number_ready_timers()
{
// Do not allow to interfere with the thread running
if (running_) {
throw std::runtime_error(
"get_number_ready_timers() can't be used while timers thread is running");
}
std::unique_lock<std::mutex> lock(timers_mutex_);
TimersHeap locked_heap = weak_timers_heap_.validate_and_lock();
return locked_heap.get_number_ready_timers();
}
bool TimersManager::execute_head_timer()
{
// Do not allow to interfere with the thread running
if (running_) {
throw std::runtime_error(
"execute_head_timer() can't be used while timers thread is running");
}
std::unique_lock<std::mutex> lock(timers_mutex_);
TimersHeap timers_heap = weak_timers_heap_.validate_and_lock();
// Nothing to do if we don't have any timer
if (timers_heap.empty()) {
return false;
}
TimerPtr head_timer = timers_heap.front();
const bool timer_ready = head_timer->is_ready();
if (timer_ready) {
// NOTE: here we always execute the timer, regardless of whether the
// on_ready_callback is set or not.
head_timer->call();
head_timer->execute_callback();
timers_heap.heapify_root();
weak_timers_heap_.store(timers_heap);
}
return timer_ready;
}
void TimersManager::execute_ready_timer(const rclcpp::TimerBase * timer_id)
{
TimerPtr ready_timer;
{
std::unique_lock<std::mutex> lock(timers_mutex_);
ready_timer = weak_timers_heap_.get_timer(timer_id);
}
if (ready_timer) {
ready_timer->execute_callback();
}
}
std::chrono::nanoseconds TimersManager::get_head_timeout_unsafe()
{
// If we don't have any weak pointer, then we just return maximum timeout
if (weak_timers_heap_.empty()) {
return std::chrono::nanoseconds::max();
}
// Weak heap is not empty, so try to lock the first element.
// If it is still a valid pointer, it is guaranteed to be the correct head
TimerPtr head_timer = weak_timers_heap_.front().lock();
if (!head_timer) {
// The first element has expired, we can't make other assumptions on the heap
// and we need to entirely validate it.
TimersHeap locked_heap = weak_timers_heap_.validate_and_lock();
// NOTE: the following operations will not modify any element in the heap, so we
// don't have to call `weak_timers_heap_.store(locked_heap)` at the end.
if (locked_heap.empty()) {
return std::chrono::nanoseconds::max();
}
head_timer = locked_heap.front();
}
return head_timer->time_until_trigger();
}
void TimersManager::execute_ready_timers_unsafe()
{
// We start by locking the timers
TimersHeap locked_heap = weak_timers_heap_.validate_and_lock();
// Nothing to do if we don't have any timer
if (locked_heap.empty()) {
return;
}
// Keep executing timers until they are ready and they were already ready when we started.
// The two checks prevent this function from blocking indefinitely if the
// time required for executing the timers is longer than their period.
TimerPtr head_timer = locked_heap.front();
const size_t number_ready_timers = locked_heap.get_number_ready_timers();
size_t executed_timers = 0;
while (executed_timers < number_ready_timers && head_timer->is_ready()) {
head_timer->call();
if (on_ready_callback_) {
on_ready_callback_(head_timer.get());
} else {
head_timer->execute_callback();
}
executed_timers++;
// Executing a timer will result in updating its time_until_trigger, so re-heapify
locked_heap.heapify_root();
// Get new head timer
head_timer = locked_heap.front();
}
// After having performed work on the locked heap we reflect the changes to weak one.
// Timers will be already sorted the next time we need them if none went out of scope.
weak_timers_heap_.store(locked_heap);
}
void TimersManager::run_timers()
{
// Make sure the running flag is set to false when we exit from this function
// to allow restarting the timers thread.
RCPPUTILS_SCOPE_EXIT(this->running_.store(false); );
while (rclcpp::ok(context_) && running_) {
// Lock mutex
std::unique_lock<std::mutex> lock(timers_mutex_);
std::chrono::nanoseconds time_to_sleep = get_head_timeout_unsafe();
// No need to wait if a timer is already available
if (time_to_sleep > std::chrono::nanoseconds::zero()) {
if (time_to_sleep != std::chrono::nanoseconds::max()) {
// Wait until timeout or notification that timers have been updated
timers_cv_.wait_for(lock, time_to_sleep, [this]() {return timers_updated_;});
} else {
// Wait until notification that timers have been updated
timers_cv_.wait(lock, [this]() {return timers_updated_;});
}
}
// Reset timers updated flag
timers_updated_ = false;
// Execute timers
this->execute_ready_timers_unsafe();
}
}
void TimersManager::clear()
{
{
// Lock mutex and then clear all data structures
std::unique_lock<std::mutex> lock(timers_mutex_);
TimersHeap locked_heap = weak_timers_heap_.validate_and_lock();
locked_heap.clear_timers_on_reset_callbacks();
weak_timers_heap_.clear();
timers_updated_ = true;
}
// Notify timers thread such that it can re-compute its timeout
timers_cv_.notify_one();
}
void TimersManager::remove_timer(TimerPtr timer)
{
bool removed = false;
{
std::unique_lock<std::mutex> lock(timers_mutex_);
removed = weak_timers_heap_.remove_timer(timer);
timers_updated_ = timers_updated_ || removed;
}
if (removed) {
// Notify timers thread such that it can re-compute its timeout
timers_cv_.notify_one();
timer->clear_on_reset_callback();
}
}

View File

@@ -25,20 +25,17 @@
namespace rclcpp
{
std::shared_ptr<void>
GenericSubscription::create_message()
std::shared_ptr<void> GenericSubscription::create_message()
{
return create_serialized_message();
}
std::shared_ptr<rclcpp::SerializedMessage>
GenericSubscription::create_serialized_message()
std::shared_ptr<rclcpp::SerializedMessage> GenericSubscription::create_serialized_message()
{
return std::make_shared<rclcpp::SerializedMessage>(0);
}
void
GenericSubscription::handle_message(
void GenericSubscription::handle_message(
std::shared_ptr<void> &,
const rclcpp::MessageInfo &)
{
@@ -46,16 +43,14 @@ GenericSubscription::handle_message(
"handle_message is not implemented for GenericSubscription");
}
void
GenericSubscription::handle_serialized_message(
void GenericSubscription::handle_serialized_message(
const std::shared_ptr<rclcpp::SerializedMessage> & message,
const rclcpp::MessageInfo &)
{
callback_(message);
}
void
GenericSubscription::handle_loaned_message(
void GenericSubscription::handle_loaned_message(
void * message, const rclcpp::MessageInfo & message_info)
{
(void) message;
@@ -64,15 +59,13 @@ GenericSubscription::handle_loaned_message(
"handle_loaned_message is not implemented for GenericSubscription");
}
void
GenericSubscription::return_message(std::shared_ptr<void> & message)
void GenericSubscription::return_message(std::shared_ptr<void> & message)
{
auto typed_message = std::static_pointer_cast<rclcpp::SerializedMessage>(message);
return_serialized_message(typed_message);
}
void
GenericSubscription::return_serialized_message(
void GenericSubscription::return_serialized_message(
std::shared_ptr<rclcpp::SerializedMessage> & message)
{
message.reset();

View File

@@ -45,7 +45,7 @@ NodeBase::NodeBase(
node_handle_(nullptr),
default_callback_group_(default_callback_group),
associated_with_executor_(false),
notify_guard_condition_(std::make_shared<rclcpp::GuardCondition>(context)),
notify_guard_condition_(context),
notify_guard_condition_is_valid_(false)
{
// Create the rcl node and store it in a shared_ptr with a custom destructor.
@@ -132,10 +132,8 @@ NodeBase::NodeBase(
// Create the default callback group, if needed.
if (nullptr == default_callback_group_) {
using rclcpp::CallbackGroupType;
// Default callback group is mutually exclusive and automatically associated with
// any executors that this node is added to.
default_callback_group_ =
NodeBase::create_callback_group(CallbackGroupType::MutuallyExclusive, true);
NodeBase::create_callback_group(CallbackGroupType::MutuallyExclusive);
}
// Indicate the notify_guard_condition is now valid.
@@ -204,27 +202,11 @@ NodeBase::create_callback_group(
rclcpp::CallbackGroupType group_type,
bool automatically_add_to_executor_with_node)
{
auto weak_context = this->get_context()->weak_from_this();
auto get_node_context = [weak_context]() -> rclcpp::Context::SharedPtr {
return weak_context.lock();
};
auto group = std::make_shared<rclcpp::CallbackGroup>(
group_type,
get_node_context,
automatically_add_to_executor_with_node);
std::lock_guard<std::mutex> lock(callback_groups_mutex_);
callback_groups_.push_back(group);
// This guard condition is generally used to signal to this node's executor that a callback
// group has been added that should be considered for new entities.
// If this is creating the default callback group, then the notify guard condition won't be
// ready or needed yet, as the node is not done being constructed and therefore cannot be added.
// If the callback group is not automatically associated with this node's executors, then
// triggering the guard condition is also unnecessary, it will be manually added to an exector.
if (notify_guard_condition_is_valid_ && automatically_add_to_executor_with_node) {
this->trigger_notify_guard_condition();
}
return group;
}
@@ -271,29 +253,9 @@ NodeBase::get_notify_guard_condition()
if (!notify_guard_condition_is_valid_) {
throw std::runtime_error("failed to get notify guard condition because it is invalid");
}
return *notify_guard_condition_;
}
rclcpp::GuardCondition::SharedPtr
NodeBase::get_shared_notify_guard_condition()
{
std::lock_guard<std::recursive_mutex> notify_condition_lock(notify_guard_condition_mutex_);
if (!notify_guard_condition_is_valid_) {
return nullptr;
}
return notify_guard_condition_;
}
void
NodeBase::trigger_notify_guard_condition()
{
std::lock_guard<std::recursive_mutex> notify_condition_lock(notify_guard_condition_mutex_);
if (!notify_guard_condition_is_valid_) {
throw std::runtime_error("failed to trigger notify guard condition because it is invalid");
}
notify_guard_condition_->trigger();
}
bool
NodeBase::get_use_intra_process_default() const
{

View File

@@ -533,8 +533,9 @@ NodeGraph::notify_graph_change()
}
}
graph_cv_.notify_all();
auto & node_gc = node_base_->get_notify_guard_condition();
try {
node_base_->trigger_notify_guard_condition();
node_gc.trigger();
} catch (const rclcpp::exceptions::RCLError & ex) {
throw std::runtime_error(
std::string("failed to notify wait set on graph change: ") + ex.what());
@@ -788,15 +789,3 @@ rclcpp::TopicEndpointInfo::qos_profile() const
{
return qos_profile_;
}
rosidl_type_hash_t &
rclcpp::TopicEndpointInfo::topic_type_hash()
{
return topic_type_hash_;
}
const rosidl_type_hash_t &
rclcpp::TopicEndpointInfo::topic_type_hash() const
{
return topic_type_hash_;
}

View File

@@ -42,8 +42,9 @@ NodeServices::add_service(
group->add_service(service_base_ptr);
// Notify the executor that a new service was created using the parent Node.
auto & node_gc = node_base_->get_notify_guard_condition();
try {
node_base_->trigger_notify_guard_condition();
node_gc.trigger();
group->trigger_notify_guard_condition();
} catch (const rclcpp::exceptions::RCLError & ex) {
throw std::runtime_error(
@@ -68,8 +69,9 @@ NodeServices::add_client(
group->add_client(client_base_ptr);
// Notify the executor that a new client was created using the parent Node.
auto & node_gc = node_base_->get_notify_guard_condition();
try {
node_base_->trigger_notify_guard_condition();
node_gc.trigger();
group->trigger_notify_guard_condition();
} catch (const rclcpp::exceptions::RCLError & ex) {
throw std::runtime_error(

View File

@@ -42,8 +42,9 @@ NodeTimers::add_timer(
}
callback_group->add_timer(timer);
auto & node_gc = node_base_->get_notify_guard_condition();
try {
node_base_->trigger_notify_guard_condition();
node_gc.trigger();
callback_group->trigger_notify_guard_condition();
} catch (const rclcpp::exceptions::RCLError & ex) {
throw std::runtime_error(

View File

@@ -70,8 +70,9 @@ NodeTopics::add_publisher(
}
// Notify the executor that a new publisher was created using the parent Node.
auto & node_gc = node_base_->get_notify_guard_condition();
try {
node_base_->trigger_notify_guard_condition();
node_gc.trigger();
callback_group->trigger_notify_guard_condition();
} catch (const rclcpp::exceptions::RCLError & ex) {
throw std::runtime_error(
@@ -118,8 +119,9 @@ NodeTopics::add_subscription(
}
// Notify the executor that a new subscription was created using the parent Node.
auto & node_gc = node_base_->get_notify_guard_condition();
try {
node_base_->trigger_notify_guard_condition();
node_gc.trigger();
callback_group->trigger_notify_guard_condition();
} catch (const rclcpp::exceptions::RCLError & ex) {
throw std::runtime_error(

View File

@@ -42,8 +42,9 @@ NodeWaitables::add_waitable(
group->add_waitable(waitable_ptr);
// Notify the executor that a new waitable was created using the parent Node.
auto & node_gc = node_base_->get_notify_guard_condition();
try {
node_base_->trigger_notify_guard_condition();
node_gc.trigger();
group->trigger_notify_guard_condition();
} catch (const rclcpp::exceptions::RCLError & ex) {
throw std::runtime_error(

View File

@@ -44,7 +44,7 @@ SubscriptionBase::SubscriptionBase(
const rcl_subscription_options_t & subscription_options,
const SubscriptionEventCallbacks & event_callbacks,
bool use_default_callbacks,
DeliveredMessageKind delivered_message_kind)
SubscriptionType subscription_type)
: node_base_(node_base),
node_handle_(node_base_->get_shared_rcl_node_handle()),
node_logger_(rclcpp::get_node_logger(node_handle_.get())),
@@ -52,8 +52,16 @@ SubscriptionBase::SubscriptionBase(
intra_process_subscription_id_(0),
event_callbacks_(event_callbacks),
type_support_(type_support_handle),
delivered_message_type_(delivered_message_kind)
subscription_type_(subscription_type)
{
if (!rmw_feature_supported(RMW_MIDDLEWARE_CAN_TAKE_DYNAMIC_MESSAGE) &&
subscription_type == rclcpp::SubscriptionType::DYNAMIC_MESSAGE_DIRECT)
{
throw std::runtime_error(
"Cannot set subscription to take dynamic message directly, feature not supported in rmw"
);
}
auto custom_deletor = [node_handle = this->node_handle_](rcl_subscription_t * rcl_subs)
{
if (rcl_subscription_fini(rcl_subs, node_handle.get()) != RCL_RET_OK) {
@@ -258,16 +266,10 @@ SubscriptionBase::get_message_type_support_handle() const
return type_support_;
}
bool
SubscriptionBase::is_serialized() const
{
return delivered_message_type_ == rclcpp::DeliveredMessageKind::SERIALIZED_MESSAGE;
}
rclcpp::DeliveredMessageKind
rclcpp::SubscriptionType
SubscriptionBase::get_subscription_type() const
{
return delivered_message_type_;
return subscription_type_;
}
size_t
@@ -526,11 +528,21 @@ SubscriptionBase::get_content_filter() const
// DYNAMIC TYPE ==================================================================================
// TODO(methylDragon): Reorder later
bool
SubscriptionBase::take_dynamic_message(
rclcpp::dynamic_typesupport::DynamicMessage & /*message_out*/,
rclcpp::MessageInfo & /*message_info_out*/)
rclcpp::dynamic_typesupport::DynamicMessage & message_out,
rclcpp::MessageInfo & message_info_out)
{
throw std::runtime_error("Unimplemented");
return false;
rcl_ret_t ret = rcl_take_dynamic_message(
this->get_subscription_handle().get(),
&message_out.get_rosidl_dynamic_data(),
&message_info_out.get_rmw_message_info(),
nullptr);
if (RCL_RET_SUBSCRIPTION_TAKE_FAILED == ret) {
return false;
} else if (RCL_RET_OK != ret) {
rclcpp::exceptions::throw_from_rcl_error(ret);
}
return true;
}

View File

@@ -362,3 +362,42 @@ 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);
}
}

View File

@@ -618,14 +618,6 @@ if(TARGET test_timer)
target_link_libraries(test_timer ${PROJECT_NAME} mimick)
endif()
ament_add_gtest(test_timers_manager test_timers_manager.cpp
APPEND_LIBRARY_DIRS "${append_library_dirs}")
if(TARGET test_timers_manager)
ament_target_dependencies(test_timers_manager
"rcl")
target_link_libraries(test_timers_manager ${PROJECT_NAME} mimick)
endif()
ament_add_gtest(test_time_source test_time_source.cpp
APPEND_LIBRARY_DIRS "${append_library_dirs}")
if(TARGET test_time_source)
@@ -671,6 +663,14 @@ if(TARGET test_executors)
target_link_libraries(test_executors ${PROJECT_NAME})
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
APPEND_LIBRARY_DIRS "${append_library_dirs}")
if(TARGET test_multi_threaded_executor)
@@ -679,37 +679,13 @@ if(TARGET test_multi_threaded_executor)
target_link_libraries(test_multi_threaded_executor ${PROJECT_NAME})
endif()
ament_add_gtest(test_entities_collector executors/test_entities_collector.cpp
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_entities_collector)
ament_target_dependencies(test_entities_collector
if(TARGET test_static_executor_entities_collector)
ament_target_dependencies(test_static_executor_entities_collector
"rcl"
"test_msgs")
target_link_libraries(test_entities_collector ${PROJECT_NAME} mimick)
endif()
ament_add_gtest(test_executor_notify_waitable executors/test_executor_notify_waitable.cpp
APPEND_LIBRARY_DIRS "${append_library_dirs}" TIMEOUT 120)
if(TARGET test_executor_notify_waitable)
ament_target_dependencies(test_executor_notify_waitable
"rcl"
"test_msgs")
target_link_libraries(test_executor_notify_waitable ${PROJECT_NAME} mimick)
endif()
ament_add_gtest(test_events_executor executors/test_events_executor.cpp TIMEOUT 5)
if(TARGET test_events_executor)
ament_target_dependencies(test_events_executor
"test_msgs")
target_link_libraries(test_events_executor ${PROJECT_NAME})
endif()
ament_add_gtest(test_events_queue executors/test_events_queue.cpp
APPEND_LIBRARY_DIRS "${append_library_dirs}")
if(TARGET test_events_queue)
ament_target_dependencies(test_events_queue
"test_msgs")
target_link_libraries(test_events_queue ${PROJECT_NAME})
target_link_libraries(test_static_executor_entities_collector ${PROJECT_NAME} mimick)
endif()
ament_add_gtest(test_guard_condition test_guard_condition.cpp

View File

@@ -1,320 +0,0 @@
// Copyright 2023 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 <gtest/gtest.h>
#include "rclcpp/executors/executor_notify_waitable.hpp"
#include "rclcpp/rclcpp.hpp"
#include "rclcpp/executors/executor_entities_collector.hpp"
#include "../../utils/rclcpp_gtest_macros.hpp"
class TestExecutorEntitiesCollector : public ::testing::Test
{
public:
void SetUp()
{
rclcpp::init(0, nullptr);
notify_waitable = std::make_shared<rclcpp::executors::ExecutorNotifyWaitable>();
entities_collector = std::make_shared<rclcpp::executors::ExecutorEntitiesCollector>(
notify_waitable);
}
void TearDown()
{
rclcpp::shutdown();
}
std::shared_ptr<rclcpp::executors::ExecutorNotifyWaitable> notify_waitable;
std::shared_ptr<rclcpp::executors::ExecutorEntitiesCollector> entities_collector;
};
TEST_F(TestExecutorEntitiesCollector, add_remove_node) {
auto node1 = std::make_shared<rclcpp::Node>("node1", "ns");
// Add a node
EXPECT_NO_THROW(entities_collector->add_node(node1->get_node_base_interface()));
EXPECT_NO_THROW(entities_collector->update_collections());
// Remove a node
EXPECT_NO_THROW(entities_collector->remove_node(node1->get_node_base_interface()));
EXPECT_NO_THROW(entities_collector->update_collections());
}
TEST_F(TestExecutorEntitiesCollector, add_node_twice) {
auto node1 = std::make_shared<rclcpp::Node>("node1", "ns");
EXPECT_NO_THROW(entities_collector->add_node(node1->get_node_base_interface()));
RCLCPP_EXPECT_THROW_EQ(
entities_collector->add_node(node1->get_node_base_interface()),
std::runtime_error("Node '/ns/node1' has already been added to an executor."));
EXPECT_NO_THROW(entities_collector->update_collections());
}
TEST_F(TestExecutorEntitiesCollector, add_associated_node) {
auto node1 = std::make_shared<rclcpp::Node>("node1", "ns");
// Simulate node being associated somewhere else
auto & has_executor = node1->get_node_base_interface()->get_associated_with_executor_atomic();
has_executor.store(true);
// Add an already-associated node
RCLCPP_EXPECT_THROW_EQ(
entities_collector->remove_node(node1->get_node_base_interface()),
std::runtime_error("Node '/ns/node1' needs to be associated with this executor."));
has_executor.store(false);
}
TEST_F(TestExecutorEntitiesCollector, remove_unassociated_node) {
auto node1 = std::make_shared<rclcpp::Node>("node1", "ns");
// Add an already-associated node
RCLCPP_EXPECT_THROW_EQ(
entities_collector->remove_node(node1->get_node_base_interface()),
std::runtime_error("Node '/ns/node1' needs to be associated with an executor."));
// Simulate node being associated somewhere else
auto & has_executor = node1->get_node_base_interface()->get_associated_with_executor_atomic();
has_executor.store(true);
// Add an already-associated node
RCLCPP_EXPECT_THROW_EQ(
entities_collector->remove_node(node1->get_node_base_interface()),
std::runtime_error("Node '/ns/node1' needs to be associated with this executor."));
}
TEST_F(TestExecutorEntitiesCollector, add_remove_node_before_update) {
auto notify_waitable = std::make_shared<rclcpp::executors::ExecutorNotifyWaitable>();
auto entities_collector = rclcpp::executors::ExecutorEntitiesCollector(notify_waitable);
auto node1 = std::make_shared<rclcpp::Node>("node1", "ns");
auto node2 = std::make_shared<rclcpp::Node>("node2", "ns");
// Add and remove nodes without running updatenode
EXPECT_NO_THROW(entities_collector.add_node(node1->get_node_base_interface()));
EXPECT_NO_THROW(entities_collector.add_node(node2->get_node_base_interface()));
EXPECT_NO_THROW(entities_collector.remove_node(node1->get_node_base_interface()));
EXPECT_NO_THROW(entities_collector.remove_node(node2->get_node_base_interface()));
EXPECT_NO_THROW(entities_collector.update_collections());
}
TEST_F(TestExecutorEntitiesCollector, add_callback_group) {
auto notify_waitable = std::make_shared<rclcpp::executors::ExecutorNotifyWaitable>();
auto entities_collector = rclcpp::executors::ExecutorEntitiesCollector(notify_waitable);
auto node = std::make_shared<rclcpp::Node>("node1", "ns");
rclcpp::CallbackGroup::SharedPtr cb_group = node->create_callback_group(
rclcpp::CallbackGroupType::MutuallyExclusive);
// Add a callback group and update
entities_collector.add_callback_group(cb_group);
ASSERT_EQ(entities_collector.get_all_callback_groups().size(), 0u);
ASSERT_EQ(entities_collector.get_manually_added_callback_groups().size(), 0u);
ASSERT_EQ(entities_collector.get_automatically_added_callback_groups().size(), 0u);
entities_collector.update_collections();
ASSERT_EQ(entities_collector.get_all_callback_groups().size(), 1u);
ASSERT_EQ(entities_collector.get_manually_added_callback_groups().size(), 1u);
ASSERT_EQ(entities_collector.get_automatically_added_callback_groups().size(), 0u);
// Remove callback group and update
entities_collector.remove_callback_group(cb_group);
ASSERT_EQ(entities_collector.get_all_callback_groups().size(), 1u);
ASSERT_EQ(entities_collector.get_manually_added_callback_groups().size(), 1u);
ASSERT_EQ(entities_collector.get_automatically_added_callback_groups().size(), 0u);
entities_collector.update_collections();
ASSERT_EQ(entities_collector.get_all_callback_groups().size(), 0u);
ASSERT_EQ(entities_collector.get_manually_added_callback_groups().size(), 0u);
ASSERT_EQ(entities_collector.get_automatically_added_callback_groups().size(), 0u);
}
TEST_F(TestExecutorEntitiesCollector, add_node_default_callback_group) {
auto notify_waitable = std::make_shared<rclcpp::executors::ExecutorNotifyWaitable>();
auto entities_collector = rclcpp::executors::ExecutorEntitiesCollector(notify_waitable);
auto node = std::make_shared<rclcpp::Node>("node1", "ns");
entities_collector.add_node(node->get_node_base_interface());
ASSERT_EQ(entities_collector.get_all_callback_groups().size(), 0u);
ASSERT_EQ(entities_collector.get_manually_added_callback_groups().size(), 0u);
ASSERT_EQ(entities_collector.get_automatically_added_callback_groups().size(), 0u);
entities_collector.update_collections();
ASSERT_EQ(entities_collector.get_all_callback_groups().size(), 1u);
ASSERT_EQ(entities_collector.get_manually_added_callback_groups().size(), 0u);
ASSERT_EQ(entities_collector.get_automatically_added_callback_groups().size(), 1u);
}
TEST_F(TestExecutorEntitiesCollector, add_callback_group_after_add_node) {
auto notify_waitable = std::make_shared<rclcpp::executors::ExecutorNotifyWaitable>();
auto entities_collector = rclcpp::executors::ExecutorEntitiesCollector(notify_waitable);
auto node = std::make_shared<rclcpp::Node>("node1", "ns");
rclcpp::CallbackGroup::SharedPtr cb_group = node->create_callback_group(
rclcpp::CallbackGroupType::MutuallyExclusive);
entities_collector.add_node(node->get_node_base_interface());
ASSERT_EQ(entities_collector.get_all_callback_groups().size(), 0u);
ASSERT_EQ(entities_collector.get_manually_added_callback_groups().size(), 0u);
ASSERT_EQ(entities_collector.get_automatically_added_callback_groups().size(), 0u);
entities_collector.update_collections();
ASSERT_EQ(entities_collector.get_all_callback_groups().size(), 2u);
ASSERT_EQ(entities_collector.get_manually_added_callback_groups().size(), 0u);
ASSERT_EQ(entities_collector.get_automatically_added_callback_groups().size(), 2u);
RCLCPP_EXPECT_THROW_EQ(
entities_collector.add_callback_group(cb_group),
std::runtime_error("Callback group has already been added to an executor."));
}
TEST_F(TestExecutorEntitiesCollector, add_callback_group_twice) {
auto notify_waitable = std::make_shared<rclcpp::executors::ExecutorNotifyWaitable>();
auto entities_collector = rclcpp::executors::ExecutorEntitiesCollector(notify_waitable);
auto node = std::make_shared<rclcpp::Node>("node1", "ns");
rclcpp::CallbackGroup::SharedPtr cb_group = node->create_callback_group(
rclcpp::CallbackGroupType::MutuallyExclusive);
entities_collector.add_callback_group(cb_group);
ASSERT_EQ(entities_collector.get_all_callback_groups().size(), 0u);
ASSERT_EQ(entities_collector.get_manually_added_callback_groups().size(), 0u);
ASSERT_EQ(entities_collector.get_automatically_added_callback_groups().size(), 0u);
entities_collector.update_collections();
ASSERT_EQ(entities_collector.get_all_callback_groups().size(), 1u);
ASSERT_EQ(entities_collector.get_manually_added_callback_groups().size(), 1u);
ASSERT_EQ(entities_collector.get_automatically_added_callback_groups().size(), 0u);
cb_group->get_associated_with_executor_atomic().exchange(false);
RCLCPP_EXPECT_THROW_EQ(
entities_collector.add_callback_group(cb_group),
std::runtime_error("Callback group has already been added to this executor."));
}
TEST_F(TestExecutorEntitiesCollector, remove_callback_group_after_node) {
auto notify_waitable = std::make_shared<rclcpp::executors::ExecutorNotifyWaitable>();
auto entities_collector = rclcpp::executors::ExecutorEntitiesCollector(notify_waitable);
auto node = std::make_shared<rclcpp::Node>("node1", "ns");
rclcpp::CallbackGroup::SharedPtr cb_group = node->create_callback_group(
rclcpp::CallbackGroupType::MutuallyExclusive);
entities_collector.add_callback_group(cb_group);
ASSERT_EQ(entities_collector.get_all_callback_groups().size(), 0u);
ASSERT_EQ(entities_collector.get_manually_added_callback_groups().size(), 0u);
ASSERT_EQ(entities_collector.get_automatically_added_callback_groups().size(), 0u);
entities_collector.update_collections();
ASSERT_EQ(entities_collector.get_all_callback_groups().size(), 1u);
ASSERT_EQ(entities_collector.get_manually_added_callback_groups().size(), 1u);
ASSERT_EQ(entities_collector.get_automatically_added_callback_groups().size(), 0u);
node.reset();
/**
* TODO(mjcarroll): Assert this when we are enforcing that nodes must be destroyed
* after their created callback groups.
RCLCPP_EXPECT_THROW_EQ(
entities_collector.remove_callback_group(cb_group),
std::runtime_error("Node must not be deleted before its callback group(s)."));
*/
EXPECT_NO_THROW(entities_collector.update_collections());
}
TEST_F(TestExecutorEntitiesCollector, remove_callback_group_after_node2) {
auto notify_waitable = std::make_shared<rclcpp::executors::ExecutorNotifyWaitable>();
auto entities_collector = rclcpp::executors::ExecutorEntitiesCollector(notify_waitable);
auto node = std::make_shared<rclcpp::Node>("node1", "ns");
rclcpp::CallbackGroup::SharedPtr cb_group = node->create_callback_group(
rclcpp::CallbackGroupType::MutuallyExclusive);
entities_collector.add_callback_group(cb_group);
ASSERT_EQ(entities_collector.get_all_callback_groups().size(), 0u);
ASSERT_EQ(entities_collector.get_manually_added_callback_groups().size(), 0u);
ASSERT_EQ(entities_collector.get_automatically_added_callback_groups().size(), 0u);
entities_collector.update_collections();
ASSERT_EQ(entities_collector.get_all_callback_groups().size(), 1u);
ASSERT_EQ(entities_collector.get_manually_added_callback_groups().size(), 1u);
ASSERT_EQ(entities_collector.get_automatically_added_callback_groups().size(), 0u);
EXPECT_NO_THROW(entities_collector.remove_callback_group(cb_group));
node.reset();
/**
* TODO(mjcarroll): Assert this when we are enforcing that nodes must be destroyed
* after their created callback groups.
RCLCPP_EXPECT_THROW_EQ(
entities_collector.remove_callback_group(cb_group),
std::runtime_error("Node must not be deleted before its callback group(s)."));
*/
EXPECT_NO_THROW(entities_collector.update_collections());
}
TEST_F(TestExecutorEntitiesCollector, remove_callback_group_twice) {
auto notify_waitable = std::make_shared<rclcpp::executors::ExecutorNotifyWaitable>();
auto entities_collector = rclcpp::executors::ExecutorEntitiesCollector(notify_waitable);
auto node = std::make_shared<rclcpp::Node>("node1", "ns");
rclcpp::CallbackGroup::SharedPtr cb_group = node->create_callback_group(
rclcpp::CallbackGroupType::MutuallyExclusive);
entities_collector.add_callback_group(cb_group);
entities_collector.update_collections();
ASSERT_EQ(entities_collector.get_all_callback_groups().size(), 1u);
ASSERT_EQ(entities_collector.get_manually_added_callback_groups().size(), 1u);
ASSERT_EQ(entities_collector.get_automatically_added_callback_groups().size(), 0u);
entities_collector.remove_callback_group(cb_group);
entities_collector.update_collections();
RCLCPP_EXPECT_THROW_EQ(
entities_collector.remove_callback_group(cb_group),
std::runtime_error("Callback group needs to be associated with an executor."));
}
TEST_F(TestExecutorEntitiesCollector, remove_node_opposite_order) {
auto notify_waitable = std::make_shared<rclcpp::executors::ExecutorNotifyWaitable>();
auto entities_collector = rclcpp::executors::ExecutorEntitiesCollector(notify_waitable);
auto node1 = std::make_shared<rclcpp::Node>("node1", "ns");
EXPECT_NO_THROW(entities_collector.add_node(node1->get_node_base_interface()));
auto node2 = std::make_shared<rclcpp::Node>("node2", "ns");
EXPECT_NO_THROW(entities_collector.add_node(node2->get_node_base_interface()));
EXPECT_NO_THROW(entities_collector.remove_node(node2->get_node_base_interface()));
}

View File

@@ -1,549 +0,0 @@
// Copyright 2023 iRobot Corporation.
//
// 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 <gtest/gtest.h>
#include <chrono>
#include <memory>
#include <string>
#include "rclcpp/experimental/executors/events_executor/events_executor.hpp"
#include "test_msgs/srv/empty.hpp"
#include "test_msgs/msg/empty.hpp"
using namespace std::chrono_literals;
using rclcpp::experimental::executors::EventsExecutor;
class TestEventsExecutor : public ::testing::Test
{
public:
void SetUp()
{
rclcpp::init(0, nullptr);
}
void TearDown()
{
rclcpp::shutdown();
}
};
TEST_F(TestEventsExecutor, run_pub_sub)
{
// rmw_connextdds doesn't support events-executor
if (std::string(rmw_get_implementation_identifier()).find("rmw_connextdds") == 0) {
GTEST_SKIP();
}
auto node = std::make_shared<rclcpp::Node>("node");
bool msg_received = false;
auto subscription = node->create_subscription<test_msgs::msg::Empty>(
"topic", rclcpp::SensorDataQoS(),
[&msg_received](test_msgs::msg::Empty::ConstSharedPtr msg)
{
(void)msg;
msg_received = true;
});
auto publisher = node->create_publisher<test_msgs::msg::Empty>("topic", rclcpp::SensorDataQoS());
EventsExecutor executor;
executor.add_node(node);
bool spin_exited = false;
std::thread spinner([&spin_exited, &executor, this]() {
executor.spin();
spin_exited = true;
});
auto msg = std::make_unique<test_msgs::msg::Empty>();
publisher->publish(std::move(msg));
// Wait some time for the subscription to receive the message
auto start = std::chrono::high_resolution_clock::now();
while (
!msg_received &&
!spin_exited &&
(std::chrono::high_resolution_clock::now() - start < 1s))
{
auto time = std::chrono::high_resolution_clock::now() - start;
auto time_msec = std::chrono::duration_cast<std::chrono::milliseconds>(time);
std::this_thread::sleep_for(25ms);
}
executor.cancel();
spinner.join();
executor.remove_node(node);
EXPECT_TRUE(msg_received);
EXPECT_TRUE(spin_exited);
}
TEST_F(TestEventsExecutor, run_clients_servers)
{
// rmw_connextdds doesn't support events-executor
if (std::string(rmw_get_implementation_identifier()).find("rmw_connextdds") == 0) {
GTEST_SKIP();
}
auto node = std::make_shared<rclcpp::Node>("node");
bool request_received = false;
bool response_received = false;
auto service =
node->create_service<test_msgs::srv::Empty>(
"service",
[&request_received](
const test_msgs::srv::Empty::Request::SharedPtr,
test_msgs::srv::Empty::Response::SharedPtr)
{
request_received = true;
});
auto client = node->create_client<test_msgs::srv::Empty>("service");
EventsExecutor executor;
executor.add_node(node);
bool spin_exited = false;
std::thread spinner([&spin_exited, &executor, this]() {
executor.spin();
spin_exited = true;
});
auto request = std::make_shared<test_msgs::srv::Empty::Request>();
client->async_send_request(
request,
[&response_received](rclcpp::Client<test_msgs::srv::Empty>::SharedFuture result_future) {
(void)result_future;
response_received = true;
});
// Wait some time for the client-server to be invoked
auto start = std::chrono::steady_clock::now();
while (
!response_received &&
!spin_exited &&
(std::chrono::steady_clock::now() - start < 1s))
{
std::this_thread::sleep_for(5ms);
}
executor.cancel();
spinner.join();
executor.remove_node(node);
EXPECT_TRUE(request_received);
EXPECT_TRUE(response_received);
EXPECT_TRUE(spin_exited);
}
TEST_F(TestEventsExecutor, spin_once_max_duration_timeout)
{
// rmw_connextdds doesn't support events-executor
if (std::string(rmw_get_implementation_identifier()).find("rmw_connextdds") == 0) {
GTEST_SKIP();
}
auto node = std::make_shared<rclcpp::Node>("node");
EventsExecutor executor;
executor.add_node(node);
// Consume previous events so we have a fresh start
executor.spin_all(1s);
size_t t_runs = 0;
auto t = node->create_wall_timer(
10s,
[&]() {
t_runs++;
});
// This first spin_once takes care of the waitable event
// generated by the addition of the timer to the node
executor.spin_once(1s);
EXPECT_EQ(0u, t_runs);
auto start = std::chrono::steady_clock::now();
// This second spin_once should take care of the timer,
executor.spin_once(10ms);
// but doesn't spin the time enough to call the timer callback.
EXPECT_EQ(0u, t_runs);
EXPECT_TRUE(std::chrono::steady_clock::now() - start < 200ms);
}
TEST_F(TestEventsExecutor, spin_once_max_duration_timer)
{
// rmw_connextdds doesn't support events-executor
if (std::string(rmw_get_implementation_identifier()).find("rmw_connextdds") == 0) {
GTEST_SKIP();
}
auto node = std::make_shared<rclcpp::Node>("node");
EventsExecutor executor;
executor.add_node(node);
// Consume previous events so we have a fresh start
executor.spin_all(1s);
size_t t_runs = 0;
auto t = node->create_wall_timer(
10ms,
[&]() {
t_runs++;
});
// This first spin_once takes care of the waitable event
// generated by the addition of the timer to the node
executor.spin_once(1s);
EXPECT_EQ(0u, t_runs);
auto start = std::chrono::steady_clock::now();
// This second spin_once should take care of the timer
executor.spin_once(11ms);
EXPECT_EQ(1u, t_runs);
EXPECT_TRUE(std::chrono::steady_clock::now() - start < 200ms);
}
TEST_F(TestEventsExecutor, spin_some_max_duration)
{
// rmw_connextdds doesn't support events-executor
if (std::string(rmw_get_implementation_identifier()).find("rmw_connextdds") == 0) {
GTEST_SKIP();
}
{
auto node = std::make_shared<rclcpp::Node>("node");
size_t t_runs = 0;
auto t = node->create_wall_timer(
10s,
[&]() {
t_runs++;
});
EventsExecutor executor;
executor.add_node(node);
auto start = std::chrono::steady_clock::now();
executor.spin_some(10ms);
EXPECT_EQ(0u, t_runs);
EXPECT_TRUE(std::chrono::steady_clock::now() - start < 200ms);
}
{
auto node = std::make_shared<rclcpp::Node>("node");
size_t t_runs = 0;
auto t = node->create_wall_timer(
10ms,
[&]() {
t_runs++;
});
// Sleep some time for the timer to be ready when spin
std::this_thread::sleep_for(10ms);
EventsExecutor executor;
executor.add_node(node);
auto start = std::chrono::steady_clock::now();
executor.spin_some(10s);
EXPECT_EQ(1u, t_runs);
EXPECT_TRUE(std::chrono::steady_clock::now() - start < 200ms);
}
}
TEST_F(TestEventsExecutor, spin_some_zero_duration)
{
// rmw_connextdds doesn't support events-executor
if (std::string(rmw_get_implementation_identifier()).find("rmw_connextdds") == 0) {
GTEST_SKIP();
}
auto node = std::make_shared<rclcpp::Node>("node");
size_t t_runs = 0;
auto t = node->create_wall_timer(
20ms,
[&]() {
t_runs++;
});
// Sleep some time for the timer to be ready when spin
std::this_thread::sleep_for(20ms);
EventsExecutor executor;
executor.add_node(node);
executor.spin_some(0ms);
EXPECT_EQ(1u, t_runs);
}
TEST_F(TestEventsExecutor, spin_all_max_duration)
{
// rmw_connextdds doesn't support events-executor
if (std::string(rmw_get_implementation_identifier()).find("rmw_connextdds") == 0) {
GTEST_SKIP();
}
{
auto node = std::make_shared<rclcpp::Node>("node");
size_t t_runs = 0;
auto t = node->create_wall_timer(
10s,
[&]() {
t_runs++;
});
EventsExecutor executor;
executor.add_node(node);
auto start = std::chrono::steady_clock::now();
executor.spin_all(10ms);
EXPECT_EQ(0u, t_runs);
EXPECT_TRUE(std::chrono::steady_clock::now() - start < 200ms);
}
{
auto node = std::make_shared<rclcpp::Node>("node");
size_t t_runs = 0;
auto t = node->create_wall_timer(
10ms,
[&]() {
t_runs++;
});
// Sleep some time for the timer to be ready when spin
std::this_thread::sleep_for(10ms);
EventsExecutor executor;
executor.add_node(node);
auto start = std::chrono::steady_clock::now();
executor.spin_all(10s);
EXPECT_EQ(1u, t_runs);
EXPECT_TRUE(std::chrono::steady_clock::now() - start < 200ms);
}
EventsExecutor executor;
EXPECT_THROW(executor.spin_all(0ms), std::invalid_argument);
EXPECT_THROW(executor.spin_all(-5ms), std::invalid_argument);
}
TEST_F(TestEventsExecutor, cancel_while_timers_running)
{
// rmw_connextdds doesn't support events-executor
if (std::string(rmw_get_implementation_identifier()).find("rmw_connextdds") == 0) {
GTEST_SKIP();
}
auto node = std::make_shared<rclcpp::Node>("node");
EventsExecutor executor;
executor.add_node(node);
// Take care of previous events for a fresh start
executor.spin_all(1s);
size_t t1_runs = 0;
auto t1 = node->create_wall_timer(
1ms,
[&]() {
t1_runs++;
std::this_thread::sleep_for(50ms);
});
size_t t2_runs = 0;
auto t2 = node->create_wall_timer(
1ms,
[&]() {
t2_runs++;
std::this_thread::sleep_for(50ms);
});
std::thread spinner([&executor, this]() {executor.spin();});
std::this_thread::sleep_for(10ms);
// Call cancel while t1 callback is still being executed
executor.cancel();
spinner.join();
// Depending on the latency on the system, t2 may start to execute before cancel is signaled
EXPECT_GE(1u, t1_runs);
EXPECT_GE(1u, t2_runs);
}
TEST_F(TestEventsExecutor, cancel_while_timers_waiting)
{
// rmw_connextdds doesn't support events-executor
if (std::string(rmw_get_implementation_identifier()).find("rmw_connextdds") == 0) {
GTEST_SKIP();
}
auto node = std::make_shared<rclcpp::Node>("node");
size_t t1_runs = 0;
auto t1 = node->create_wall_timer(
100s,
[&]() {
t1_runs++;
});
EventsExecutor executor;
executor.add_node(node);
auto start = std::chrono::steady_clock::now();
std::thread spinner([&executor, this]() {executor.spin();});
std::this_thread::sleep_for(10ms);
executor.cancel();
spinner.join();
EXPECT_EQ(0u, t1_runs);
EXPECT_TRUE(std::chrono::steady_clock::now() - start < 1s);
}
TEST_F(TestEventsExecutor, destroy_entities)
{
// This test fails on Windows! We skip it for now
GTEST_SKIP();
// rmw_connextdds doesn't support events-executor
if (std::string(rmw_get_implementation_identifier()).find("rmw_connextdds") == 0) {
GTEST_SKIP();
}
// Create a publisher node and start publishing messages
auto node_pub = std::make_shared<rclcpp::Node>("node_pub");
auto publisher = node_pub->create_publisher<test_msgs::msg::Empty>("topic", rclcpp::QoS(10));
auto timer = node_pub->create_wall_timer(
2ms, [&]() {publisher->publish(std::make_unique<test_msgs::msg::Empty>());});
EventsExecutor executor_pub;
executor_pub.add_node(node_pub);
std::thread spinner([&executor_pub, this]() {executor_pub.spin();});
// Create a node with two different subscriptions to the topic
auto node_sub = std::make_shared<rclcpp::Node>("node_sub");
size_t callback_count_1 = 0;
auto subscription_1 =
node_sub->create_subscription<test_msgs::msg::Empty>(
"topic", rclcpp::QoS(10), [&](test_msgs::msg::Empty::ConstSharedPtr) {callback_count_1++;});
size_t callback_count_2 = 0;
auto subscription_2 =
node_sub->create_subscription<test_msgs::msg::Empty>(
"topic", rclcpp::QoS(10), [&](test_msgs::msg::Empty::ConstSharedPtr) {callback_count_2++;});
EventsExecutor executor_sub;
executor_sub.add_node(node_sub);
// Wait some time while messages are published
std::this_thread::sleep_for(10ms);
// Destroy one of the two subscriptions
subscription_1.reset();
// Let subscriptions executor spin
executor_sub.spin_some(10ms);
// The callback count of the destroyed subscription remained at 0
EXPECT_EQ(0u, callback_count_1);
EXPECT_LT(0u, callback_count_2);
executor_pub.cancel();
spinner.join();
}
// Testing construction of a subscriptions with QoS event callback functions.
std::string * g_pub_log_msg;
std::string * g_sub_log_msg;
std::promise<void> * g_log_msgs_promise;
TEST_F(TestEventsExecutor, test_default_incompatible_qos_callbacks)
{
// rmw_connextdds doesn't support events-executor
if (std::string(rmw_get_implementation_identifier()).find("rmw_connextdds") == 0) {
GTEST_SKIP();
}
auto node = std::make_shared<rclcpp::Node>("node");
rcutils_logging_output_handler_t original_output_handler = rcutils_logging_get_output_handler();
std::string pub_log_msg;
std::string sub_log_msg;
std::promise<void> log_msgs_promise;
g_pub_log_msg = &pub_log_msg;
g_sub_log_msg = &sub_log_msg;
g_log_msgs_promise = &log_msgs_promise;
auto logger_callback = [](
const rcutils_log_location_t * /*location*/,
int /*level*/, const char * /*name*/, rcutils_time_point_value_t /*timestamp*/,
const char * format, va_list * args) -> void {
char buffer[1024];
vsnprintf(buffer, sizeof(buffer), format, *args);
const std::string msg = buffer;
if (msg.rfind("New subscription discovered on topic '/test_topic'", 0) == 0) {
*g_pub_log_msg = buffer;
} else if (msg.rfind("New publisher discovered on topic '/test_topic'", 0) == 0) {
*g_sub_log_msg = buffer;
}
if (!g_pub_log_msg->empty() && !g_sub_log_msg->empty()) {
g_log_msgs_promise->set_value();
}
};
rcutils_logging_set_output_handler(logger_callback);
std::shared_future<void> log_msgs_future = log_msgs_promise.get_future();
rclcpp::QoS qos_profile_publisher(10);
qos_profile_publisher.durability(RMW_QOS_POLICY_DURABILITY_VOLATILE);
auto publisher = node->create_publisher<test_msgs::msg::Empty>(
"test_topic", qos_profile_publisher);
rclcpp::QoS qos_profile_subscription(10);
qos_profile_subscription.durability(RMW_QOS_POLICY_DURABILITY_TRANSIENT_LOCAL);
auto subscription = node->create_subscription<test_msgs::msg::Empty>(
"test_topic", qos_profile_subscription, [&](test_msgs::msg::Empty::ConstSharedPtr) {});
EventsExecutor ex;
ex.add_node(node->get_node_base_interface());
const auto timeout = std::chrono::seconds(10);
ex.spin_until_future_complete(log_msgs_future, timeout);
EXPECT_EQ(
"New subscription discovered on topic '/test_topic', requesting incompatible QoS. "
"No messages will be sent to it. Last incompatible policy: DURABILITY_QOS_POLICY",
pub_log_msg);
EXPECT_EQ(
"New publisher discovered on topic '/test_topic', offering incompatible QoS. "
"No messages will be sent to it. Last incompatible policy: DURABILITY_QOS_POLICY",
sub_log_msg);
rcutils_logging_set_output_handler(original_output_handler);
}

View File

@@ -1,82 +0,0 @@
// Copyright 2023 iRobot Corporation.
//
// 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 <gtest/gtest.h>
#include <memory>
#include "rclcpp/experimental/executors/events_executor/events_executor_event_types.hpp"
#include "rclcpp/experimental/executors/events_executor/simple_events_queue.hpp"
using namespace std::chrono_literals;
TEST(TestEventsQueue, SimpleQueueTest)
{
// Create a SimpleEventsQueue and a local queue
auto simple_queue = std::make_unique<rclcpp::experimental::executors::SimpleEventsQueue>();
rclcpp::experimental::executors::ExecutorEvent event {};
bool ret = false;
// Make sure the queue is empty at startup
EXPECT_TRUE(simple_queue->empty());
EXPECT_EQ(simple_queue->size(), 0u);
// Push 11 messages
for (uint32_t i = 1; i < 11; i++) {
rclcpp::experimental::executors::ExecutorEvent stub_event {};
stub_event.num_events = 1;
simple_queue->enqueue(stub_event);
EXPECT_FALSE(simple_queue->empty());
EXPECT_EQ(simple_queue->size(), i);
}
// Pop one message
ret = simple_queue->dequeue(event);
EXPECT_TRUE(ret);
EXPECT_FALSE(simple_queue->empty());
EXPECT_EQ(simple_queue->size(), 9u);
// Pop one message
ret = simple_queue->dequeue(event, std::chrono::nanoseconds(0));
EXPECT_TRUE(ret);
EXPECT_FALSE(simple_queue->empty());
EXPECT_EQ(simple_queue->size(), 8u);
while (!simple_queue->empty()) {
ret = simple_queue->dequeue(event);
EXPECT_TRUE(ret);
}
EXPECT_TRUE(simple_queue->empty());
EXPECT_EQ(simple_queue->size(), 0u);
ret = simple_queue->dequeue(event, std::chrono::nanoseconds(0));
EXPECT_FALSE(ret);
// Lets push an event into the queue and get it back
rclcpp::experimental::executors::ExecutorEvent push_event = {
simple_queue.get(),
99,
rclcpp::experimental::executors::ExecutorEventType::SUBSCRIPTION_EVENT,
1};
simple_queue->enqueue(push_event);
ret = simple_queue->dequeue(event);
EXPECT_TRUE(ret);
EXPECT_EQ(push_event.entity_key, event.entity_key);
EXPECT_EQ(push_event.waitable_data, event.waitable_data);
EXPECT_EQ(push_event.type, event.type);
EXPECT_EQ(push_event.num_events, event.num_events);
}

View File

@@ -1,97 +0,0 @@
// Copyright 2023 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 <gtest/gtest.h>
#include <chrono>
#include <memory>
#include <stdexcept>
#include "rclcpp/executors/single_threaded_executor.hpp"
#include "rclcpp/rclcpp.hpp"
#include "rcpputils/scope_exit.hpp"
#include "rclcpp/executors/executor_notify_waitable.hpp"
#include "../../utils/rclcpp_gtest_macros.hpp"
class TestExecutorNotifyWaitable : public ::testing::Test
{
public:
void SetUp()
{
rclcpp::init(0, nullptr);
}
void TearDown()
{
rclcpp::shutdown();
}
};
TEST_F(TestExecutorNotifyWaitable, construct_destruct) {
{
auto waitable = std::make_shared<rclcpp::executors::ExecutorNotifyWaitable>();
waitable.reset();
}
{
auto on_execute_callback = []() {};
auto waitable =
std::make_shared<rclcpp::executors::ExecutorNotifyWaitable>(on_execute_callback);
waitable.reset();
}
}
TEST_F(TestExecutorNotifyWaitable, add_remove_guard_conditions) {
auto on_execute_callback = []() {};
auto waitable =
std::make_shared<rclcpp::executors::ExecutorNotifyWaitable>(on_execute_callback);
auto node = std::make_shared<rclcpp::Node>("my_node", "/ns");
auto notify_guard_condition =
node->get_node_base_interface()->get_shared_notify_guard_condition();
EXPECT_NO_THROW(waitable->add_guard_condition(notify_guard_condition));
EXPECT_NO_THROW(waitable->remove_guard_condition(notify_guard_condition));
}
TEST_F(TestExecutorNotifyWaitable, wait) {
int on_execute_calls = 0;
auto on_execute_callback = [&on_execute_calls]() {on_execute_calls++;};
auto waitable =
std::make_shared<rclcpp::executors::ExecutorNotifyWaitable>(on_execute_callback);
auto node = std::make_shared<rclcpp::Node>("my_node", "/ns");
auto notify_guard_condition =
node->get_node_base_interface()->get_shared_notify_guard_condition();
EXPECT_NO_THROW(waitable->add_guard_condition(notify_guard_condition));
auto default_cbg = node->get_node_base_interface()->get_default_callback_group();
ASSERT_NE(nullptr, default_cbg->get_notify_guard_condition());
auto waitables = node->get_node_waitables_interface();
waitables->add_waitable(std::static_pointer_cast<rclcpp::Waitable>(waitable), default_cbg);
rclcpp::executors::SingleThreadedExecutor executor;
executor.add_node(node);
executor.spin_all(std::chrono::seconds(1));
EXPECT_EQ(1u, on_execute_calls);
// on_execute_callback doesn't change if the topology doesn't change
executor.spin_all(std::chrono::seconds(1));
EXPECT_EQ(1u, on_execute_calls);
}

View File

@@ -83,6 +83,8 @@ public:
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>
class TestExecutorsStable : public TestExecutors<T> {};
@@ -90,8 +92,7 @@ using ExecutorTypes =
::testing::Types<
rclcpp::executors::SingleThreadedExecutor,
rclcpp::executors::MultiThreadedExecutor,
rclcpp::executors::StaticSingleThreadedExecutor,
rclcpp::experimental::executors::EventsExecutor>;
rclcpp::executors::StaticSingleThreadedExecutor>;
class ExecutorTypeNames
{
@@ -112,10 +113,6 @@ public:
return "StaticSingleThreadedExecutor";
}
if (std::is_same<T, rclcpp::experimental::executors::EventsExecutor>()) {
return "EventsExecutor";
}
return "";
}
};
@@ -124,18 +121,17 @@ public:
// is updated.
TYPED_TEST_SUITE(TestExecutors, ExecutorTypes, ExecutorTypeNames);
// Make sure that executors detach from nodes when destructing
TYPED_TEST(TestExecutors, detachOnDestruction)
{
using ExecutorType = TypeParam;
// rmw_connextdds doesn't support events-executor
if (
std::is_same<ExecutorType, rclcpp::experimental::executors::EventsExecutor>() &&
std::string(rmw_get_implementation_identifier()).find("rmw_connextdds") == 0)
{
GTEST_SKIP();
}
// 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>;
TYPED_TEST_SUITE(TestExecutorsStable, StandardExecutors, ExecutorTypeNames);
// Make sure that executors detach from nodes when destructing
TYPED_TEST(TestExecutors, detachOnDestruction) {
using ExecutorType = TypeParam;
{
ExecutorType executor;
executor.add_node(this->node);
@@ -147,16 +143,10 @@ TYPED_TEST(TestExecutors, detachOnDestruction)
}
// Make sure that the executor can automatically remove expired nodes correctly
TYPED_TEST(TestExecutors, addTemporaryNode) {
// Currently fails for StaticSingleThreadedExecutor so it is being skipped, see:
// https://github.com/ros2/rclcpp/issues/1231
TYPED_TEST(TestExecutorsStable, addTemporaryNode) {
using ExecutorType = TypeParam;
// rmw_connextdds doesn't support events-executor
if (
std::is_same<ExecutorType, rclcpp::experimental::executors::EventsExecutor>() &&
std::string(rmw_get_implementation_identifier()).find("rmw_connextdds") == 0)
{
GTEST_SKIP();
}
ExecutorType executor;
{
@@ -173,37 +163,9 @@ TYPED_TEST(TestExecutors, addTemporaryNode) {
spinner.join();
}
// Make sure that a spinning empty executor can be cancelled
TYPED_TEST(TestExecutors, emptyExecutor)
{
using ExecutorType = TypeParam;
// rmw_connextdds doesn't support events-executor
if (
std::is_same<ExecutorType, rclcpp::experimental::executors::EventsExecutor>() &&
std::string(rmw_get_implementation_identifier()).find("rmw_connextdds") == 0)
{
GTEST_SKIP();
}
ExecutorType executor;
std::thread spinner([&]() {EXPECT_NO_THROW(executor.spin());});
std::this_thread::sleep_for(50ms);
executor.cancel();
spinner.join();
}
// Check executor throws properly if the same node is added a second time
TYPED_TEST(TestExecutors, addNodeTwoExecutors)
{
TYPED_TEST(TestExecutors, addNodeTwoExecutors) {
using ExecutorType = TypeParam;
// rmw_connextdds doesn't support events-executor
if (
std::is_same<ExecutorType, rclcpp::experimental::executors::EventsExecutor>() &&
std::string(rmw_get_implementation_identifier()).find("rmw_connextdds") == 0)
{
GTEST_SKIP();
}
ExecutorType executor1;
ExecutorType executor2;
EXPECT_NO_THROW(executor1.add_node(this->node));
@@ -212,17 +174,8 @@ TYPED_TEST(TestExecutors, addNodeTwoExecutors)
}
// Check simple spin example
TYPED_TEST(TestExecutors, spinWithTimer)
{
TYPED_TEST(TestExecutors, spinWithTimer) {
using ExecutorType = TypeParam;
// rmw_connextdds doesn't support events-executor
if (
std::is_same<ExecutorType, rclcpp::experimental::executors::EventsExecutor>() &&
std::string(rmw_get_implementation_identifier()).find("rmw_connextdds") == 0)
{
GTEST_SKIP();
}
ExecutorType executor;
bool timer_completed = false;
@@ -243,31 +196,19 @@ TYPED_TEST(TestExecutors, spinWithTimer)
executor.remove_node(this->node, true);
}
TYPED_TEST(TestExecutors, spinWhileAlreadySpinning)
{
TYPED_TEST(TestExecutors, spinWhileAlreadySpinning) {
using ExecutorType = TypeParam;
// rmw_connextdds doesn't support events-executor
if (
std::is_same<ExecutorType, rclcpp::experimental::executors::EventsExecutor>() &&
std::string(rmw_get_implementation_identifier()).find("rmw_connextdds") == 0)
{
GTEST_SKIP();
}
ExecutorType executor;
executor.add_node(this->node);
std::atomic_bool timer_completed = false;
auto timer = this->node->create_wall_timer(
1ms, [&]() {
timer_completed.store(true);
});
bool timer_completed = false;
auto timer = this->node->create_wall_timer(1ms, [&]() {timer_completed = true;});
std::thread spinner([&]() {executor.spin();});
// Sleep for a short time to verify executor.spin() is going, and didn't throw.
auto start = std::chrono::steady_clock::now();
while (!timer_completed.load() && (std::chrono::steady_clock::now() - start) < 10s) {
while (!timer_completed && (std::chrono::steady_clock::now() - start) < 10s) {
std::this_thread::sleep_for(1ms);
}
@@ -281,17 +222,8 @@ TYPED_TEST(TestExecutors, spinWhileAlreadySpinning)
}
// Check executor exits immediately if future is complete.
TYPED_TEST(TestExecutors, testSpinUntilFutureComplete)
{
TYPED_TEST(TestExecutors, testSpinUntilFutureComplete) {
using ExecutorType = TypeParam;
// rmw_connextdds doesn't support events-executor
if (
std::is_same<ExecutorType, rclcpp::experimental::executors::EventsExecutor>() &&
std::string(rmw_get_implementation_identifier()).find("rmw_connextdds") == 0)
{
GTEST_SKIP();
}
ExecutorType executor;
executor.add_node(this->node);
@@ -312,17 +244,8 @@ TYPED_TEST(TestExecutors, testSpinUntilFutureComplete)
}
// Same test, but uses a shared future.
TYPED_TEST(TestExecutors, testSpinUntilSharedFutureComplete)
{
TYPED_TEST(TestExecutors, testSpinUntilSharedFutureComplete) {
using ExecutorType = TypeParam;
// rmw_connextdds doesn't support events-executor
if (
std::is_same<ExecutorType, rclcpp::experimental::executors::EventsExecutor>() &&
std::string(rmw_get_implementation_identifier()).find("rmw_connextdds") == 0)
{
GTEST_SKIP();
}
ExecutorType executor;
executor.add_node(this->node);
@@ -344,17 +267,8 @@ TYPED_TEST(TestExecutors, testSpinUntilSharedFutureComplete)
}
// For a longer running future that should require several iterations of spin_once
TYPED_TEST(TestExecutors, testSpinUntilFutureCompleteNoTimeout)
{
TYPED_TEST(TestExecutors, testSpinUntilFutureCompleteNoTimeout) {
using ExecutorType = TypeParam;
// rmw_connextdds doesn't support events-executor
if (
std::is_same<ExecutorType, rclcpp::experimental::executors::EventsExecutor>() &&
std::string(rmw_get_implementation_identifier()).find("rmw_connextdds") == 0)
{
GTEST_SKIP();
}
ExecutorType executor;
executor.add_node(this->node);
@@ -399,17 +313,8 @@ TYPED_TEST(TestExecutors, testSpinUntilFutureCompleteNoTimeout)
}
// Check spin_until_future_complete timeout works as expected
TYPED_TEST(TestExecutors, testSpinUntilFutureCompleteWithTimeout)
{
TYPED_TEST(TestExecutors, testSpinUntilFutureCompleteWithTimeout) {
using ExecutorType = TypeParam;
// rmw_connextdds doesn't support events-executor
if (
std::is_same<ExecutorType, rclcpp::experimental::executors::EventsExecutor>() &&
std::string(rmw_get_implementation_identifier()).find("rmw_connextdds") == 0)
{
GTEST_SKIP();
}
ExecutorType executor;
executor.add_node(this->node);
@@ -475,13 +380,6 @@ public:
return nullptr;
}
std::shared_ptr<void>
take_data_by_entity_id(size_t id) override
{
(void) id;
return nullptr;
}
void
execute(std::shared_ptr<void> & data) override
{
@@ -490,21 +388,6 @@ public:
std::this_thread::sleep_for(3ms);
}
void
set_on_ready_callback(std::function<void(size_t, int)> callback) override
{
auto gc_callback = [callback](size_t count) {
callback(count, 0);
};
gc_.set_on_trigger_callback(gc_callback);
}
void
clear_on_ready_callback() override
{
gc_.set_on_trigger_callback(nullptr);
}
size_t
get_number_of_ready_guard_conditions() override {return 1;}
@@ -519,17 +402,8 @@ private:
rclcpp::GuardCondition gc_;
};
TYPED_TEST(TestExecutors, spinAll)
{
TYPED_TEST(TestExecutors, spinAll) {
using ExecutorType = TypeParam;
// rmw_connextdds doesn't support events-executor
if (
std::is_same<ExecutorType, rclcpp::experimental::executors::EventsExecutor>() &&
std::string(rmw_get_implementation_identifier()).find("rmw_connextdds") == 0)
{
GTEST_SKIP();
}
ExecutorType executor;
auto waitable_interfaces = this->node->get_node_waitables_interface();
auto my_waitable = std::make_shared<TestWaitable>();
@@ -569,17 +443,8 @@ TYPED_TEST(TestExecutors, spinAll)
spinner.join();
}
TYPED_TEST(TestExecutors, spinSome)
{
TYPED_TEST(TestExecutors, spinSome) {
using ExecutorType = TypeParam;
// rmw_connextdds doesn't support events-executor
if (
std::is_same<ExecutorType, rclcpp::experimental::executors::EventsExecutor>() &&
std::string(rmw_get_implementation_identifier()).find("rmw_connextdds") == 0)
{
GTEST_SKIP();
}
ExecutorType executor;
auto waitable_interfaces = this->node->get_node_waitables_interface();
auto my_waitable = std::make_shared<TestWaitable>();
@@ -607,9 +472,8 @@ TYPED_TEST(TestExecutors, spinSome)
this->publisher->publish(test_msgs::msg::Empty());
std::this_thread::sleep_for(1ms);
}
// The count of "execute" depends on whether the executor starts spinning before (1) or after (0)
// the first iteration of the while loop
EXPECT_LE(1u, my_waitable->get_count());
EXPECT_EQ(1u, my_waitable->get_count());
waitable_interfaces->remove_waitable(my_waitable, nullptr);
EXPECT_TRUE(spin_exited);
// Cancel if it hasn't exited already.
@@ -619,17 +483,8 @@ TYPED_TEST(TestExecutors, spinSome)
}
// Check spin_node_until_future_complete with node base pointer
TYPED_TEST(TestExecutors, testSpinNodeUntilFutureCompleteNodeBasePtr)
{
TYPED_TEST(TestExecutors, testSpinNodeUntilFutureCompleteNodeBasePtr) {
using ExecutorType = TypeParam;
// rmw_connextdds doesn't support events-executor
if (
std::is_same<ExecutorType, rclcpp::experimental::executors::EventsExecutor>() &&
std::string(rmw_get_implementation_identifier()).find("rmw_connextdds") == 0)
{
GTEST_SKIP();
}
ExecutorType executor;
std::promise<bool> promise;
@@ -643,17 +498,8 @@ TYPED_TEST(TestExecutors, testSpinNodeUntilFutureCompleteNodeBasePtr)
}
// Check spin_node_until_future_complete with node pointer
TYPED_TEST(TestExecutors, testSpinNodeUntilFutureCompleteNodePtr)
{
TYPED_TEST(TestExecutors, testSpinNodeUntilFutureCompleteNodePtr) {
using ExecutorType = TypeParam;
// rmw_connextdds doesn't support events-executor
if (
std::is_same<ExecutorType, rclcpp::experimental::executors::EventsExecutor>() &&
std::string(rmw_get_implementation_identifier()).find("rmw_connextdds") == 0)
{
GTEST_SKIP();
}
ExecutorType executor;
std::promise<bool> promise;
@@ -667,17 +513,8 @@ TYPED_TEST(TestExecutors, testSpinNodeUntilFutureCompleteNodePtr)
}
// Check spin_until_future_complete can be properly interrupted.
TYPED_TEST(TestExecutors, testSpinUntilFutureCompleteInterrupted)
{
TYPED_TEST(TestExecutors, testSpinUntilFutureCompleteInterrupted) {
using ExecutorType = TypeParam;
// rmw_connextdds doesn't support events-executor
if (
std::is_same<ExecutorType, rclcpp::experimental::executors::EventsExecutor>() &&
std::string(rmw_get_implementation_identifier()).find("rmw_connextdds") == 0)
{
GTEST_SKIP();
}
ExecutorType executor;
executor.add_node(this->node);
@@ -719,8 +556,7 @@ TYPED_TEST(TestExecutors, testSpinUntilFutureCompleteInterrupted)
}
// Check spin_until_future_complete with node base pointer (instantiates its own executor)
TEST(TestExecutors, testSpinUntilFutureCompleteNodeBasePtr)
{
TEST(TestExecutors, testSpinUntilFutureCompleteNodeBasePtr) {
rclcpp::init(0, nullptr);
{
@@ -740,8 +576,7 @@ TEST(TestExecutors, testSpinUntilFutureCompleteNodeBasePtr)
}
// Check spin_until_future_complete with node pointer (instantiates its own executor)
TEST(TestExecutors, testSpinUntilFutureCompleteNodePtr)
{
TEST(TestExecutors, testSpinUntilFutureCompleteNodePtr) {
rclcpp::init(0, nullptr);
{
@@ -758,106 +593,3 @@ TEST(TestExecutors, testSpinUntilFutureCompleteNodePtr)
rclcpp::shutdown();
}
template<typename T>
class TestIntraprocessExecutors : public ::testing::Test
{
public:
static void SetUpTestCase()
{
rclcpp::init(0, nullptr);
}
static void TearDownTestCase()
{
rclcpp::shutdown();
}
void SetUp()
{
const auto test_info = ::testing::UnitTest::GetInstance()->current_test_info();
std::stringstream test_name;
test_name << test_info->test_case_name() << "_" << test_info->name();
node = std::make_shared<rclcpp::Node>("node", test_name.str());
callback_count = 0;
const std::string topic_name = std::string("topic_") + test_name.str();
rclcpp::PublisherOptions po;
po.use_intra_process_comm = rclcpp::IntraProcessSetting::Enable;
publisher = node->create_publisher<test_msgs::msg::Empty>(topic_name, rclcpp::QoS(1), po);
auto callback = [this](test_msgs::msg::Empty::ConstSharedPtr) {
this->callback_count.fetch_add(1);
};
rclcpp::SubscriptionOptions so;
so.use_intra_process_comm = rclcpp::IntraProcessSetting::Enable;
subscription =
node->create_subscription<test_msgs::msg::Empty>(
topic_name, rclcpp::QoS(kNumMessages), std::move(callback), so);
}
void TearDown()
{
publisher.reset();
subscription.reset();
node.reset();
}
const size_t kNumMessages = 100;
rclcpp::Node::SharedPtr node;
rclcpp::Publisher<test_msgs::msg::Empty>::SharedPtr publisher;
rclcpp::Subscription<test_msgs::msg::Empty>::SharedPtr subscription;
std::atomic_int callback_count;
};
TYPED_TEST_SUITE(TestIntraprocessExecutors, ExecutorTypes, ExecutorTypeNames);
TYPED_TEST(TestIntraprocessExecutors, testIntraprocessRetrigger) {
// This tests that executors will continue to service intraprocess subscriptions in the case
// that publishers aren't continuing to publish.
// This was previously broken in that intraprocess guard conditions were only triggered on
// publish and the test was added to prevent future regressions.
const size_t kNumMessages = 100;
using ExecutorType = TypeParam;
ExecutorType executor;
executor.add_node(this->node);
EXPECT_EQ(0, this->callback_count.load());
this->publisher->publish(test_msgs::msg::Empty());
// Wait for up to 5 seconds for the first message to come available.
const std::chrono::milliseconds sleep_per_loop(10);
int loops = 0;
while (1u != this->callback_count.load() && loops < 500) {
rclcpp::sleep_for(sleep_per_loop);
executor.spin_some();
loops++;
}
EXPECT_EQ(1u, this->callback_count.load());
// reset counter
this->callback_count.store(0);
for (size_t ii = 0; ii < kNumMessages; ++ii) {
this->publisher->publish(test_msgs::msg::Empty());
}
// Fire a timer every 10ms up to 5 seconds waiting for subscriptions to be read.
loops = 0;
auto timer = this->node->create_wall_timer(
std::chrono::milliseconds(10), [this, &executor, &loops, &kNumMessages]() {
loops++;
if (kNumMessages == this->callback_count.load() ||
loops == 500)
{
executor.cancel();
}
});
executor.spin();
EXPECT_EQ(kNumMessages, this->callback_count.load());
}

View File

@@ -28,7 +28,6 @@
#include "rclcpp/node_interfaces/node_graph.hpp"
#include "rclcpp/rclcpp.hpp"
#include "rcutils/strdup.h"
#include "test_msgs/msg/empty.h"
#include "test_msgs/msg/empty.hpp"
#include "test_msgs/srv/empty.hpp"
@@ -600,18 +599,6 @@ TEST_F(TestNodeGraph, get_info_by_topic)
rclcpp::QoS const_actual_qos = const_publisher_endpoint_info.qos_profile();
EXPECT_EQ(const_actual_qos.reliability(), rclcpp::ReliabilityPolicy::Reliable);
const rosidl_type_hash_t expected_type_hash = *test_msgs__msg__Empty__get_type_hash(nullptr);
EXPECT_EQ(
0, memcmp(
&publisher_endpoint_info.topic_type_hash(),
&expected_type_hash,
sizeof(rosidl_type_hash_t)));
EXPECT_EQ(
0, memcmp(
&const_publisher_endpoint_info.topic_type_hash(),
&expected_type_hash,
sizeof(rosidl_type_hash_t)));
auto endpoint_gid = publisher_endpoint_info.endpoint_gid();
auto const_endpoint_gid = const_publisher_endpoint_info.endpoint_gid();
bool endpoint_gid_is_all_zeros = true;

View File

@@ -46,15 +46,11 @@ public:
}
};
template<typename T>
class TestAddCallbackGroupsToExecutorStable : public TestAddCallbackGroupsToExecutor<T> {};
using ExecutorTypes =
::testing::Types<
rclcpp::executors::SingleThreadedExecutor,
rclcpp::executors::MultiThreadedExecutor,
rclcpp::executors::StaticSingleThreadedExecutor,
rclcpp::experimental::executors::EventsExecutor>;
rclcpp::executors::StaticSingleThreadedExecutor>;
class ExecutorTypeNames
{
@@ -75,38 +71,17 @@ public:
return "StaticSingleThreadedExecutor";
}
if (std::is_same<T, rclcpp::experimental::executors::EventsExecutor>()) {
return "EventsExecutor";
}
return "";
}
};
TYPED_TEST_SUITE(TestAddCallbackGroupsToExecutor, ExecutorTypes, ExecutorTypeNames);
// StaticSingleThreadedExecutor is not included in these tests for now
using StandardExecutors =
::testing::Types<
rclcpp::executors::SingleThreadedExecutor,
rclcpp::executors::MultiThreadedExecutor,
rclcpp::experimental::executors::EventsExecutor>;
TYPED_TEST_SUITE(TestAddCallbackGroupsToExecutorStable, StandardExecutors, ExecutorTypeNames);
/*
* Test adding callback groups.
*/
TYPED_TEST(TestAddCallbackGroupsToExecutor, add_callback_groups)
{
TYPED_TEST(TestAddCallbackGroupsToExecutor, add_callback_groups) {
using ExecutorType = TypeParam;
// rmw_connextdds doesn't support events-executor
if (
std::is_same<ExecutorType, rclcpp::experimental::executors::EventsExecutor>() &&
std::string(rmw_get_implementation_identifier()).find("rmw_connextdds") == 0)
{
GTEST_SKIP();
}
auto node = std::make_shared<rclcpp::Node>("my_node", "/ns");
auto timer_callback = []() {};
rclcpp::CallbackGroup::SharedPtr cb_grp = node->create_callback_group(
@@ -152,17 +127,8 @@ TYPED_TEST(TestAddCallbackGroupsToExecutor, add_callback_groups)
/*
* Test removing callback groups.
*/
TYPED_TEST(TestAddCallbackGroupsToExecutor, remove_callback_groups)
{
TYPED_TEST(TestAddCallbackGroupsToExecutor, remove_callback_groups) {
using ExecutorType = TypeParam;
// rmw_connextdds doesn't support events-executor
if (
std::is_same<ExecutorType, rclcpp::experimental::executors::EventsExecutor>() &&
std::string(rmw_get_implementation_identifier()).find("rmw_connextdds") == 0)
{
GTEST_SKIP();
}
auto node = std::make_shared<rclcpp::Node>("my_node", "/ns");
auto timer_callback = []() {};
rclcpp::CallbackGroup::SharedPtr cb_grp = node->create_callback_group(
@@ -192,16 +158,7 @@ TYPED_TEST(TestAddCallbackGroupsToExecutor, remove_callback_groups)
*/
TYPED_TEST(TestAddCallbackGroupsToExecutor, add_duplicate_callback_groups)
{
using ExecutorType = TypeParam;
// rmw_connextdds doesn't support events-executor
if (
std::is_same<ExecutorType, rclcpp::experimental::executors::EventsExecutor>() &&
std::string(rmw_get_implementation_identifier()).find("rmw_connextdds") == 0)
{
GTEST_SKIP();
}
ExecutorType executor;
rclcpp::executors::MultiThreadedExecutor executor;
auto node = std::make_shared<rclcpp::Node>("my_node", "/ns");
auto timer_callback = []() {};
rclcpp::CallbackGroup::SharedPtr cb_grp = node->create_callback_group(
@@ -219,16 +176,7 @@ TYPED_TEST(TestAddCallbackGroupsToExecutor, add_duplicate_callback_groups)
*/
TYPED_TEST(TestAddCallbackGroupsToExecutor, add_callback_groups_after_add_node_to_executor)
{
using ExecutorType = TypeParam;
// rmw_connextdds doesn't support events-executor
if (
std::is_same<ExecutorType, rclcpp::experimental::executors::EventsExecutor>() &&
std::string(rmw_get_implementation_identifier()).find("rmw_connextdds") == 0)
{
GTEST_SKIP();
}
ExecutorType executor;
rclcpp::executors::MultiThreadedExecutor executor;
auto node = std::make_shared<rclcpp::Node>("my_node", "/ns");
executor.add_node(node->get_node_base_interface());
ASSERT_EQ(executor.get_all_callback_groups().size(), 1u);
@@ -262,22 +210,13 @@ TYPED_TEST(TestAddCallbackGroupsToExecutor, add_callback_groups_after_add_node_t
*/
TYPED_TEST(TestAddCallbackGroupsToExecutor, add_unallowable_callback_groups)
{
using ExecutorType = TypeParam;
// rmw_connextdds doesn't support events-executor
if (
std::is_same<ExecutorType, rclcpp::experimental::executors::EventsExecutor>() &&
std::string(rmw_get_implementation_identifier()).find("rmw_connextdds") == 0)
{
GTEST_SKIP();
}
ExecutorType executor;
auto node = std::make_shared<rclcpp::Node>("my_node", "/ns");
auto timer_callback = []() {};
rclcpp::CallbackGroup::SharedPtr cb_grp = node->create_callback_group(
rclcpp::CallbackGroupType::MutuallyExclusive, false);
rclcpp::TimerBase::SharedPtr timer_ = node->create_wall_timer(
2s, timer_callback, cb_grp);
rclcpp::executors::MultiThreadedExecutor executor;
executor.add_callback_group(cb_grp, node->get_node_base_interface());
ASSERT_EQ(executor.get_all_callback_groups().size(), 1u);
@@ -306,23 +245,14 @@ TYPED_TEST(TestAddCallbackGroupsToExecutor, add_unallowable_callback_groups)
*/
TYPED_TEST(TestAddCallbackGroupsToExecutor, one_node_many_callback_groups_many_executors)
{
using ExecutorType = TypeParam;
// rmw_connextdds doesn't support events-executor
if (
std::is_same<ExecutorType, rclcpp::experimental::executors::EventsExecutor>() &&
std::string(rmw_get_implementation_identifier()).find("rmw_connextdds") == 0)
{
GTEST_SKIP();
}
ExecutorType timer_executor;
ExecutorType sub_executor;
auto node = std::make_shared<rclcpp::Node>("my_node", "/ns");
auto timer_callback = []() {};
rclcpp::CallbackGroup::SharedPtr cb_grp = node->create_callback_group(
rclcpp::CallbackGroupType::MutuallyExclusive, false);
rclcpp::TimerBase::SharedPtr timer_ = node->create_wall_timer(
2s, timer_callback, cb_grp);
rclcpp::executors::MultiThreadedExecutor timer_executor;
rclcpp::executors::MultiThreadedExecutor sub_executor;
timer_executor.add_callback_group(cb_grp, node->get_node_base_interface());
const rclcpp::QoS qos(10);
auto options = rclcpp::SubscriptionOptions();
@@ -352,23 +282,14 @@ TYPED_TEST(TestAddCallbackGroupsToExecutor, one_node_many_callback_groups_many_e
* because the executor can't be triggered while a subscriber created, see
* https://github.com/ros2/rclcpp/issues/1611
*/
TYPED_TEST(TestAddCallbackGroupsToExecutorStable, subscriber_triggered_to_receive_message)
TYPED_TEST(TestAddCallbackGroupsToExecutor, subscriber_triggered_to_receive_message)
{
using ExecutorType = TypeParam;
// rmw_connextdds doesn't support events-executor
if (
std::is_same<ExecutorType, rclcpp::experimental::executors::EventsExecutor>() &&
std::string(rmw_get_implementation_identifier()).find("rmw_connextdds") == 0)
{
GTEST_SKIP();
}
auto node = std::make_shared<rclcpp::Node>("my_node", "/ns");
// create a thread running an executor with a new callback group for a coming subscriber
rclcpp::CallbackGroup::SharedPtr cb_grp = node->create_callback_group(
rclcpp::CallbackGroupType::MutuallyExclusive, false);
ExecutorType cb_grp_executor;
rclcpp::executors::SingleThreadedExecutor cb_grp_executor;
std::promise<bool> received_message_promise;
auto received_message_future = received_message_promise.get_future();
@@ -408,7 +329,7 @@ TYPED_TEST(TestAddCallbackGroupsToExecutorStable, subscriber_triggered_to_receiv
timer_promise.set_value();
};
ExecutorType timer_executor;
rclcpp::executors::SingleThreadedExecutor timer_executor;
timer = node->create_wall_timer(100ms, timer_callback);
timer_executor.add_node(node);
auto future = timer_promise.get_future();
@@ -425,17 +346,8 @@ TYPED_TEST(TestAddCallbackGroupsToExecutorStable, subscriber_triggered_to_receiv
* because the executor can't be triggered while a subscriber created, see
* https://github.com/ros2/rclcpp/issues/2067
*/
TYPED_TEST(TestAddCallbackGroupsToExecutorStable, callback_group_create_after_spin)
TYPED_TEST(TestAddCallbackGroupsToExecutor, callback_group_create_after_spin)
{
using ExecutorType = TypeParam;
// rmw_connextdds doesn't support events-executor
if (
std::is_same<ExecutorType, rclcpp::experimental::executors::EventsExecutor>() &&
std::string(rmw_get_implementation_identifier()).find("rmw_connextdds") == 0)
{
GTEST_SKIP();
}
auto node = std::make_shared<rclcpp::Node>("my_node", "/ns");
// create a publisher to send data
@@ -445,7 +357,7 @@ TYPED_TEST(TestAddCallbackGroupsToExecutorStable, callback_group_create_after_sp
publisher->publish(test_msgs::msg::Empty());
// create a thread running an executor
ExecutorType executor;
rclcpp::executors::SingleThreadedExecutor executor;
executor.add_node(node);
std::promise<bool> received_message_promise;
auto received_message_future = received_message_promise.get_future();
@@ -480,16 +392,7 @@ TYPED_TEST(TestAddCallbackGroupsToExecutorStable, callback_group_create_after_sp
*/
TYPED_TEST(TestAddCallbackGroupsToExecutor, remove_callback_group)
{
using ExecutorType = TypeParam;
// rmw_connextdds doesn't support events-executor
if (
std::is_same<ExecutorType, rclcpp::experimental::executors::EventsExecutor>() &&
std::string(rmw_get_implementation_identifier()).find("rmw_connextdds") == 0)
{
GTEST_SKIP();
}
ExecutorType executor;
rclcpp::executors::MultiThreadedExecutor executor;
auto node = std::make_shared<rclcpp::Node>("my_node", "/ns");
auto timer_callback = []() {};
rclcpp::CallbackGroup::SharedPtr cb_grp = node->create_callback_group(

View File

@@ -46,6 +46,23 @@ public:
{
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
@@ -113,7 +130,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);
RCLCPP_EXPECT_THROW_EQ(
static_cast<void>(std::make_unique<DummyExecutor>()),
std::runtime_error("Failed to create wait set: error not set"));
std::runtime_error("Failed to create wait set in Executor constructor: error not set"));
}
TEST_F(TestExecutor, add_callback_group_twice) {
@@ -125,7 +142,7 @@ TEST_F(TestExecutor, add_callback_group_twice) {
cb_group->get_associated_with_executor_atomic().exchange(false);
RCLCPP_EXPECT_THROW_EQ(
dummy.add_callback_group(cb_group, node->get_node_base_interface(), false),
std::runtime_error("Callback group has already been added to this executor."));
std::runtime_error("Callback group was already added to executor."));
}
TEST_F(TestExecutor, add_callback_group_failed_trigger_guard_condition) {
@@ -151,15 +168,9 @@ TEST_F(TestExecutor, remove_callback_group_null_node) {
node.reset();
/**
* TODO(mjcarroll): Assert this when we are enforcing that nodes must be destroyed
* after their created callback groups.
RCLCPP_EXPECT_THROW_EQ(
dummy.remove_callback_group(cb_group, false),
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) {
@@ -186,7 +197,7 @@ TEST_F(TestExecutor, remove_node_not_associated) {
RCLCPP_EXPECT_THROW_EQ(
dummy.remove_node(node->get_node_base_interface(), false),
std::runtime_error("Node '/ns/node' needs to be associated with an executor."));
std::runtime_error("Node needs to be associated with an executor."));
}
TEST_F(TestExecutor, remove_node_associated_with_different_executor) {
@@ -200,7 +211,7 @@ TEST_F(TestExecutor, remove_node_associated_with_different_executor) {
RCLCPP_EXPECT_THROW_EQ(
dummy2.remove_node(node1->get_node_base_interface(), false),
std::runtime_error("Node '/ns/node1' needs to be associated with this executor."));
std::runtime_error("Node needs to be associated with this executor."));
}
TEST_F(TestExecutor, spin_node_once_nanoseconds) {
@@ -317,14 +328,42 @@ TEST_F(TestExecutor, cancel_failed_trigger_guard_condition) {
std::runtime_error("Failed to trigger guard condition in cancel: error not set"));
}
TEST_F(TestExecutor, create_executor_fail_wait_set_clear) {
auto mock = mocking_utils::patch_and_return("lib:rclcpp", rcl_wait_set_clear, RCL_RET_ERROR);
TEST_F(TestExecutor, set_memory_strategy_nullptr) {
DummyExecutor dummy;
RCLCPP_EXPECT_THROW_EQ(
DummyExecutor dummy,
std::runtime_error("Couldn't clear the wait set: error not set"));
dummy.set_memory_strategy(nullptr),
std::runtime_error("Received NULL memory strategy in executor."));
}
TEST_F(TestExecutor, spin_all_fail_wait_set_clear) {
TEST_F(TestExecutor, set_memory_strategy) {
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;
auto node = std::make_shared<rclcpp::Node>("node", "ns");
auto timer =
@@ -332,10 +371,9 @@ TEST_F(TestExecutor, spin_all_fail_wait_set_clear) {
dummy.add_node(node);
auto mock = mocking_utils::patch_and_return("lib:rclcpp", rcl_wait_set_clear, RCL_RET_ERROR);
RCLCPP_EXPECT_THROW_EQ(
dummy.spin_all(std::chrono::milliseconds(1)),
std::runtime_error("Couldn't clear the wait set: error not set"));
dummy.spin_some(std::chrono::milliseconds(1)),
std::runtime_error("Couldn't clear wait set: error not set"));
}
TEST_F(TestExecutor, spin_some_fail_wait_set_resize) {
@@ -363,7 +401,7 @@ TEST_F(TestExecutor, spin_some_fail_add_handles_to_wait_set) {
RCL_RET_ERROR);
RCLCPP_EXPECT_THROW_EQ(
dummy.spin_some(std::chrono::milliseconds(1)),
std::runtime_error("Couldn't fill wait set: error not set"));
std::runtime_error("Couldn't fill wait set"));
}
TEST_F(TestExecutor, spin_some_fail_wait) {
@@ -379,6 +417,71 @@ TEST_F(TestExecutor, spin_some_fail_wait) {
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) {
DummyExecutor dummy;
auto node = std::make_shared<rclcpp::Node>("node", "ns");

View File

@@ -89,9 +89,9 @@ public:
T2 message;
write_message(data, message);
rclcpp::Serialization<T2> ser;
rclcpp::Serialization<T2> serialization_support;
SerializedMessage result;
ser.serialize_message(&message, &result);
serialization_support.serialize_message(&message, &result);
return result;
}

View File

@@ -1,419 +0,0 @@
// Copyright 2023 iRobot Corporation.
//
// 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 <gtest/gtest.h>
#include <chrono>
#include <memory>
#include <utility>
#include "rclcpp/contexts/default_context.hpp"
#include "rclcpp/experimental/timers_manager.hpp"
using namespace std::chrono_literals;
using rclcpp::experimental::TimersManager;
using CallbackT = std::function<void ()>;
using TimerT = rclcpp::WallTimer<CallbackT>;
class TestTimersManager : public ::testing::Test
{
public:
void SetUp()
{
rclcpp::init(0, nullptr);
}
void TearDown()
{
rclcpp::shutdown();
}
};
static void execute_all_ready_timers(std::shared_ptr<TimersManager> timers_manager)
{
bool head_was_ready = false;
do {
head_was_ready = timers_manager->execute_head_timer();
} while (head_was_ready);
}
TEST_F(TestTimersManager, empty_manager)
{
auto timers_manager = std::make_shared<TimersManager>(
rclcpp::contexts::get_global_default_context());
EXPECT_EQ(std::chrono::nanoseconds::max(), timers_manager->get_head_timeout());
EXPECT_FALSE(timers_manager->execute_head_timer());
EXPECT_NO_THROW(timers_manager->clear());
EXPECT_NO_THROW(timers_manager->start());
EXPECT_NO_THROW(timers_manager->stop());
}
TEST_F(TestTimersManager, add_run_remove_timer)
{
size_t t_runs = 0;
auto t = TimerT::make_shared(
10ms,
[&t_runs]() {
t_runs++;
},
rclcpp::contexts::get_global_default_context());
std::weak_ptr<TimerT> t_weak = t;
// Add the timer to the timers manager
auto timers_manager = std::make_shared<TimersManager>(
rclcpp::contexts::get_global_default_context());
timers_manager->add_timer(t);
// Sleep for more 3 times the timer period
std::this_thread::sleep_for(30ms);
// The timer is executed only once, even if we slept 3 times the period
execute_all_ready_timers(timers_manager);
EXPECT_EQ(1u, t_runs);
// Remove the timer from the manager
timers_manager->remove_timer(t);
t.reset();
// The timer is now not valid anymore
EXPECT_FALSE(t_weak.lock() != nullptr);
}
TEST_F(TestTimersManager, clear)
{
auto timers_manager = std::make_shared<TimersManager>(
rclcpp::contexts::get_global_default_context());
auto t1 = TimerT::make_shared(1ms, CallbackT(), rclcpp::contexts::get_global_default_context());
std::weak_ptr<TimerT> t1_weak = t1;
auto t2 = TimerT::make_shared(1ms, CallbackT(), rclcpp::contexts::get_global_default_context());
std::weak_ptr<TimerT> t2_weak = t2;
timers_manager->add_timer(t1);
timers_manager->add_timer(t2);
EXPECT_TRUE(t1_weak.lock() != nullptr);
EXPECT_TRUE(t2_weak.lock() != nullptr);
timers_manager->clear();
t1.reset();
t2.reset();
EXPECT_FALSE(t1_weak.lock() != nullptr);
EXPECT_FALSE(t2_weak.lock() != nullptr);
}
TEST_F(TestTimersManager, remove_not_existing_timer)
{
auto timers_manager = std::make_shared<TimersManager>(
rclcpp::contexts::get_global_default_context());
// Try to remove a nullptr timer
EXPECT_NO_THROW(timers_manager->remove_timer(nullptr));
auto t = TimerT::make_shared(1ms, CallbackT(), rclcpp::contexts::get_global_default_context());
timers_manager->add_timer(t);
// Remove twice the same timer
timers_manager->remove_timer(t);
EXPECT_NO_THROW(timers_manager->remove_timer(t));
}
TEST_F(TestTimersManager, timers_thread_exclusive_usage)
{
auto timers_manager = std::make_shared<TimersManager>(
rclcpp::contexts::get_global_default_context());
timers_manager->start();
EXPECT_THROW(timers_manager->start(), std::exception);
EXPECT_THROW(timers_manager->get_head_timeout(), std::exception);
EXPECT_THROW(timers_manager->execute_head_timer(), std::exception);
timers_manager->stop();
EXPECT_NO_THROW(timers_manager->get_head_timeout());
EXPECT_NO_THROW(timers_manager->execute_head_timer());
}
TEST_F(TestTimersManager, add_timer_twice)
{
auto timers_manager = std::make_shared<TimersManager>(
rclcpp::contexts::get_global_default_context());
auto t = TimerT::make_shared(1ms, CallbackT(), rclcpp::contexts::get_global_default_context());
timers_manager->add_timer(t);
EXPECT_NO_THROW(timers_manager->add_timer(t));
}
TEST_F(TestTimersManager, add_nullptr)
{
auto timers_manager = std::make_shared<TimersManager>(
rclcpp::contexts::get_global_default_context());
EXPECT_THROW(timers_manager->add_timer(nullptr), std::exception);
}
TEST_F(TestTimersManager, head_not_ready)
{
auto timers_manager = std::make_shared<TimersManager>(
rclcpp::contexts::get_global_default_context());
size_t t_runs = 0;
auto t = TimerT::make_shared(
10s,
[&t_runs]() {
t_runs++;
},
rclcpp::contexts::get_global_default_context());
timers_manager->add_timer(t);
// Timer will take 10s to get ready, so nothing to execute here
bool ret = timers_manager->execute_head_timer();
EXPECT_FALSE(ret);
EXPECT_EQ(0u, t_runs);
}
TEST_F(TestTimersManager, timers_order)
{
auto timers_manager = std::make_shared<TimersManager>(
rclcpp::contexts::get_global_default_context());
size_t t1_runs = 0;
auto t1 = TimerT::make_shared(
10ms,
[&t1_runs]() {
t1_runs++;
},
rclcpp::contexts::get_global_default_context());
size_t t2_runs = 0;
auto t2 = TimerT::make_shared(
30ms,
[&t2_runs]() {
t2_runs++;
},
rclcpp::contexts::get_global_default_context());
size_t t3_runs = 0;
auto t3 = TimerT::make_shared(
100ms,
[&t3_runs]() {
t3_runs++;
},
rclcpp::contexts::get_global_default_context());
// Add timers in a random order
timers_manager->add_timer(t2);
timers_manager->add_timer(t3);
timers_manager->add_timer(t1);
std::this_thread::sleep_for(10ms);
execute_all_ready_timers(timers_manager);
EXPECT_EQ(1u, t1_runs);
EXPECT_EQ(0u, t2_runs);
EXPECT_EQ(0u, t3_runs);
std::this_thread::sleep_for(30ms);
execute_all_ready_timers(timers_manager);
EXPECT_EQ(2u, t1_runs);
EXPECT_EQ(1u, t2_runs);
EXPECT_EQ(0u, t3_runs);
std::this_thread::sleep_for(100ms);
execute_all_ready_timers(timers_manager);
EXPECT_EQ(3u, t1_runs);
EXPECT_EQ(2u, t2_runs);
EXPECT_EQ(1u, t3_runs);
timers_manager->remove_timer(t1);
std::this_thread::sleep_for(30ms);
execute_all_ready_timers(timers_manager);
EXPECT_EQ(3u, t1_runs);
EXPECT_EQ(3u, t2_runs);
EXPECT_EQ(1u, t3_runs);
}
TEST_F(TestTimersManager, start_stop_timers_thread)
{
auto timers_manager = std::make_shared<TimersManager>(
rclcpp::contexts::get_global_default_context());
auto t = TimerT::make_shared(1ms, []() {}, rclcpp::contexts::get_global_default_context());
timers_manager->add_timer(t);
// Calling start multiple times will throw an error
EXPECT_NO_THROW(timers_manager->start());
EXPECT_THROW(timers_manager->start(), std::exception);
// Calling stop multiple times does not throw an error
EXPECT_NO_THROW(timers_manager->stop());
EXPECT_NO_THROW(timers_manager->stop());
}
TEST_F(TestTimersManager, timers_thread)
{
auto timers_manager = std::make_shared<TimersManager>(
rclcpp::contexts::get_global_default_context());
size_t t1_runs = 0;
auto t1 = TimerT::make_shared(
1ms,
[&t1_runs]() {
t1_runs++;
},
rclcpp::contexts::get_global_default_context());
size_t t2_runs = 0;
auto t2 = TimerT::make_shared(
1ms,
[&t2_runs]() {
t2_runs++;
},
rclcpp::contexts::get_global_default_context());
// Add timers
timers_manager->add_timer(t1);
timers_manager->add_timer(t2);
// Run timers thread for a while
timers_manager->start();
std::this_thread::sleep_for(5ms);
timers_manager->stop();
EXPECT_LT(1u, t1_runs);
EXPECT_LT(1u, t2_runs);
EXPECT_EQ(t1_runs, t2_runs);
}
TEST_F(TestTimersManager, destructor)
{
size_t t_runs = 0;
auto t = TimerT::make_shared(
1ms,
[&t_runs]() {
t_runs++;
},
rclcpp::contexts::get_global_default_context());
std::weak_ptr<TimerT> t_weak = t;
// When the timers manager is destroyed, it will stop the thread
// and clear the timers
{
auto timers_manager = std::make_shared<TimersManager>(
rclcpp::contexts::get_global_default_context());
timers_manager->add_timer(t);
timers_manager->start();
std::this_thread::sleep_for(100ms);
EXPECT_LT(1u, t_runs);
}
// The thread is not running anymore, so this value does not increase
size_t runs = t_runs;
std::this_thread::sleep_for(100ms);
EXPECT_EQ(runs, t_runs);
t.reset();
EXPECT_FALSE(t_weak.lock() != nullptr);
}
TEST_F(TestTimersManager, add_remove_while_thread_running)
{
auto timers_manager = std::make_shared<TimersManager>(
rclcpp::contexts::get_global_default_context());
size_t t1_runs = 0;
auto t1 = TimerT::make_shared(
1ms,
[&t1_runs]() {
t1_runs++;
},
rclcpp::contexts::get_global_default_context());
size_t t2_runs = 0;
auto t2 = TimerT::make_shared(
1ms,
[&t2_runs]() {
t2_runs++;
},
rclcpp::contexts::get_global_default_context());
// Add timers
timers_manager->add_timer(t1);
// Start timers thread
timers_manager->start();
// After a while remove t1 and add t2
std::this_thread::sleep_for(5ms);
timers_manager->remove_timer(t1);
size_t tmp_t1 = t1_runs;
timers_manager->add_timer(t2);
// Wait some more time and then stop
std::this_thread::sleep_for(5ms);
timers_manager->stop();
// t1 has stopped running
EXPECT_EQ(tmp_t1, t1_runs);
// t2 is correctly running
EXPECT_LT(1u, t2_runs);
}
TEST_F(TestTimersManager, infinite_loop)
{
// This test makes sure that even if timers have a period shorter than the duration
// of their callback the functions never block indefinitely.
auto timers_manager = std::make_shared<TimersManager>(
rclcpp::contexts::get_global_default_context());
size_t t1_runs = 0;
auto t1 = TimerT::make_shared(
1ms,
[&t1_runs]() {
t1_runs++;
std::this_thread::sleep_for(5ms);
},
rclcpp::contexts::get_global_default_context());
size_t t2_runs = 0;
auto t2 = TimerT::make_shared(
1ms,
[&t2_runs]() {
t2_runs++;
std::this_thread::sleep_for(5ms);
},
rclcpp::contexts::get_global_default_context());
timers_manager->add_timer(t1);
timers_manager->add_timer(t2);
// Start a timers thread and make sure that we can stop it later
timers_manager->start();
std::this_thread::sleep_for(50ms);
timers_manager->stop();
EXPECT_LT(0u, t1_runs);
EXPECT_LT(0u, t2_runs);
}

View File

@@ -3,13 +3,6 @@ Changelog for package rclcpp_action
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
20.0.0 (2023-04-13)
-------------------
* extract the result response before the callback is issued. (`#2132 <https://github.com/ros2/rclcpp/issues/2132>`_)
* Update all rclcpp packages to C++17. (`#2121 <https://github.com/ros2/rclcpp/issues/2121>`_)
* Fix the GoalUUID to_string representation (`#1999 <https://github.com/ros2/rclcpp/issues/1999>`_)
* Contributors: Chris Lalancette, Nathan Wiebe Neufeldt, Tomoya Fujita
19.3.0 (2023-03-01)
-------------------

View File

@@ -2,7 +2,7 @@
<?xml-model href="http://download.ros.org/schema/package_format2.xsd" schematypens="http://www.w3.org/2001/XMLSchema"?>
<package format="2">
<name>rclcpp_action</name>
<version>20.0.0</version>
<version>19.3.0</version>
<description>Adds action APIs for C++.</description>
<maintainer email="ivanpauno@ekumenlabs.com">Ivan Paunovic</maintainer>

View File

@@ -2,11 +2,6 @@
Changelog for package rclcpp_components
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
20.0.0 (2023-04-13)
-------------------
* Update all rclcpp packages to C++17. (`#2121 <https://github.com/ros2/rclcpp/issues/2121>`_)
* Contributors: Chris Lalancette
19.3.0 (2023-03-01)
-------------------

View File

@@ -2,7 +2,7 @@
<?xml-model href="http://download.ros.org/schema/package_format2.xsd" schematypens="http://www.w3.org/2001/XMLSchema"?>
<package format="2">
<name>rclcpp_components</name>
<version>20.0.0</version>
<version>19.3.0</version>
<description>Package containing tools for dynamically loadable components</description>
<maintainer email="ivanpauno@ekumenlabs.com">Ivan Paunovic</maintainer>

View File

@@ -3,14 +3,6 @@ Changelog for package rclcpp_lifecycle
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
20.0.0 (2023-04-13)
-------------------
* Fixes to silence some clang warnings. (`#2127 <https://github.com/ros2/rclcpp/issues/2127>`_)
* Update all rclcpp packages to C++17. (`#2121 <https://github.com/ros2/rclcpp/issues/2121>`_)
* Use the correct macro for LifecycleNode::get_fully_qualified_name (`#2117 <https://github.com/ros2/rclcpp/issues/2117>`_)
* add get_fully_qualified_name to rclcpp_lifecycle (`#2115 <https://github.com/ros2/rclcpp/issues/2115>`_)
* Contributors: Chris Lalancette, Steve Macenski
19.3.0 (2023-03-01)
-------------------

View File

@@ -91,24 +91,6 @@ public:
rclcpp::Publisher<MessageT, Alloc>::publish(msg);
}
/// LifecyclePublisher publish function
/**
* The publish function checks whether the communication
* was enabled or disabled and forwards the message
* to the actual rclcpp Publisher base class
*/
virtual void
publish(
rclcpp::LoanedMessage<typename rclcpp::Publisher<MessageT,
Alloc>::ROSMessageType, Alloc> && loaned_msg)
{
if (!this->is_activated()) {
log_publisher_not_enabled();
return;
}
rclcpp::Publisher<MessageT, Alloc>::publish(std::move(loaned_msg));
}
void
on_activate() override
{

View File

@@ -2,7 +2,7 @@
<?xml-model href="http://download.ros.org/schema/package_format2.xsd" schematypens="http://www.w3.org/2001/XMLSchema"?>
<package format="2">
<name>rclcpp_lifecycle</name>
<version>20.0.0</version>
<version>19.3.0</version>
<description>Package containing a prototype for lifecycle implementation</description>
<maintainer email="ivanpauno@ekumenlabs.com">Ivan Paunovic</maintainer>

View File

@@ -129,10 +129,6 @@ TEST_P(TestLifecyclePublisher, publish_managed_by_node) {
auto msg_ptr = std::make_unique<test_msgs::msg::Empty>();
EXPECT_NO_THROW(node_->publisher()->publish(std::move(msg_ptr)));
}
{
auto loaned_msg = node_->publisher()->borrow_loaned_message();
EXPECT_NO_THROW(node_->publisher()->publish(std::move(loaned_msg)));
}
node_->trigger_transition(
rclcpp_lifecycle::Transition(Transition::TRANSITION_DEACTIVATE), ret);
ASSERT_EQ(success, ret);
@@ -147,10 +143,6 @@ TEST_P(TestLifecyclePublisher, publish_managed_by_node) {
auto msg_ptr = std::make_unique<test_msgs::msg::Empty>();
EXPECT_NO_THROW(node_->publisher()->publish(std::move(msg_ptr)));
}
{
auto loaned_msg = node_->publisher()->borrow_loaned_message();
EXPECT_NO_THROW(node_->publisher()->publish(std::move(loaned_msg)));
}
}
TEST_P(TestLifecyclePublisher, publish) {
@@ -165,10 +157,6 @@ TEST_P(TestLifecyclePublisher, publish) {
auto msg_ptr = std::make_unique<test_msgs::msg::Empty>();
EXPECT_NO_THROW(node_->publisher()->publish(std::move(msg_ptr)));
}
{
auto loaned_msg = node_->publisher()->borrow_loaned_message();
EXPECT_NO_THROW(node_->publisher()->publish(std::move(loaned_msg)));
}
node_->publisher()->on_activate();
EXPECT_TRUE(node_->publisher()->is_activated());
{
@@ -179,10 +167,6 @@ TEST_P(TestLifecyclePublisher, publish) {
auto msg_ptr = std::make_unique<test_msgs::msg::Empty>();
EXPECT_NO_THROW(node_->publisher()->publish(std::move(msg_ptr)));
}
{
auto loaned_msg = node_->publisher()->borrow_loaned_message();
EXPECT_NO_THROW(node_->publisher()->publish(std::move(loaned_msg)));
}
}
INSTANTIATE_TEST_SUITE_P(