Compare commits

...

15 Commits

Author SHA1 Message Date
Audrow Nash
00ef09cbf3 16.0.4
Signed-off-by: Audrow Nash <audrow@intrinsic.ai>
2023-04-25 20:55:32 +00:00
Tomoya Fujita
b2b7bdeac1 Revert "Revert "extract the result response before the callback is issued. (#2133)" (#2148)" (#2152)
This reverts commit 19a666f1c9.

Signed-off-by: Tomoya Fujita <Tomoya.Fujita@sony.com>
2023-04-02 18:03:05 -07:00
Tomoya Fujita
19a666f1c9 Revert "extract the result response before the callback is issued. (#2133)" (#2148)
This reverts commit c8ac675035.

Signed-off-by: Tomoya Fujita <Tomoya.Fujita@sony.com>
2023-03-31 08:45:10 -07:00
Tomoya Fujita
c8ac675035 extract the result response before the callback is issued. (#2133)
backport of https://github.com/ros2/rclcpp/pull/2132

Signed-off-by: Tomoya Fujita <Tomoya.Fujita@sony.com>
Co-authored-by: Chen Lihui <lihui.chen@sony.com>
2023-03-30 08:43:03 -07:00
mergify[bot]
4196a2a8b4 use allocator via init_options argument. (#2129) (#2131)
Signed-off-by: Tomoya Fujita <Tomoya.Fujita@sony.com>
(cherry picked from commit 1a796b5515)

Co-authored-by: Tomoya Fujita <Tomoya.Fujita@sony.com>
2023-03-20 13:36:32 -07:00
Audrow Nash
9171122eae 16.0.3
Signed-off-by: Audrow Nash <audrow@openrobotics.org>
2023-01-10 07:58:19 -06:00
mergify[bot]
ce13f1afba Fix SharedFuture from async_send_request never becomes valid (#2044) (#2076)
Signed-off-by: Lei Liu <Lei.Liu.AP@sony.com>
(cherry picked from commit 66b19448b0)

Co-authored-by: Lei Liu <64953129+llapx@users.noreply.github.com>
2023-01-06 10:19:33 -08:00
mergify[bot]
df08474d38 do not throw exception if trying to dequeue an empty intra-process buffer (#2061) (#2070)
Signed-off-by: Alberto Soragna <alberto.soragna@gmail.com>

Signed-off-by: Alberto Soragna <alberto.soragna@gmail.com>
(cherry picked from commit 3fb012e2e9)

Co-authored-by: Alberto Soragna <alberto.soragna@gmail.com>
2022-12-22 14:14:36 -08:00
mergify[bot]
f9050cd666 fix nullptr dereference in prune_requests_older_than (#2008) (#2065)
* fix nullptr dereference in prune_requests_older_than

Signed-off-by: akela1101 <akela1101@gmail.com>

* add tests for prune_requests_older_than

Signed-off-by: akela1101 <akela1101@gmail.com>

* Update rclcpp/test/rclcpp/test_client.cpp

Co-authored-by: Chen Lihui <lihui.chen@sony.com>
Signed-off-by: akela1101 <akela1101@gmail.com>

Signed-off-by: akela1101 <akela1101@gmail.com>
Co-authored-by: Chen Lihui <lihui.chen@sony.com>
(cherry picked from commit 1ac37b692c)

Co-authored-by: andrei <akela1101@gmail.com>
2022-12-13 15:40:16 -03:00
mergify[bot]
33cbd76c07 Fix bug that a callback not reached (#1640) (#2033)
* Add a test case

a subscriber on a new executor with a callback group triggered to receive a message

Signed-off-by: Chen Lihui <lihui.chen@sony.com>

* fix flaky and not to use spin_some

Signed-off-by: Chen Lihui <lihui.chen@sony.com>

* update comment

Signed-off-by: Chen Lihui <lihui.chen@sony.com>

* update for not using anti-pattern source code

Signed-off-by: Chen Lihui <lihui.chen@sony.com>

* add a notify guard condition for callback group

Co-authored-by: William Woodall <william@osrfoundation.org>
Signed-off-by: Chen Lihui <lihui.chen@sony.com>

* Notify guard condition of Node not to be used in Executor

it is only for the waitset of GraphListener

Signed-off-by: Chen Lihui <lihui.chen@sony.com>

* put code in the try catch

Signed-off-by: Chen Lihui <lihui.chen@sony.com>

* defer to create guard condition

Signed-off-by: Chen Lihui <lihui.chen@sony.com>

* use context directly for the create function

Signed-off-by: Chen Lihui <lihui.chen@sony.com>

* cpplint

Signed-off-by: Chen Lihui <lihui.chen@sony.com>

* fix that some case might call add_node after shutdown

Signed-off-by: Chen Lihui <lihui.chen@sony.com>

* nitpick and fix some potential bug

Signed-off-by: Chen Lihui <lihui.chen@sony.com>

* add sanity check as some case might not create notify guard condition after shutdown

Signed-off-by: Chen Lihui <lihui.chen@sony.com>

* Cleanup includes.

Signed-off-by: Chris Lalancette <clalancette@openrobotics.org>

* remove destroy method

make a callback group can only create one guard condition

Signed-off-by: Chen Lihui <lihui.chen@sony.com>

* remove limitation that guard condition can not be re-created in callback group

Signed-off-by: Chen Lihui <lihui.chen@sony.com>

Signed-off-by: Chen Lihui <lihui.chen@sony.com>
Signed-off-by: Chris Lalancette <clalancette@openrobotics.org>
Co-authored-by: William Woodall <william@osrfoundation.org>
Co-authored-by: Chris Lalancette <clalancette@openrobotics.org>
(cherry picked from commit d119157948)

Co-authored-by: Chen Lihui <lihui.chen@sony.com>
2022-11-09 22:16:14 -08:00
Audrow Nash
ae8b033ae0 16.0.2
Signed-off-by: Audrow Nash <audrow@openrobotics.org>
2022-11-07 09:12:14 -06:00
mergify[bot]
4fa3489cfd fix mismatched issue if using zero_allocate (#1995) (#2026)
* fix mismatched issue if uzing zero_allocated

Signed-off-by: Chen Lihui <lihui.chen@sony.com>
(cherry picked from commit 978439191f)

Co-authored-by: Chen Lihui <lihui.chen@sony.com>
2022-10-17 10:50:44 -07:00
mergify[bot]
7f575103d8 use regex for wildcard matching (backport #1839) (#1986)
* use regex for wildcard matching (#1839)

* use regex for wildcard matching

Co-authored-by: Aaron Lipinski <aaron.lipinski@roboticsplus.co.nz>
Signed-off-by: Chen Lihui <lihui.chen@sony.com>

* use map to process the content of parameter file by order

Signed-off-by: Chen Lihui <lihui.chen@sony.com>

* add more test cases

Signed-off-by: Chen Lihui <lihui.chen@sony.com>

* try to not decrease the performance and make the param win last

Signed-off-by: Chen Lihui <lihui.chen@sony.com>

* update node name

Signed-off-by: Chen Lihui <lihui.chen@sony.com>

* update document comment

Signed-off-by: Chen Lihui <lihui.chen@sony.com>

* add more test for parameter_map_from

Signed-off-by: Chen Lihui <lihui.chen@sony.com>

Co-authored-by: Aaron Lipinski <aaron.lipinski@roboticsplus.co.nz>
(cherry picked from commit 6dd3a0377b)

* not to break ABI

Signed-off-by: Chen Lihui <lihui.chen@sony.com>

Signed-off-by: Chen Lihui <lihui.chen@sony.com>
Co-authored-by: Chen Lihui <lihui.chen@sony.com>
2022-09-09 11:55:00 -07:00
mergify[bot]
166007dde3 Drop wrong template specialization (#1926) (#1937)
This fails with g++ -std=gnu++20.

Signed-off-by: Jochen Sprickerhof <git@jochen.sprickerhof.de>
(cherry picked from commit 02802bcc38)

Co-authored-by: Jochen Sprickerhof <github@jochen.sprickerhof.de>
2022-06-15 15:02:18 -03:00
mergify[bot]
cf2a27805e Add statistics for handle_loaned_message (#1927) (#1932)
* Add statistics for handle_loaned_message

Signed-off-by: Barry Xu <barry.xu@sony.com>
(cherry picked from commit 5c688303b3)

Co-authored-by: Barry Xu <barry.xu@sony.com>
2022-06-09 08:54:15 -07:00
37 changed files with 763 additions and 86 deletions

View File

@@ -2,6 +2,27 @@
Changelog for package rclcpp
^^^^^^^^^^^^^^^^^^^^^^^^^^^^
16.0.4 (2023-04-25)
-------------------
* use allocator via init_options argument. (`#2129 <https://github.com/ros2/rclcpp/issues/2129>`_) (`#2131 <https://github.com/ros2/rclcpp/issues/2131>`_)
* Contributors: mergify[bot]
16.0.3 (2023-01-10)
-------------------
* Fix SharedFuture from async_send_request never becomes valid (`#2044 <https://github.com/ros2/rclcpp/issues/2044>`_) (`#2076 <https://github.com/ros2/rclcpp/issues/2076>`_)
* do not throw exception if trying to dequeue an empty intra-process buffer (`#2061 <https://github.com/ros2/rclcpp/issues/2061>`_) (`#2070 <https://github.com/ros2/rclcpp/issues/2070>`_)
* fix nullptr dereference in prune_requests_older_than (`#2008 <https://github.com/ros2/rclcpp/issues/2008>`_) (`#2065 <https://github.com/ros2/rclcpp/issues/2065>`_)
* Fix bug that a callback not reached (`#1640 <https://github.com/ros2/rclcpp/issues/1640>`_) (`#2033 <https://github.com/ros2/rclcpp/issues/2033>`_)
* Contributors: mergify[bot]
16.0.2 (2022-11-07)
-------------------
* fix mismatched issue if using zero_allocate (`#1995 <https://github.com/ros2/rclcpp/issues/1995>`_) (`#2026 <https://github.com/ros2/rclcpp/issues/2026>`_)
* use regex for wildcard matching (backport `#1839 <https://github.com/ros2/rclcpp/issues/1839>`_) (`#1986 <https://github.com/ros2/rclcpp/issues/1986>`_)
* Drop wrong template specialization (`#1926 <https://github.com/ros2/rclcpp/issues/1926>`_) (`#1937 <https://github.com/ros2/rclcpp/issues/1937>`_)
* Add statistics for handle_loaned_message (`#1927 <https://github.com/ros2/rclcpp/issues/1927>`_) (`#1932 <https://github.com/ros2/rclcpp/issues/1932>`_)
* Contributors: mergify[bot]
16.0.1 (2022-04-13)
-------------------
* remove DEFINE_CONTENT_FILTER cmake option (`#1914 <https://github.com/ros2/rclcpp/issues/1914>`_)

View File

@@ -15,6 +15,7 @@
#ifndef RCLCPP__ALLOCATOR__ALLOCATOR_COMMON_HPP_
#define RCLCPP__ALLOCATOR__ALLOCATOR_COMMON_HPP_
#include <cstring>
#include <memory>
#include "rcl/allocator.h"
@@ -39,6 +40,22 @@ void * retyped_allocate(size_t size, void * untyped_allocator)
return std::allocator_traits<Alloc>::allocate(*typed_allocator, size);
}
template<typename Alloc>
void * retyped_zero_allocate(size_t number_of_elem, size_t size_of_elem, void * untyped_allocator)
{
auto typed_allocator = static_cast<Alloc *>(untyped_allocator);
if (!typed_allocator) {
throw std::runtime_error("Received incorrect allocator type");
}
size_t size = number_of_elem * size_of_elem;
void * allocated_memory =
std::allocator_traits<Alloc>::allocate(*typed_allocator, size);
if (allocated_memory) {
std::memset(allocated_memory, 0, size);
}
return allocated_memory;
}
template<typename T, typename Alloc>
void retyped_deallocate(void * untyped_pointer, void * untyped_allocator)
{
@@ -73,6 +90,7 @@ rcl_allocator_t get_rcl_allocator(Alloc & allocator)
rcl_allocator_t rcl_allocator = rcl_get_default_allocator();
#ifndef _WIN32
rcl_allocator.allocate = &retyped_allocate<Alloc>;
rcl_allocator.zero_allocate = &retyped_zero_allocate<Alloc>;
rcl_allocator.deallocate = &retyped_deallocate<T, Alloc>;
rcl_allocator.reallocate = &retyped_reallocate<T, Alloc>;
rcl_allocator.state = &allocator;

View File

@@ -16,11 +16,14 @@
#define RCLCPP__CALLBACK_GROUP_HPP_
#include <atomic>
#include <functional>
#include <memory>
#include <mutex>
#include <string>
#include <vector>
#include "rclcpp/client.hpp"
#include "rclcpp/context.hpp"
#include "rclcpp/guard_condition.hpp"
#include "rclcpp/publisher_base.hpp"
#include "rclcpp/service.hpp"
#include "rclcpp/subscription_base.hpp"
@@ -95,6 +98,10 @@ public:
CallbackGroupType group_type,
bool automatically_add_to_executor_with_node = true);
/// Default destructor.
RCLCPP_PUBLIC
~CallbackGroup();
template<typename Function>
rclcpp::SubscriptionBase::SharedPtr
find_subscription_ptrs_if(Function func) const
@@ -171,6 +178,16 @@ public:
bool
automatically_add_to_executor_with_node() const;
/// Defer creating the notify guard condition and return it.
RCLCPP_PUBLIC
rclcpp::GuardCondition::SharedPtr
get_notify_guard_condition(const rclcpp::Context::SharedPtr context_ptr);
/// Trigger the notify guard condition.
RCLCPP_PUBLIC
void
trigger_notify_guard_condition();
protected:
RCLCPP_DISABLE_COPY(CallbackGroup)
@@ -213,6 +230,9 @@ protected:
std::vector<rclcpp::Waitable::WeakPtr> waitable_ptrs_;
std::atomic_bool can_be_taken_from_;
const bool automatically_add_to_executor_with_node_;
// defer the creation of the guard condition
std::shared_ptr<rclcpp::GuardCondition> notify_guard_condition_ = nullptr;
std::recursive_mutex notify_guard_condition_mutex_;
private:
template<typename TypeT, typename Function>

View File

@@ -769,7 +769,9 @@ public:
auto old_size = pending_requests_.size();
for (auto it = pending_requests_.begin(), last = pending_requests_.end(); it != last; ) {
if (it->second.first < time_point) {
pruned_requests->push_back(it->first);
if (pruned_requests) {
pruned_requests->push_back(it->first);
}
it = pending_requests_.erase(it);
} else {
++it;
@@ -792,16 +794,14 @@ protected:
async_send_request_impl(const Request & request, CallbackInfoVariant value)
{
int64_t sequence_number;
std::lock_guard<std::mutex> lock(pending_requests_mutex_);
rcl_ret_t ret = rcl_send_request(get_client_handle().get(), &request, &sequence_number);
if (RCL_RET_OK != ret) {
rclcpp::exceptions::throw_from_rcl_error(ret, "failed to send request");
}
{
std::lock_guard<std::mutex> lock(pending_requests_mutex_);
pending_requests_.try_emplace(
sequence_number,
std::make_pair(std::chrono::system_clock::now(), std::move(value)));
}
pending_requests_.try_emplace(
sequence_number,
std::make_pair(std::chrono::system_clock::now(), std::move(value)));
return sequence_number;
}

View File

@@ -560,14 +560,14 @@ protected:
virtual void
spin_once_impl(std::chrono::nanoseconds timeout);
typedef std::map<rclcpp::node_interfaces::NodeBaseInterface::WeakPtr,
typedef std::map<rclcpp::CallbackGroup::WeakPtr,
const rclcpp::GuardCondition *,
std::owner_less<rclcpp::node_interfaces::NodeBaseInterface::WeakPtr>>
WeakNodesToGuardConditionsMap;
std::owner_less<rclcpp::CallbackGroup::WeakPtr>>
WeakCallbackGroupsToGuardConditionsMap;
/// maps nodes to guard conditions
WeakNodesToGuardConditionsMap
weak_nodes_to_guard_conditions_ RCPPUTILS_TSA_GUARDED_BY(mutex_);
/// maps callback groups to guard conditions
WeakCallbackGroupsToGuardConditionsMap
weak_groups_to_guard_conditions_ RCPPUTILS_TSA_GUARDED_BY(mutex_);
/// maps callback groups associated to nodes
WeakCallbackGroupsToNodesMap

View File

@@ -86,8 +86,7 @@ public:
std::lock_guard<std::mutex> lock(mutex_);
if (!has_data_()) {
RCLCPP_ERROR(rclcpp::get_logger("rclcpp"), "Calling dequeue on empty intra-process buffer");
throw std::runtime_error("Calling dequeue on empty intra-process buffer");
return BufferT();
}
auto request = std::move(ring_buffer_[read_index_]);

View File

@@ -109,8 +109,14 @@ public:
if (any_callback_.use_take_shared_method()) {
shared_msg = this->buffer_->consume_shared();
if (!shared_msg) {
return nullptr;
}
} else {
unique_msg = this->buffer_->consume_unique();
if (!unique_msg) {
return nullptr;
}
}
return std::static_pointer_cast<void>(
std::make_shared<std::pair<ConstMessageSharedPtr, MessageUniquePtr>>(
@@ -138,7 +144,7 @@ protected:
execute_impl(std::shared_ptr<void> & data)
{
if (!data) {
throw std::runtime_error("'data' is empty");
return;
}
rmw_message_info_t msg_info;

View File

@@ -41,6 +41,16 @@ RCLCPP_PUBLIC
ParameterMap
parameter_map_from(const rcl_params_t * const c_params);
/// Convert parameters from rcl_yaml_param_parser into C++ class instances.
/// \param[in] c_params C structures containing parameters for multiple nodes.
/// \param[in] node_fqn a Fully Qualified Name of node, default value is nullptr.
/// If it's not nullptr, return the relative node parameters belonging to this node_fqn.
/// \returns a map where the keys are fully qualified node names and values a list of parameters.
/// \throws InvalidParametersException if the `rcl_params_t` is inconsistent or invalid.
RCLCPP_PUBLIC
ParameterMap
parameter_map_from(const rcl_params_t * const c_params, const char * node_fqn);
/// Convert parameter value from rcl_yaml_param_parser into a C++ class instance.
/// \param[in] c_value C structure containing a value of a parameter.
/// \returns an instance of a parameter value

View File

@@ -72,7 +72,7 @@ struct PublisherOptionsWithAllocator : public PublisherOptionsBase
/// Optional custom allocator.
std::shared_ptr<Allocator> allocator = nullptr;
PublisherOptionsWithAllocator<Allocator>() {}
PublisherOptionsWithAllocator() {}
/// Constructor using base class as input.
explicit PublisherOptionsWithAllocator(const PublisherOptionsBase & publisher_options_base)

View File

@@ -363,11 +363,31 @@ public:
void * loaned_message,
const rclcpp::MessageInfo & message_info) override
{
if (matches_any_intra_process_publishers(&message_info.get_rmw_message_info().publisher_gid)) {
// In this case, the message will be delivered via intra process and
// we should ignore this copy of the message.
return;
}
auto typed_message = static_cast<ROSMessageType *>(loaned_message);
// message is loaned, so we have to make sure that the deleter does not deallocate the message
auto sptr = std::shared_ptr<ROSMessageType>(
typed_message, [](ROSMessageType * msg) {(void) msg;});
std::chrono::time_point<std::chrono::system_clock> now;
if (subscription_topic_statistics_) {
// get current time before executing callback to
// exclude callback duration from topic statistics result.
now = std::chrono::system_clock::now();
}
any_callback_.dispatch(sptr, message_info);
if (subscription_topic_statistics_) {
const auto nanos = std::chrono::time_point_cast<std::chrono::nanoseconds>(now);
const auto time = rclcpp::Time(nanos.time_since_epoch().count());
subscription_topic_statistics_->handle_message(*typed_message, time);
}
}
/// Return the borrowed message.

View File

@@ -97,7 +97,7 @@ struct SubscriptionOptionsWithAllocator : public SubscriptionOptionsBase
/// Optional custom allocator.
std::shared_ptr<Allocator> allocator = nullptr;
SubscriptionOptionsWithAllocator<Allocator>() {}
SubscriptionOptionsWithAllocator() {}
/// Constructor using base class as input.
explicit SubscriptionOptionsWithAllocator(

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>16.0.1</version>
<version>16.0.4</version>
<description>The ROS client library in C++.</description>
<maintainer email="ivanpauno@ekumenlabs.com">Ivan Paunovic</maintainer>
<maintainer email="jacob@openrobotics.org">Jacob Perron</maintainer>

View File

@@ -12,9 +12,19 @@
// See the License for the specific language governing permissions and
// limitations under the License.
#include "rclcpp/callback_group.hpp"
#include <algorithm>
#include <atomic>
#include <functional>
#include <memory>
#include <mutex>
#include <stdexcept>
#include <vector>
#include "rclcpp/callback_group.hpp"
#include "rclcpp/client.hpp"
#include "rclcpp/service.hpp"
#include "rclcpp/subscription_base.hpp"
#include "rclcpp/timer.hpp"
#include "rclcpp/waitable.hpp"
using rclcpp::CallbackGroup;
using rclcpp::CallbackGroupType;
@@ -27,6 +37,10 @@ CallbackGroup::CallbackGroup(
automatically_add_to_executor_with_node_(automatically_add_to_executor_with_node)
{}
CallbackGroup::~CallbackGroup()
{
trigger_notify_guard_condition();
}
std::atomic_bool &
CallbackGroup::can_be_taken_from()
@@ -97,6 +111,33 @@ CallbackGroup::automatically_add_to_executor_with_node() const
return automatically_add_to_executor_with_node_;
}
rclcpp::GuardCondition::SharedPtr
CallbackGroup::get_notify_guard_condition(const rclcpp::Context::SharedPtr context_ptr)
{
std::lock_guard<std::recursive_mutex> lock(notify_guard_condition_mutex_);
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_;
}
void
CallbackGroup::trigger_notify_guard_condition()
{
std::lock_guard<std::recursive_mutex> lock(notify_guard_condition_mutex_);
if (notify_guard_condition_) {
notify_guard_condition_->trigger();
}
}
void
CallbackGroup::add_subscription(
const rclcpp::SubscriptionBase::SharedPtr subscription_ptr)

View File

@@ -217,7 +217,7 @@ Context::init(
if (0u == count) {
ret = rcl_logging_configure_with_output_handler(
&rcl_context_->global_arguments,
rcl_init_options_get_allocator(init_options_.get_rcl_init_options()),
rcl_init_options_get_allocator(init_options.get_rcl_init_options()),
rclcpp_logging_output_handler);
if (RCL_RET_OK != ret) {
rcl_context_.reset();

View File

@@ -51,18 +51,13 @@ rclcpp::detail::resolve_parameter_overrides(
[params]() {
rcl_yaml_node_struct_fini(params);
});
rclcpp::ParameterMap initial_map = rclcpp::parameter_map_from(params);
rclcpp::ParameterMap initial_map = rclcpp::parameter_map_from(params, node_fqn.c_str());
// Enforce wildcard matching precedence
// TODO(cottsay) implement further wildcard matching
const std::array<std::string, 2> node_matching_names{"/**", node_fqn};
for (const auto & node_name : node_matching_names) {
if (initial_map.count(node_name) > 0) {
// Combine parameter yaml files, overwriting values in older ones
for (const rclcpp::Parameter & param : initial_map.at(node_name)) {
result[param.get_name()] =
rclcpp::ParameterValue(param.get_value_message());
}
if (initial_map.count(node_fqn) > 0) {
// Combine parameter yaml files, overwriting values in older ones
for (const rclcpp::Parameter & param : initial_map.at(node_fqn)) {
result[param.get_name()] =
rclcpp::ParameterValue(param.get_value_message());
}
}
}

View File

@@ -106,11 +106,11 @@ Executor::~Executor()
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_nodes_to_guard_conditions_) {
for (const auto & pair : weak_groups_to_guard_conditions_) {
auto guard_condition = pair.second;
memory_strategy_->remove_guard_condition(guard_condition);
}
weak_nodes_to_guard_conditions_.clear();
weak_groups_to_guard_conditions_.clear();
// Finalize the wait set.
if (rcl_wait_set_fini(&wait_set_) != RCL_RET_OK) {
@@ -204,8 +204,7 @@ Executor::add_callback_group_to_map(
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_to_nodes_associated_with_executor_) &&
!has_node(node_ptr, weak_groups_associated_with_executor_to_nodes_);
rclcpp::CallbackGroup::WeakPtr weak_group_ptr = group_ptr;
auto insert_info =
weak_groups_to_nodes.insert(std::make_pair(weak_group_ptr, node_ptr));
@@ -215,21 +214,24 @@ Executor::add_callback_group_to_map(
}
// Also add to the map that contains all callback groups
weak_groups_to_nodes_.insert(std::make_pair(weak_group_ptr, node_ptr));
if (is_new_node) {
const auto & gc = node_ptr->get_notify_guard_condition();
weak_nodes_to_guard_conditions_[node_ptr] = &gc;
if (notify) {
// Interrupt waiting to handle new node
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 add: ") + ex.what());
}
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();
} catch (const rclcpp::exceptions::RCLError & ex) {
throw std::runtime_error(
std::string(
"Failed to trigger guard condition on callback group add: ") + ex.what());
}
// Add the node's notify condition to the guard condition handles
memory_strategy_->add_guard_condition(gc);
}
}
@@ -300,7 +302,12 @@ Executor::remove_callback_group_from_map(
if (!has_node(node_ptr, weak_groups_to_nodes_associated_with_executor_) &&
!has_node(node_ptr, weak_groups_associated_with_executor_to_nodes_))
{
weak_nodes_to_guard_conditions_.erase(node_ptr);
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();
@@ -310,7 +317,6 @@ Executor::remove_callback_group_from_map(
"Failed to trigger guard condition on callback group remove: ") + ex.what());
}
}
memory_strategy_->remove_guard_condition(&node_ptr->get_notify_guard_condition());
}
}
@@ -700,12 +706,6 @@ Executor::wait_for_work(std::chrono::nanoseconds timeout)
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(
@@ -721,6 +721,12 @@ Executor::wait_for_work(std::chrono::nanoseconds timeout)
{
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);
});
}

View File

@@ -35,15 +35,17 @@ NodeServices::add_service(
// TODO(jacquelinekay): use custom exception
throw std::runtime_error("Cannot create service, group not in node.");
}
group->add_service(service_base_ptr);
} else {
node_base_->get_default_callback_group()->add_service(service_base_ptr);
group = node_base_->get_default_callback_group();
}
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_gc.trigger();
group->trigger_notify_guard_condition();
} catch (const rclcpp::exceptions::RCLError & ex) {
throw std::runtime_error(
std::string("failed to notify wait set on service creation: ") + ex.what());
@@ -60,15 +62,17 @@ NodeServices::add_client(
// TODO(jacquelinekay): use custom exception
throw std::runtime_error("Cannot create client, group not in node.");
}
group->add_client(client_base_ptr);
} else {
node_base_->get_default_callback_group()->add_client(client_base_ptr);
group = node_base_->get_default_callback_group();
}
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_gc.trigger();
group->trigger_notify_guard_condition();
} catch (const rclcpp::exceptions::RCLError & ex) {
throw std::runtime_error(
std::string("failed to notify wait set on client creation: ") + ex.what());

View File

@@ -37,14 +37,15 @@ NodeTimers::add_timer(
// TODO(jacquelinekay): use custom exception
throw std::runtime_error("Cannot create timer, group not in node.");
}
callback_group->add_timer(timer);
} else {
node_base_->get_default_callback_group()->add_timer(timer);
callback_group = node_base_->get_default_callback_group();
}
callback_group->add_timer(timer);
auto & node_gc = node_base_->get_notify_guard_condition();
try {
node_gc.trigger();
callback_group->trigger_notify_guard_condition();
} catch (const rclcpp::exceptions::RCLError & ex) {
throw std::runtime_error(
std::string("failed to notify wait set on timer creation: ") + ex.what());

View File

@@ -73,6 +73,7 @@ NodeTopics::add_publisher(
auto & node_gc = node_base_->get_notify_guard_condition();
try {
node_gc.trigger();
callback_group->trigger_notify_guard_condition();
} catch (const rclcpp::exceptions::RCLError & ex) {
throw std::runtime_error(
std::string("failed to notify wait set on publisher creation: ") + ex.what());
@@ -121,6 +122,7 @@ NodeTopics::add_subscription(
auto & node_gc = node_base_->get_notify_guard_condition();
try {
node_gc.trigger();
callback_group->trigger_notify_guard_condition();
} catch (const rclcpp::exceptions::RCLError & ex) {
throw std::runtime_error(
std::string("failed to notify wait set on subscription creation: ") + ex.what());

View File

@@ -35,15 +35,17 @@ NodeWaitables::add_waitable(
// TODO(jacobperron): use custom exception
throw std::runtime_error("Cannot create waitable, group not in node.");
}
group->add_waitable(waitable_ptr);
} else {
node_base_->get_default_callback_group()->add_waitable(waitable_ptr);
group = node_base_->get_default_callback_group();
}
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_gc.trigger();
group->trigger_notify_guard_condition();
} catch (const rclcpp::exceptions::RCLError & ex) {
throw std::runtime_error(
std::string("failed to notify wait set on waitable creation: ") + ex.what());

View File

@@ -13,8 +13,10 @@
// limitations under the License.
#include <string>
#include <regex>
#include <vector>
#include "rcpputils/find_and_replace.hpp"
#include "rclcpp/parameter_map.hpp"
using rclcpp::exceptions::InvalidParametersException;
@@ -24,6 +26,12 @@ using rclcpp::ParameterValue;
ParameterMap
rclcpp::parameter_map_from(const rcl_params_t * const c_params)
{
return parameter_map_from(c_params, nullptr);
}
ParameterMap
rclcpp::parameter_map_from(const rcl_params_t * const c_params, const char * node_fqn)
{
if (NULL == c_params) {
throw InvalidParametersException("parameters struct is NULL");
@@ -49,6 +57,17 @@ rclcpp::parameter_map_from(const rcl_params_t * const c_params)
node_name = c_node_name;
}
if (node_fqn) {
// Update the regular expression ["/*" -> "(/\\w+)" and "/**" -> "(/\\w+)*"]
std::string regex = rcpputils::find_and_replace(node_name, "/*", "(/\\w+)");
if (!std::regex_match(node_fqn, std::regex(regex))) {
// No need to parse the items because the user just care about node_fqn
continue;
}
node_name = node_fqn;
}
const rcl_node_params_t * const c_params_node = &(c_params->params[n]);
std::vector<Parameter> & params_node = parameters[node_name];
@@ -65,6 +84,7 @@ rclcpp::parameter_map_from(const rcl_params_t * const c_params)
params_node.emplace_back(c_param_name, parameter_value_from(c_param_value));
}
}
return parameters;
}

View File

@@ -100,15 +100,20 @@ if(TARGET test_create_subscription)
"test_msgs"
)
endif()
ament_add_gtest(test_add_callback_groups_to_executor
test_add_callback_groups_to_executor.cpp
TIMEOUT 120)
if(TARGET test_add_callback_groups_to_executor)
target_link_libraries(test_add_callback_groups_to_executor ${PROJECT_NAME})
ament_target_dependencies(test_add_callback_groups_to_executor
"test_msgs"
function(test_add_callback_groups_to_executor_for_rmw_implementation)
set(rmw_implementation_env_var RMW_IMPLEMENTATION=${rmw_implementation})
ament_add_gmock(test_add_callback_groups_to_executor${target_suffix} test_add_callback_groups_to_executor.cpp
ENV ${rmw_implementation_env_var}
TIMEOUT 120
)
endif()
if(TARGET test_add_callback_groups_to_executor${target_suffix})
target_link_libraries(test_add_callback_groups_to_executor${target_suffix} ${PROJECT_NAME})
ament_target_dependencies(test_add_callback_groups_to_executor${target_suffix}
"test_msgs"
)
endif()
endfunction()
call_for_each_rmw_implementation(test_add_callback_groups_to_executor_for_rmw_implementation)
ament_add_gtest(test_expand_topic_or_service_name test_expand_topic_or_service_name.cpp)
if(TARGET test_expand_topic_or_service_name)
ament_target_dependencies(test_expand_topic_or_service_name

View File

@@ -51,6 +51,53 @@ TEST(TestAllocatorCommon, retyped_allocate) {
EXPECT_NO_THROW(code2());
}
TEST(TestAllocatorCommon, retyped_zero_allocate_basic) {
std::allocator<int> allocator;
void * untyped_allocator = &allocator;
void * allocated_mem =
rclcpp::allocator::retyped_zero_allocate<std::allocator<char>>(20u, 1u, untyped_allocator);
ASSERT_TRUE(nullptr != allocated_mem);
auto code = [&untyped_allocator, allocated_mem]() {
rclcpp::allocator::retyped_deallocate<char, std::allocator<char>>(
allocated_mem, untyped_allocator);
};
EXPECT_NO_THROW(code());
}
TEST(TestAllocatorCommon, retyped_zero_allocate) {
std::allocator<int> allocator;
void * untyped_allocator = &allocator;
void * allocated_mem =
rclcpp::allocator::retyped_zero_allocate<std::allocator<char>>(20u, 1u, untyped_allocator);
// The more natural check here is ASSERT_NE(nullptr, ptr), but clang static
// analysis throws a false-positive memory leak warning. Use ASSERT_TRUE instead.
ASSERT_TRUE(nullptr != allocated_mem);
auto code = [&untyped_allocator, allocated_mem]() {
rclcpp::allocator::retyped_deallocate<int, std::allocator<int>>(
allocated_mem, untyped_allocator);
};
EXPECT_NO_THROW(code());
allocated_mem = allocator.allocate(1);
// The more natural check here is ASSERT_NE(nullptr, ptr), but clang static
// analysis throws a false-positive memory leak warning. Use ASSERT_TRUE instead.
ASSERT_TRUE(nullptr != allocated_mem);
void * reallocated_mem =
rclcpp::allocator::retyped_reallocate<int, std::allocator<int>>(
allocated_mem, 2u, untyped_allocator);
// The more natural check here is ASSERT_NE(nullptr, ptr), but clang static
// analysis throws a false-positive memory leak warning. Use ASSERT_TRUE instead.
ASSERT_TRUE(nullptr != reallocated_mem);
auto code2 = [&untyped_allocator, reallocated_mem]() {
rclcpp::allocator::retyped_deallocate<int, std::allocator<int>>(
reallocated_mem, untyped_allocator);
};
EXPECT_NO_THROW(code2());
}
TEST(TestAllocatorCommon, get_rcl_allocator) {
std::allocator<int> allocator;
auto rcl_allocator = rclcpp::allocator::get_rcl_allocator<int>(allocator);

View File

@@ -31,6 +31,8 @@
#include "../../mocking_utils/patch.hpp"
#include "../../utils/rclcpp_gtest_macros.hpp"
#include "rcpputils/filesystem_helper.hpp"
class TestNodeParameters : public ::testing::Test
{
public:
@@ -47,6 +49,7 @@ public:
dynamic_cast<rclcpp::node_interfaces::NodeParameters *>(
node->get_node_parameters_interface().get());
ASSERT_NE(nullptr, node_parameters);
test_resources_path /= "test_node_parameters";
}
void TearDown()
@@ -57,6 +60,8 @@ public:
protected:
std::shared_ptr<rclcpp::Node> node;
rclcpp::node_interfaces::NodeParameters * node_parameters;
rcpputils::fs::path test_resources_path{TEST_RESOURCES_DIRECTORY};
};
TEST_F(TestNodeParameters, construct_destruct_rcl_errors) {
@@ -199,3 +204,130 @@ TEST_F(TestNodeParameters, add_remove_parameters_callback) {
node_parameters->remove_on_set_parameters_callback(handle.get()),
std::runtime_error("Callback doesn't exist"));
}
TEST_F(TestNodeParameters, wildcard_with_namespace)
{
rclcpp::NodeOptions opts;
opts.arguments(
{
"--ros-args",
"--params-file", (test_resources_path / "wildcards.yaml").string()
});
std::shared_ptr<rclcpp::Node> node = std::make_shared<rclcpp::Node>("node2", "ns", opts);
auto * node_parameters =
dynamic_cast<rclcpp::node_interfaces::NodeParameters *>(
node->get_node_parameters_interface().get());
ASSERT_NE(nullptr, node_parameters);
const auto & parameter_overrides = node_parameters->get_parameter_overrides();
EXPECT_EQ(7u, parameter_overrides.size());
EXPECT_EQ(parameter_overrides.at("full_wild").get<std::string>(), "full_wild");
EXPECT_EQ(parameter_overrides.at("namespace_wild").get<std::string>(), "namespace_wild");
EXPECT_EQ(
parameter_overrides.at("namespace_wild_another").get<std::string>(),
"namespace_wild_another");
EXPECT_EQ(
parameter_overrides.at("namespace_wild_one_star").get<std::string>(),
"namespace_wild_one_star");
EXPECT_EQ(parameter_overrides.at("node_wild_in_ns").get<std::string>(), "node_wild_in_ns");
EXPECT_EQ(
parameter_overrides.at("node_wild_in_ns_another").get<std::string>(),
"node_wild_in_ns_another");
EXPECT_EQ(parameter_overrides.at("explicit_in_ns").get<std::string>(), "explicit_in_ns");
EXPECT_EQ(parameter_overrides.count("should_not_appear"), 0u);
}
TEST_F(TestNodeParameters, wildcard_no_namespace)
{
rclcpp::NodeOptions opts;
opts.arguments(
{
"--ros-args",
"--params-file", (test_resources_path / "wildcards.yaml").string()
});
std::shared_ptr<rclcpp::Node> node = std::make_shared<rclcpp::Node>("node2", opts);
auto * node_parameters =
dynamic_cast<rclcpp::node_interfaces::NodeParameters *>(
node->get_node_parameters_interface().get());
ASSERT_NE(nullptr, node_parameters);
const auto & parameter_overrides = node_parameters->get_parameter_overrides();
EXPECT_EQ(5u, parameter_overrides.size());
EXPECT_EQ(parameter_overrides.at("full_wild").get<std::string>(), "full_wild");
EXPECT_EQ(parameter_overrides.at("namespace_wild").get<std::string>(), "namespace_wild");
EXPECT_EQ(
parameter_overrides.at("namespace_wild_another").get<std::string>(),
"namespace_wild_another");
EXPECT_EQ(parameter_overrides.at("node_wild_no_ns").get<std::string>(), "node_wild_no_ns");
EXPECT_EQ(parameter_overrides.at("explicit_no_ns").get<std::string>(), "explicit_no_ns");
EXPECT_EQ(parameter_overrides.count("should_not_appear"), 0u);
// "/*" match exactly one token, not expect to get `namespace_wild_one_star`
EXPECT_EQ(parameter_overrides.count("namespace_wild_one_star"), 0u);
}
TEST_F(TestNodeParameters, params_by_order)
{
rclcpp::NodeOptions opts;
opts.arguments(
{
"--ros-args",
"--params-file", (test_resources_path / "params_by_order.yaml").string()
});
std::shared_ptr<rclcpp::Node> node = std::make_shared<rclcpp::Node>("node2", "ns", opts);
auto * node_parameters =
dynamic_cast<rclcpp::node_interfaces::NodeParameters *>(
node->get_node_parameters_interface().get());
ASSERT_NE(nullptr, node_parameters);
const auto & parameter_overrides = node_parameters->get_parameter_overrides();
EXPECT_EQ(3u, parameter_overrides.size());
EXPECT_EQ(parameter_overrides.at("a_value").get<std::string>(), "last_one_win");
EXPECT_EQ(parameter_overrides.at("foo").get<std::string>(), "foo");
EXPECT_EQ(parameter_overrides.at("bar").get<std::string>(), "bar");
}
TEST_F(TestNodeParameters, complicated_wildcards)
{
rclcpp::NodeOptions opts;
opts.arguments(
{
"--ros-args",
"--params-file", (test_resources_path / "complicated_wildcards.yaml").string()
});
{
// regex matched: /**/foo/*/bar
std::shared_ptr<rclcpp::Node> node =
std::make_shared<rclcpp::Node>("node2", "/a/b/c/foo/d/bar", opts);
auto * node_parameters =
dynamic_cast<rclcpp::node_interfaces::NodeParameters *>(
node->get_node_parameters_interface().get());
ASSERT_NE(nullptr, node_parameters);
const auto & parameter_overrides = node_parameters->get_parameter_overrides();
EXPECT_EQ(2u, parameter_overrides.size());
EXPECT_EQ(parameter_overrides.at("foo").get<std::string>(), "foo");
EXPECT_EQ(parameter_overrides.at("bar").get<std::string>(), "bar");
}
{
// regex not matched: /**/foo/*/bar
std::shared_ptr<rclcpp::Node> node =
std::make_shared<rclcpp::Node>("node2", "/a/b/c/foo/bar", opts);
auto * node_parameters =
dynamic_cast<rclcpp::node_interfaces::NodeParameters *>(
node->get_node_parameters_interface().get());
ASSERT_NE(nullptr, node_parameters);
const auto & parameter_overrides = node_parameters->get_parameter_overrides();
EXPECT_EQ(0u, parameter_overrides.size());
}
}

View File

@@ -276,6 +276,70 @@ TYPED_TEST(TestAddCallbackGroupsToExecutor, one_node_many_callback_groups_many_e
ASSERT_EQ(timer_executor.get_all_callback_groups().size(), 2u);
}
/*
* Test callback groups from one node to many executors.
* A subscriber on a new executor with a callback group not received a message
* because the executor can't be triggered while a subscriber created, see
* https://github.com/ros2/rclcpp/issues/1611
*/
TYPED_TEST(TestAddCallbackGroupsToExecutor, subscriber_triggered_to_receive_message)
{
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);
rclcpp::executors::SingleThreadedExecutor cb_grp_executor;
std::promise<bool> received_message_promise;
auto received_message_future = received_message_promise.get_future();
rclcpp::FutureReturnCode return_code = rclcpp::FutureReturnCode::TIMEOUT;
std::thread cb_grp_thread = std::thread(
[&cb_grp, &node, &cb_grp_executor, &received_message_future, &return_code]() {
cb_grp_executor.add_callback_group(cb_grp, node->get_node_base_interface());
return_code = cb_grp_executor.spin_until_future_complete(received_message_future, 10s);
});
// expect the subscriber to receive a message
auto sub_callback = [&received_message_promise](test_msgs::msg::Empty::ConstSharedPtr) {
received_message_promise.set_value(true);
};
rclcpp::Subscription<test_msgs::msg::Empty>::SharedPtr subscription;
rclcpp::Publisher<test_msgs::msg::Empty>::SharedPtr publisher;
// to create a timer with a callback run on another executor
rclcpp::TimerBase::SharedPtr timer = nullptr;
std::promise<void> timer_promise;
auto timer_callback =
[&subscription, &publisher, &timer, &cb_grp, &node, &sub_callback, &timer_promise]() {
if (timer) {
timer.reset();
}
// create a subscription using the `cb_grp` callback group
rclcpp::QoS qos = rclcpp::QoS(1).reliable();
auto options = rclcpp::SubscriptionOptions();
options.callback_group = cb_grp;
subscription =
node->create_subscription<test_msgs::msg::Empty>("topic_name", qos, sub_callback, options);
// create a publisher to send data
publisher =
node->create_publisher<test_msgs::msg::Empty>("topic_name", qos);
publisher->publish(test_msgs::msg::Empty());
timer_promise.set_value();
};
rclcpp::executors::SingleThreadedExecutor timer_executor;
timer = node->create_wall_timer(100ms, timer_callback);
timer_executor.add_node(node);
auto future = timer_promise.get_future();
timer_executor.spin_until_future_complete(future);
cb_grp_thread.join();
ASSERT_EQ(rclcpp::FutureReturnCode::SUCCESS, return_code);
EXPECT_TRUE(received_message_future.get());
}
/*
* Test removing callback group from executor that its not associated with.
*/

View File

@@ -282,6 +282,27 @@ TEST_F(TestClientWithServer, test_client_remove_pending_request) {
EXPECT_TRUE(client->remove_pending_request(future));
}
TEST_F(TestClientWithServer, prune_requests_older_than_no_pruned) {
auto client = node->create_client<test_msgs::srv::Empty>(service_name);
auto request = std::make_shared<test_msgs::srv::Empty::Request>();
auto future = client->async_send_request(request);
auto time = std::chrono::system_clock::now() + 1s;
EXPECT_EQ(1u, client->prune_requests_older_than(time));
}
TEST_F(TestClientWithServer, prune_requests_older_than_with_pruned) {
auto client = node->create_client<test_msgs::srv::Empty>(service_name);
auto request = std::make_shared<test_msgs::srv::Empty::Request>();
auto future = client->async_send_request(request);
auto time = std::chrono::system_clock::now() + 1s;
std::vector<int64_t> pruned_requests;
EXPECT_EQ(1u, client->prune_requests_older_than(time, &pruned_requests));
ASSERT_EQ(1u, pruned_requests.size());
EXPECT_EQ(future.request_id, pruned_requests[0]);
}
TEST_F(TestClientWithServer, async_send_request_rcl_send_request_error) {
// Checking rcl_send_request in rclcpp::Client::async_send_request()
auto mock = mocking_utils::patch_and_return("lib:rclcpp", rcl_send_request, RCL_RET_ERROR);

View File

@@ -19,6 +19,7 @@
#include <cstdio>
#include <string>
#include <unordered_map>
#include <vector>
#include "rclcpp/parameter_map.hpp"
@@ -353,3 +354,132 @@ TEST(Test_parameter_map_from, string_array_param_value)
c_params->params[0].parameter_values[0].string_array_value = NULL;
rcl_yaml_node_struct_fini(c_params);
}
TEST(Test_parameter_map_from, one_node_one_param_by_node_fqn)
{
rcl_params_t * c_params = make_params({"foo"});
make_node_params(c_params, 0, {"string_param"});
std::string hello_world = "hello world";
char * c_hello_world = new char[hello_world.length() + 1];
std::snprintf(c_hello_world, hello_world.size() + 1, "%s", hello_world.c_str());
c_params->params[0].parameter_values[0].string_value = c_hello_world;
rclcpp::ParameterMap map = rclcpp::parameter_map_from(c_params, "/foo");
const std::vector<rclcpp::Parameter> & params = map.at("/foo");
EXPECT_STREQ("string_param", params.at(0).get_name().c_str());
EXPECT_STREQ(hello_world.c_str(), params.at(0).get_value<std::string>().c_str());
c_params->params[0].parameter_values[0].string_value = NULL;
delete[] c_hello_world;
rcl_yaml_node_struct_fini(c_params);
}
TEST(Test_parameter_map_from, multi_nodes_same_param_name_by_node_fqn)
{
std::vector<std::string> node_names_keys = {
"/**", // index: 0
"/*", // index: 1
"/**/node", // index: 2
"/*/node", // index: 3
"/ns/node" // index: 4
};
rcl_params_t * c_params = make_params(node_names_keys);
std::vector<char *> param_values;
for (size_t i = 0; i < node_names_keys.size(); ++i) {
make_node_params(c_params, i, {"string_param"});
std::string hello_world = "hello world" + std::to_string(i);
char * c_hello_world = new char[hello_world.length() + 1];
std::snprintf(c_hello_world, hello_world.size() + 1, "%s", hello_world.c_str());
c_params->params[i].parameter_values[0].string_value = c_hello_world;
param_values.push_back(c_hello_world);
}
std::unordered_map<std::string, std::vector<size_t>> node_fqn_expected = {
{"/ns/foo/another_node", {0}},
{"/another", {0, 1}},
{"/node", {0, 1, 2}},
{"/another_ns/node", {0, 2, 3}},
{"/ns/node", {0, 2, 3, 4}},
};
for (auto & kv : node_fqn_expected) {
rclcpp::ParameterMap map = rclcpp::parameter_map_from(c_params, kv.first.c_str());
const std::vector<rclcpp::Parameter> & params = map.at(kv.first);
EXPECT_EQ(kv.second.size(), params.size());
for (size_t i = 0; i < params.size(); ++i) {
std::string param_value = "hello world" + std::to_string(kv.second[i]);
EXPECT_STREQ("string_param", params.at(i).get_name().c_str());
EXPECT_STREQ(param_value.c_str(), params.at(i).get_value<std::string>().c_str());
}
}
for (size_t i = 0; i < node_names_keys.size(); ++i) {
c_params->params[i].parameter_values[0].string_value = NULL;
}
for (auto c_hello_world : param_values) {
delete[] c_hello_world;
}
rcl_yaml_node_struct_fini(c_params);
}
TEST(Test_parameter_map_from, multi_nodes_diff_param_name_by_node_fqn)
{
std::vector<std::string> node_names_keys = {
"/**", // index: 0
"/*", // index: 1
"/**/node", // index: 2
"/*/node", // index: 3
"/ns/**", // index: 4
"/ns/*", // index: 5
"/ns/**/node", // index: 6
"/ns/*/node", // index: 7
"/ns/**/a/*/node", // index: 8
"/ns/node" // index: 9
};
rcl_params_t * c_params = make_params(node_names_keys);
for (size_t i = 0; i < node_names_keys.size(); ++i) {
std::string param_name = "string_param" + std::to_string(i);
make_node_params(c_params, i, {param_name});
}
std::string hello_world = "hello world";
char * c_hello_world = new char[hello_world.length() + 1];
std::snprintf(c_hello_world, hello_world.size() + 1, "%s", hello_world.c_str());
for (size_t i = 0; i < node_names_keys.size(); ++i) {
c_params->params[i].parameter_values[0].string_value = c_hello_world;
}
std::unordered_map<std::string, std::vector<size_t>> node_fqn_expected = {
{"/ns/node", {0, 2, 3, 4, 5, 6, 9}},
{"/node", {0, 1, 2}},
{"/ns/foo/node", {0, 2, 4, 6, 7}},
{"/ns/foo/a/node", {0, 2, 4, 6}},
{"/ns/foo/a/bar/node", {0, 2, 4, 6, 8}},
{"/ns/a/bar/node", {0, 2, 4, 6, 8}},
{"/ns/foo/zoo/a/bar/node", {0, 2, 4, 6, 8}},
};
for (auto & kv : node_fqn_expected) {
rclcpp::ParameterMap map = rclcpp::parameter_map_from(c_params, kv.first.c_str());
const std::vector<rclcpp::Parameter> & params = map.at(kv.first);
EXPECT_EQ(kv.second.size(), params.size());
for (size_t i = 0; i < params.size(); ++i) {
std::string param_name = "string_param" + std::to_string(kv.second[i]);
EXPECT_STREQ(param_name.c_str(), params.at(i).get_name().c_str());
EXPECT_STREQ(hello_world.c_str(), params.at(i).get_value<std::string>().c_str());
}
}
for (size_t i = 0; i < node_names_keys.size(); ++i) {
c_params->params[i].parameter_values[0].string_value = NULL;
}
delete[] c_hello_world;
rcl_yaml_node_struct_fini(c_params);
}

View File

@@ -0,0 +1,5 @@
/**/foo/*/bar:
node2:
ros__parameters:
foo: "foo"
bar: "bar"

View File

@@ -0,0 +1,16 @@
/**:
node2:
ros__parameters:
a_value: "first"
foo: "foo"
/ns:
node2:
ros__parameters:
a_value: "second"
bar: "bar"
/*:
node2:
ros__parameters:
a_value: "last_one_win"

View File

@@ -0,0 +1,57 @@
/**:
ros__parameters:
full_wild: "full_wild"
/**:
node2:
ros__parameters:
namespace_wild: "namespace_wild"
/**/node2:
ros__parameters:
namespace_wild_another: "namespace_wild_another"
/*:
node2:
ros__parameters:
namespace_wild_one_star: "namespace_wild_one_star"
ns:
"*":
ros__parameters:
node_wild_in_ns: "node_wild_in_ns"
/ns/*:
ros__parameters:
node_wild_in_ns_another: "node_wild_in_ns_another"
ns:
node2:
ros__parameters:
explicit_in_ns: "explicit_in_ns"
"*":
ros__parameters:
node_wild_no_ns: "node_wild_no_ns"
node2:
ros__parameters:
explicit_no_ns: "explicit_no_ns"
ns:
nodeX:
ros__parameters:
should_not_appear: "incorrect_node_name"
/**/nodeX:
ros__parameters:
should_not_appear: "incorrect_node_name"
nsX:
node2:
ros__parameters:
should_not_appear: "incorrect_namespace"
/nsX/*:
ros__parameters:
should_not_appear: "incorrect_namespace"

View File

@@ -3,6 +3,19 @@ Changelog for package rclcpp_action
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
16.0.4 (2023-04-25)
-------------------
* Revert "Revert "extract the result response before the callback is issued. (`#2133 <https://github.com/ros2/rclcpp/issues/2133>`_)" (`#2148 <https://github.com/ros2/rclcpp/issues/2148>`_)" (`#2152 <https://github.com/ros2/rclcpp/issues/2152>`_)
* Revert "extract the result response before the callback is issued. (`#2133 <https://github.com/ros2/rclcpp/issues/2133>`_)" (`#2148 <https://github.com/ros2/rclcpp/issues/2148>`_)
* extract the result response before the callback is issued. (`#2133 <https://github.com/ros2/rclcpp/issues/2133>`_)
* Contributors: Tomoya Fujita
16.0.3 (2023-01-10)
-------------------
16.0.2 (2022-11-07)
-------------------
16.0.1 (2022-04-13)
-------------------

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>16.0.1</version>
<version>16.0.4</version>
<description>Adds action APIs for C++.</description>
<maintainer email="ivanpauno@ekumenlabs.com">Ivan Paunovic</maintainer>
<maintainer email="jacob@openrobotics.org">Jacob Perron</maintainer>

View File

@@ -319,14 +319,18 @@ ClientBase::handle_result_response(
const rmw_request_id_t & response_header,
std::shared_ptr<void> response)
{
std::lock_guard<std::mutex> guard(pimpl_->result_requests_mutex);
const int64_t & sequence_number = response_header.sequence_number;
if (pimpl_->pending_result_responses.count(sequence_number) == 0) {
RCLCPP_ERROR(pimpl_->logger, "unknown result response, ignoring...");
return;
ResponseCallback response_callback;
{
std::lock_guard<std::mutex> guard(pimpl_->result_requests_mutex);
const int64_t & sequence_number = response_header.sequence_number;
if (pimpl_->pending_result_responses.count(sequence_number) == 0) {
RCLCPP_ERROR(pimpl_->logger, "unknown result response, ignoring...");
return;
}
response_callback = std::move(pimpl_->pending_result_responses[sequence_number]);
pimpl_->pending_result_responses.erase(sequence_number);
}
pimpl_->pending_result_responses[sequence_number](response);
pimpl_->pending_result_responses.erase(sequence_number);
response_callback(response);
}
void

View File

@@ -2,6 +2,15 @@
Changelog for package rclcpp_components
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
16.0.4 (2023-04-25)
-------------------
16.0.3 (2023-01-10)
-------------------
16.0.2 (2022-11-07)
-------------------
16.0.1 (2022-04-13)
-------------------

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>16.0.1</version>
<version>16.0.4</version>
<description>Package containing tools for dynamically loadable components</description>
<maintainer email="ivanpauno@ekumenlabs.com">Ivan Paunovic</maintainer>
<maintainer email="jacob@openrobotics.org">Jacob Perron</maintainer>

View File

@@ -3,6 +3,15 @@ Changelog for package rclcpp_lifecycle
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
16.0.4 (2023-04-25)
-------------------
16.0.3 (2023-01-10)
-------------------
16.0.2 (2022-11-07)
-------------------
16.0.1 (2022-04-13)
-------------------

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>16.0.1</version>
<version>16.0.4</version>
<description>Package containing a prototype for lifecycle implementation</description>
<maintainer email="ivanpauno@ekumenlabs.com">Ivan Paunovic</maintainer>
<maintainer email="jacob@openrobotics.org">Jacob Perron</maintainer>