From cf2a27805e7a5daf79b890ad9ece0f95c9ce172c Mon Sep 17 00:00:00 2001 From: "mergify[bot]" <37929162+mergify[bot]@users.noreply.github.com> Date: Thu, 9 Jun 2022 08:54:15 -0700 Subject: [PATCH 01/98] Add statistics for handle_loaned_message (#1927) (#1932) * Add statistics for handle_loaned_message Signed-off-by: Barry Xu (cherry picked from commit 5c688303b3cb994969f448979f64c12971243295) Co-authored-by: Barry Xu --- rclcpp/include/rclcpp/subscription.hpp | 20 ++++++++++++++++++++ 1 file changed, 20 insertions(+) diff --git a/rclcpp/include/rclcpp/subscription.hpp b/rclcpp/include/rclcpp/subscription.hpp index 69b6031405..11bf9c6e43 100644 --- a/rclcpp/include/rclcpp/subscription.hpp +++ b/rclcpp/include/rclcpp/subscription.hpp @@ -363,11 +363,31 @@ class Subscription : public SubscriptionBase 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(loaned_message); // message is loaned, so we have to make sure that the deleter does not deallocate the message auto sptr = std::shared_ptr( typed_message, [](ROSMessageType * msg) {(void) msg;}); + + std::chrono::time_point 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(now); + const auto time = rclcpp::Time(nanos.time_since_epoch().count()); + subscription_topic_statistics_->handle_message(*typed_message, time); + } } /// Return the borrowed message. From 166007dde37b013c37e8f1af13fc10f217d5e5a1 Mon Sep 17 00:00:00 2001 From: "mergify[bot]" <37929162+mergify[bot]@users.noreply.github.com> Date: Wed, 15 Jun 2022 15:02:18 -0300 Subject: [PATCH 02/98] Drop wrong template specialization (#1926) (#1937) This fails with g++ -std=gnu++20. Signed-off-by: Jochen Sprickerhof (cherry picked from commit 02802bcc385c3d6d814add93618f3388d44adec7) Co-authored-by: Jochen Sprickerhof --- rclcpp/include/rclcpp/publisher_options.hpp | 2 +- rclcpp/include/rclcpp/subscription_options.hpp | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/rclcpp/include/rclcpp/publisher_options.hpp b/rclcpp/include/rclcpp/publisher_options.hpp index 48250307e9..3c88ebccd1 100644 --- a/rclcpp/include/rclcpp/publisher_options.hpp +++ b/rclcpp/include/rclcpp/publisher_options.hpp @@ -72,7 +72,7 @@ struct PublisherOptionsWithAllocator : public PublisherOptionsBase /// Optional custom allocator. std::shared_ptr allocator = nullptr; - PublisherOptionsWithAllocator() {} + PublisherOptionsWithAllocator() {} /// Constructor using base class as input. explicit PublisherOptionsWithAllocator(const PublisherOptionsBase & publisher_options_base) diff --git a/rclcpp/include/rclcpp/subscription_options.hpp b/rclcpp/include/rclcpp/subscription_options.hpp index b6914ce4b2..2b819da399 100644 --- a/rclcpp/include/rclcpp/subscription_options.hpp +++ b/rclcpp/include/rclcpp/subscription_options.hpp @@ -97,7 +97,7 @@ struct SubscriptionOptionsWithAllocator : public SubscriptionOptionsBase /// Optional custom allocator. std::shared_ptr allocator = nullptr; - SubscriptionOptionsWithAllocator() {} + SubscriptionOptionsWithAllocator() {} /// Constructor using base class as input. explicit SubscriptionOptionsWithAllocator( From 7f575103d8fe06285bd46474a0324c7dfbb2a2f0 Mon Sep 17 00:00:00 2001 From: "mergify[bot]" <37929162+mergify[bot]@users.noreply.github.com> Date: Fri, 9 Sep 2022 11:55:00 -0700 Subject: [PATCH 03/98] use regex for wildcard matching (backport #1839) (#1986) * use regex for wildcard matching (#1839) * use regex for wildcard matching Co-authored-by: Aaron Lipinski Signed-off-by: Chen Lihui * use map to process the content of parameter file by order Signed-off-by: Chen Lihui * add more test cases Signed-off-by: Chen Lihui * try to not decrease the performance and make the param win last Signed-off-by: Chen Lihui * update node name Signed-off-by: Chen Lihui * update document comment Signed-off-by: Chen Lihui * add more test for parameter_map_from Signed-off-by: Chen Lihui Co-authored-by: Aaron Lipinski (cherry picked from commit 6dd3a0377bbacd07fa6ed3c9e70c6de70931b45f) * not to break ABI Signed-off-by: Chen Lihui Signed-off-by: Chen Lihui Co-authored-by: Chen Lihui --- rclcpp/include/rclcpp/parameter_map.hpp | 10 ++ .../detail/resolve_parameter_overrides.cpp | 17 +-- rclcpp/src/rclcpp/parameter_map.cpp | 20 +++ .../node_interfaces/test_node_parameters.cpp | 132 ++++++++++++++++++ rclcpp/test/rclcpp/test_parameter_map.cpp | 130 +++++++++++++++++ .../complicated_wildcards.yaml | 5 + .../test_node_parameters/params_by_order.yaml | 16 +++ .../test_node_parameters/wildcards.yaml | 57 ++++++++ 8 files changed, 376 insertions(+), 11 deletions(-) create mode 100644 rclcpp/test/resources/test_node_parameters/complicated_wildcards.yaml create mode 100644 rclcpp/test/resources/test_node_parameters/params_by_order.yaml create mode 100644 rclcpp/test/resources/test_node_parameters/wildcards.yaml diff --git a/rclcpp/include/rclcpp/parameter_map.hpp b/rclcpp/include/rclcpp/parameter_map.hpp index 9cfcdaaacf..5bebe27119 100644 --- a/rclcpp/include/rclcpp/parameter_map.hpp +++ b/rclcpp/include/rclcpp/parameter_map.hpp @@ -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 diff --git a/rclcpp/src/rclcpp/detail/resolve_parameter_overrides.cpp b/rclcpp/src/rclcpp/detail/resolve_parameter_overrides.cpp index a62121b37f..3959e64882 100644 --- a/rclcpp/src/rclcpp/detail/resolve_parameter_overrides.cpp +++ b/rclcpp/src/rclcpp/detail/resolve_parameter_overrides.cpp @@ -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 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()); } } } diff --git a/rclcpp/src/rclcpp/parameter_map.cpp b/rclcpp/src/rclcpp/parameter_map.cpp index e5e3da019c..6365f55478 100644 --- a/rclcpp/src/rclcpp/parameter_map.cpp +++ b/rclcpp/src/rclcpp/parameter_map.cpp @@ -13,8 +13,10 @@ // limitations under the License. #include +#include #include +#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 & 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; } diff --git a/rclcpp/test/rclcpp/node_interfaces/test_node_parameters.cpp b/rclcpp/test/rclcpp/node_interfaces/test_node_parameters.cpp index 31b755b4a7..97e3a3188d 100644 --- a/rclcpp/test/rclcpp/node_interfaces/test_node_parameters.cpp +++ b/rclcpp/test/rclcpp/node_interfaces/test_node_parameters.cpp @@ -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 @@ class TestNodeParameters : public ::testing::Test dynamic_cast( node->get_node_parameters_interface().get()); ASSERT_NE(nullptr, node_parameters); + test_resources_path /= "test_node_parameters"; } void TearDown() @@ -57,6 +60,8 @@ class TestNodeParameters : public ::testing::Test protected: std::shared_ptr 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 node = std::make_shared("node2", "ns", opts); + + auto * node_parameters = + dynamic_cast( + 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(), "full_wild"); + EXPECT_EQ(parameter_overrides.at("namespace_wild").get(), "namespace_wild"); + EXPECT_EQ( + parameter_overrides.at("namespace_wild_another").get(), + "namespace_wild_another"); + EXPECT_EQ( + parameter_overrides.at("namespace_wild_one_star").get(), + "namespace_wild_one_star"); + EXPECT_EQ(parameter_overrides.at("node_wild_in_ns").get(), "node_wild_in_ns"); + EXPECT_EQ( + parameter_overrides.at("node_wild_in_ns_another").get(), + "node_wild_in_ns_another"); + EXPECT_EQ(parameter_overrides.at("explicit_in_ns").get(), "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 node = std::make_shared("node2", opts); + + auto * node_parameters = + dynamic_cast( + 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(), "full_wild"); + EXPECT_EQ(parameter_overrides.at("namespace_wild").get(), "namespace_wild"); + EXPECT_EQ( + parameter_overrides.at("namespace_wild_another").get(), + "namespace_wild_another"); + EXPECT_EQ(parameter_overrides.at("node_wild_no_ns").get(), "node_wild_no_ns"); + EXPECT_EQ(parameter_overrides.at("explicit_no_ns").get(), "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 node = std::make_shared("node2", "ns", opts); + + auto * node_parameters = + dynamic_cast( + 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(), "last_one_win"); + EXPECT_EQ(parameter_overrides.at("foo").get(), "foo"); + EXPECT_EQ(parameter_overrides.at("bar").get(), "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 node = + std::make_shared("node2", "/a/b/c/foo/d/bar", opts); + + auto * node_parameters = + dynamic_cast( + 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(), "foo"); + EXPECT_EQ(parameter_overrides.at("bar").get(), "bar"); + } + + { + // regex not matched: /**/foo/*/bar + std::shared_ptr node = + std::make_shared("node2", "/a/b/c/foo/bar", opts); + + auto * node_parameters = + dynamic_cast( + 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()); + } +} diff --git a/rclcpp/test/rclcpp/test_parameter_map.cpp b/rclcpp/test/rclcpp/test_parameter_map.cpp index 3f54e4c879..0158b7c7e7 100644 --- a/rclcpp/test/rclcpp/test_parameter_map.cpp +++ b/rclcpp/test/rclcpp/test_parameter_map.cpp @@ -19,6 +19,7 @@ #include #include +#include #include #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 & 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().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 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 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> 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 & 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().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 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> 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 & 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().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); +} diff --git a/rclcpp/test/resources/test_node_parameters/complicated_wildcards.yaml b/rclcpp/test/resources/test_node_parameters/complicated_wildcards.yaml new file mode 100644 index 0000000000..53da409135 --- /dev/null +++ b/rclcpp/test/resources/test_node_parameters/complicated_wildcards.yaml @@ -0,0 +1,5 @@ +/**/foo/*/bar: + node2: + ros__parameters: + foo: "foo" + bar: "bar" diff --git a/rclcpp/test/resources/test_node_parameters/params_by_order.yaml b/rclcpp/test/resources/test_node_parameters/params_by_order.yaml new file mode 100644 index 0000000000..680d96beaf --- /dev/null +++ b/rclcpp/test/resources/test_node_parameters/params_by_order.yaml @@ -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" diff --git a/rclcpp/test/resources/test_node_parameters/wildcards.yaml b/rclcpp/test/resources/test_node_parameters/wildcards.yaml new file mode 100644 index 0000000000..b89b0d8cd0 --- /dev/null +++ b/rclcpp/test/resources/test_node_parameters/wildcards.yaml @@ -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" From 4fa3489cfd075affb34812878b034ef8a462e379 Mon Sep 17 00:00:00 2001 From: "mergify[bot]" <37929162+mergify[bot]@users.noreply.github.com> Date: Mon, 17 Oct 2022 10:50:44 -0700 Subject: [PATCH 04/98] fix mismatched issue if using zero_allocate (#1995) (#2026) * fix mismatched issue if uzing zero_allocated Signed-off-by: Chen Lihui (cherry picked from commit 978439191fdb3924af900a506dd35b68f8da725c) Co-authored-by: Chen Lihui --- .../rclcpp/allocator/allocator_common.hpp | 18 +++++++ .../allocator/test_allocator_common.cpp | 47 +++++++++++++++++++ 2 files changed, 65 insertions(+) diff --git a/rclcpp/include/rclcpp/allocator/allocator_common.hpp b/rclcpp/include/rclcpp/allocator/allocator_common.hpp index d117376517..12b2f383b6 100644 --- a/rclcpp/include/rclcpp/allocator/allocator_common.hpp +++ b/rclcpp/include/rclcpp/allocator/allocator_common.hpp @@ -15,6 +15,7 @@ #ifndef RCLCPP__ALLOCATOR__ALLOCATOR_COMMON_HPP_ #define RCLCPP__ALLOCATOR__ALLOCATOR_COMMON_HPP_ +#include #include #include "rcl/allocator.h" @@ -39,6 +40,22 @@ void * retyped_allocate(size_t size, void * untyped_allocator) return std::allocator_traits::allocate(*typed_allocator, size); } +template +void * retyped_zero_allocate(size_t number_of_elem, size_t size_of_elem, void * untyped_allocator) +{ + auto typed_allocator = static_cast(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::allocate(*typed_allocator, size); + if (allocated_memory) { + std::memset(allocated_memory, 0, size); + } + return allocated_memory; +} + template 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; + rcl_allocator.zero_allocate = &retyped_zero_allocate; rcl_allocator.deallocate = &retyped_deallocate; rcl_allocator.reallocate = &retyped_reallocate; rcl_allocator.state = &allocator; diff --git a/rclcpp/test/rclcpp/allocator/test_allocator_common.cpp b/rclcpp/test/rclcpp/allocator/test_allocator_common.cpp index 341846a3eb..4619b7665d 100644 --- a/rclcpp/test/rclcpp/allocator/test_allocator_common.cpp +++ b/rclcpp/test/rclcpp/allocator/test_allocator_common.cpp @@ -51,6 +51,53 @@ TEST(TestAllocatorCommon, retyped_allocate) { EXPECT_NO_THROW(code2()); } +TEST(TestAllocatorCommon, retyped_zero_allocate_basic) { + std::allocator allocator; + void * untyped_allocator = &allocator; + void * allocated_mem = + rclcpp::allocator::retyped_zero_allocate>(20u, 1u, untyped_allocator); + ASSERT_TRUE(nullptr != allocated_mem); + + auto code = [&untyped_allocator, allocated_mem]() { + rclcpp::allocator::retyped_deallocate>( + allocated_mem, untyped_allocator); + }; + EXPECT_NO_THROW(code()); +} + +TEST(TestAllocatorCommon, retyped_zero_allocate) { + std::allocator allocator; + void * untyped_allocator = &allocator; + void * allocated_mem = + rclcpp::allocator::retyped_zero_allocate>(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>( + 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>( + 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>( + reallocated_mem, untyped_allocator); + }; + EXPECT_NO_THROW(code2()); +} + TEST(TestAllocatorCommon, get_rcl_allocator) { std::allocator allocator; auto rcl_allocator = rclcpp::allocator::get_rcl_allocator(allocator); From ae8b033ae01601475104bf9e4c2350b98c9560c4 Mon Sep 17 00:00:00 2001 From: Audrow Nash Date: Mon, 7 Nov 2022 09:12:14 -0600 Subject: [PATCH 05/98] 16.0.2 Signed-off-by: Audrow Nash --- rclcpp/CHANGELOG.rst | 8 ++++++++ rclcpp/package.xml | 2 +- rclcpp_action/CHANGELOG.rst | 3 +++ rclcpp_action/package.xml | 2 +- rclcpp_components/CHANGELOG.rst | 3 +++ rclcpp_components/package.xml | 2 +- rclcpp_lifecycle/CHANGELOG.rst | 3 +++ rclcpp_lifecycle/package.xml | 2 +- 8 files changed, 21 insertions(+), 4 deletions(-) diff --git a/rclcpp/CHANGELOG.rst b/rclcpp/CHANGELOG.rst index dd4eadc225..d6f3da6eec 100644 --- a/rclcpp/CHANGELOG.rst +++ b/rclcpp/CHANGELOG.rst @@ -2,6 +2,14 @@ Changelog for package rclcpp ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +16.0.2 (2022-11-07) +------------------- +* fix mismatched issue if using zero_allocate (`#1995 `_) (`#2026 `_) +* use regex for wildcard matching (backport `#1839 `_) (`#1986 `_) +* Drop wrong template specialization (`#1926 `_) (`#1937 `_) +* Add statistics for handle_loaned_message (`#1927 `_) (`#1932 `_) +* Contributors: mergify[bot] + 16.0.1 (2022-04-13) ------------------- * remove DEFINE_CONTENT_FILTER cmake option (`#1914 `_) diff --git a/rclcpp/package.xml b/rclcpp/package.xml index 9a9e358be0..9390433faa 100644 --- a/rclcpp/package.xml +++ b/rclcpp/package.xml @@ -2,7 +2,7 @@ rclcpp - 16.0.1 + 16.0.2 The ROS client library in C++. Ivan Paunovic Jacob Perron diff --git a/rclcpp_action/CHANGELOG.rst b/rclcpp_action/CHANGELOG.rst index 0e3bdc155e..43ab885467 100644 --- a/rclcpp_action/CHANGELOG.rst +++ b/rclcpp_action/CHANGELOG.rst @@ -3,6 +3,9 @@ Changelog for package rclcpp_action ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +16.0.2 (2022-11-07) +------------------- + 16.0.1 (2022-04-13) ------------------- diff --git a/rclcpp_action/package.xml b/rclcpp_action/package.xml index 76e9cfcddf..a1774eba80 100644 --- a/rclcpp_action/package.xml +++ b/rclcpp_action/package.xml @@ -2,7 +2,7 @@ rclcpp_action - 16.0.1 + 16.0.2 Adds action APIs for C++. Ivan Paunovic Jacob Perron diff --git a/rclcpp_components/CHANGELOG.rst b/rclcpp_components/CHANGELOG.rst index b35089782f..512a0cf9aa 100644 --- a/rclcpp_components/CHANGELOG.rst +++ b/rclcpp_components/CHANGELOG.rst @@ -2,6 +2,9 @@ Changelog for package rclcpp_components ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +16.0.2 (2022-11-07) +------------------- + 16.0.1 (2022-04-13) ------------------- diff --git a/rclcpp_components/package.xml b/rclcpp_components/package.xml index 834e9a7527..c2f6c0a226 100644 --- a/rclcpp_components/package.xml +++ b/rclcpp_components/package.xml @@ -2,7 +2,7 @@ rclcpp_components - 16.0.1 + 16.0.2 Package containing tools for dynamically loadable components Ivan Paunovic Jacob Perron diff --git a/rclcpp_lifecycle/CHANGELOG.rst b/rclcpp_lifecycle/CHANGELOG.rst index c8052cd9c8..3d12db8ba1 100644 --- a/rclcpp_lifecycle/CHANGELOG.rst +++ b/rclcpp_lifecycle/CHANGELOG.rst @@ -3,6 +3,9 @@ Changelog for package rclcpp_lifecycle ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +16.0.2 (2022-11-07) +------------------- + 16.0.1 (2022-04-13) ------------------- diff --git a/rclcpp_lifecycle/package.xml b/rclcpp_lifecycle/package.xml index 44bd945e3c..39f382beba 100644 --- a/rclcpp_lifecycle/package.xml +++ b/rclcpp_lifecycle/package.xml @@ -2,7 +2,7 @@ rclcpp_lifecycle - 16.0.1 + 16.0.2 Package containing a prototype for lifecycle implementation Ivan Paunovic Jacob Perron From 33cbd76c07dc9a30e8dbeecd5f5e70057122a369 Mon Sep 17 00:00:00 2001 From: "mergify[bot]" <37929162+mergify[bot]@users.noreply.github.com> Date: Wed, 9 Nov 2022 22:16:14 -0800 Subject: [PATCH 06/98] 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 * fix flaky and not to use spin_some Signed-off-by: Chen Lihui * update comment Signed-off-by: Chen Lihui * update for not using anti-pattern source code Signed-off-by: Chen Lihui * add a notify guard condition for callback group Co-authored-by: William Woodall Signed-off-by: Chen Lihui * Notify guard condition of Node not to be used in Executor it is only for the waitset of GraphListener Signed-off-by: Chen Lihui * put code in the try catch Signed-off-by: Chen Lihui * defer to create guard condition Signed-off-by: Chen Lihui * use context directly for the create function Signed-off-by: Chen Lihui * cpplint Signed-off-by: Chen Lihui * fix that some case might call add_node after shutdown Signed-off-by: Chen Lihui * nitpick and fix some potential bug Signed-off-by: Chen Lihui * add sanity check as some case might not create notify guard condition after shutdown Signed-off-by: Chen Lihui * Cleanup includes. Signed-off-by: Chris Lalancette * remove destroy method make a callback group can only create one guard condition Signed-off-by: Chen Lihui * remove limitation that guard condition can not be re-created in callback group Signed-off-by: Chen Lihui Signed-off-by: Chen Lihui Signed-off-by: Chris Lalancette Co-authored-by: William Woodall Co-authored-by: Chris Lalancette (cherry picked from commit d119157948720f5888d47898e1c59b56fe1f86c5) Co-authored-by: Chen Lihui --- rclcpp/include/rclcpp/callback_group.hpp | 22 ++++++- rclcpp/include/rclcpp/executor.hpp | 12 ++-- rclcpp/src/rclcpp/callback_group.cpp | 45 ++++++++++++- rclcpp/src/rclcpp/executor.cpp | 58 +++++++++-------- .../rclcpp/node_interfaces/node_services.cpp | 12 ++-- .../rclcpp/node_interfaces/node_timers.cpp | 5 +- .../rclcpp/node_interfaces/node_topics.cpp | 2 + .../rclcpp/node_interfaces/node_waitables.cpp | 6 +- rclcpp/test/rclcpp/CMakeLists.txt | 21 +++--- .../test_add_callback_groups_to_executor.cpp | 64 +++++++++++++++++++ 10 files changed, 196 insertions(+), 51 deletions(-) diff --git a/rclcpp/include/rclcpp/callback_group.hpp b/rclcpp/include/rclcpp/callback_group.hpp index 94bceced81..7d03edf343 100644 --- a/rclcpp/include/rclcpp/callback_group.hpp +++ b/rclcpp/include/rclcpp/callback_group.hpp @@ -16,11 +16,14 @@ #define RCLCPP__CALLBACK_GROUP_HPP_ #include +#include +#include #include -#include #include #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 @@ class CallbackGroup CallbackGroupType group_type, bool automatically_add_to_executor_with_node = true); + /// Default destructor. + RCLCPP_PUBLIC + ~CallbackGroup(); + template rclcpp::SubscriptionBase::SharedPtr find_subscription_ptrs_if(Function func) const @@ -171,6 +178,16 @@ class CallbackGroup 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 @@ class CallbackGroup std::vector 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 notify_guard_condition_ = nullptr; + std::recursive_mutex notify_guard_condition_mutex_; private: template diff --git a/rclcpp/include/rclcpp/executor.hpp b/rclcpp/include/rclcpp/executor.hpp index ed2ddc4a0a..65d0a930cb 100644 --- a/rclcpp/include/rclcpp/executor.hpp +++ b/rclcpp/include/rclcpp/executor.hpp @@ -560,14 +560,14 @@ class Executor virtual void spin_once_impl(std::chrono::nanoseconds timeout); - typedef std::map> - WeakNodesToGuardConditionsMap; + std::owner_less> + 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 diff --git a/rclcpp/src/rclcpp/callback_group.cpp b/rclcpp/src/rclcpp/callback_group.cpp index 4b11156cf9..734c781a69 100644 --- a/rclcpp/src/rclcpp/callback_group.cpp +++ b/rclcpp/src/rclcpp/callback_group.cpp @@ -12,9 +12,19 @@ // See the License for the specific language governing permissions and // limitations under the License. -#include "rclcpp/callback_group.hpp" +#include +#include +#include +#include +#include +#include -#include +#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 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(context_ptr); + } + + return notify_guard_condition_; +} + +void +CallbackGroup::trigger_notify_guard_condition() +{ + std::lock_guard lock(notify_guard_condition_mutex_); + if (notify_guard_condition_) { + notify_guard_condition_->trigger(); + } +} + void CallbackGroup::add_subscription( const rclcpp::SubscriptionBase::SharedPtr subscription_ptr) diff --git a/rclcpp/src/rclcpp/executor.cpp b/rclcpp/src/rclcpp/executor.cpp index 73b7b8d80d..401beb0a73 100644 --- a/rclcpp/src/rclcpp/executor.cpp +++ b/rclcpp/src/rclcpp/executor.cpp @@ -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); }); } diff --git a/rclcpp/src/rclcpp/node_interfaces/node_services.cpp b/rclcpp/src/rclcpp/node_interfaces/node_services.cpp index 14ab1c82c4..2f1afd3224 100644 --- a/rclcpp/src/rclcpp/node_interfaces/node_services.cpp +++ b/rclcpp/src/rclcpp/node_interfaces/node_services.cpp @@ -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()); diff --git a/rclcpp/src/rclcpp/node_interfaces/node_timers.cpp b/rclcpp/src/rclcpp/node_interfaces/node_timers.cpp index b463e8a0e7..d2e821a9e6 100644 --- a/rclcpp/src/rclcpp/node_interfaces/node_timers.cpp +++ b/rclcpp/src/rclcpp/node_interfaces/node_timers.cpp @@ -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()); diff --git a/rclcpp/src/rclcpp/node_interfaces/node_topics.cpp b/rclcpp/src/rclcpp/node_interfaces/node_topics.cpp index 159409528d..167a35f35d 100644 --- a/rclcpp/src/rclcpp/node_interfaces/node_topics.cpp +++ b/rclcpp/src/rclcpp/node_interfaces/node_topics.cpp @@ -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()); diff --git a/rclcpp/src/rclcpp/node_interfaces/node_waitables.cpp b/rclcpp/src/rclcpp/node_interfaces/node_waitables.cpp index 6f243f6025..1d1fe2ce59 100644 --- a/rclcpp/src/rclcpp/node_interfaces/node_waitables.cpp +++ b/rclcpp/src/rclcpp/node_interfaces/node_waitables.cpp @@ -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()); diff --git a/rclcpp/test/rclcpp/CMakeLists.txt b/rclcpp/test/rclcpp/CMakeLists.txt index 6f915feef5..0bdb0d931e 100644 --- a/rclcpp/test/rclcpp/CMakeLists.txt +++ b/rclcpp/test/rclcpp/CMakeLists.txt @@ -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 diff --git a/rclcpp/test/rclcpp/test_add_callback_groups_to_executor.cpp b/rclcpp/test/rclcpp/test_add_callback_groups_to_executor.cpp index fa636b7157..07ca1e87d8 100644 --- a/rclcpp/test/rclcpp/test_add_callback_groups_to_executor.cpp +++ b/rclcpp/test/rclcpp/test_add_callback_groups_to_executor.cpp @@ -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("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 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::SharedPtr subscription; + rclcpp::Publisher::SharedPtr publisher; + // to create a timer with a callback run on another executor + rclcpp::TimerBase::SharedPtr timer = nullptr; + std::promise 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("topic_name", qos, sub_callback, options); + // create a publisher to send data + publisher = + node->create_publisher("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. */ From f9050cd6663ccde9c4b6cabc8c7fa3ad154879ce Mon Sep 17 00:00:00 2001 From: "mergify[bot]" <37929162+mergify[bot]@users.noreply.github.com> Date: Tue, 13 Dec 2022 15:40:16 -0300 Subject: [PATCH 07/98] fix nullptr dereference in prune_requests_older_than (#2008) (#2065) * fix nullptr dereference in prune_requests_older_than Signed-off-by: akela1101 * add tests for prune_requests_older_than Signed-off-by: akela1101 * Update rclcpp/test/rclcpp/test_client.cpp Co-authored-by: Chen Lihui Signed-off-by: akela1101 Signed-off-by: akela1101 Co-authored-by: Chen Lihui (cherry picked from commit 1ac37b692c4cce54f0ffeaad1f4fe3d5688322bd) Co-authored-by: andrei --- rclcpp/include/rclcpp/client.hpp | 4 +++- rclcpp/test/rclcpp/test_client.cpp | 21 +++++++++++++++++++++ 2 files changed, 24 insertions(+), 1 deletion(-) diff --git a/rclcpp/include/rclcpp/client.hpp b/rclcpp/include/rclcpp/client.hpp index e88fa8a949..adc9094f61 100644 --- a/rclcpp/include/rclcpp/client.hpp +++ b/rclcpp/include/rclcpp/client.hpp @@ -769,7 +769,9 @@ class Client : public ClientBase 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; diff --git a/rclcpp/test/rclcpp/test_client.cpp b/rclcpp/test/rclcpp/test_client.cpp index 7cb9b0af65..7cfb7c3213 100644 --- a/rclcpp/test/rclcpp/test_client.cpp +++ b/rclcpp/test/rclcpp/test_client.cpp @@ -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(service_name); + auto request = std::make_shared(); + 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(service_name); + auto request = std::make_shared(); + auto future = client->async_send_request(request); + auto time = std::chrono::system_clock::now() + 1s; + + std::vector 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); From df08474d385874a84fb709f65030507caa14da04 Mon Sep 17 00:00:00 2001 From: "mergify[bot]" <37929162+mergify[bot]@users.noreply.github.com> Date: Thu, 22 Dec 2022 14:14:36 -0800 Subject: [PATCH 08/98] do not throw exception if trying to dequeue an empty intra-process buffer (#2061) (#2070) Signed-off-by: Alberto Soragna Signed-off-by: Alberto Soragna (cherry picked from commit 3fb012e2e979475f5044ab0e0f9b91d336db5f46) Co-authored-by: Alberto Soragna --- .../experimental/buffers/ring_buffer_implementation.hpp | 3 +-- .../rclcpp/experimental/subscription_intra_process.hpp | 8 +++++++- 2 files changed, 8 insertions(+), 3 deletions(-) diff --git a/rclcpp/include/rclcpp/experimental/buffers/ring_buffer_implementation.hpp b/rclcpp/include/rclcpp/experimental/buffers/ring_buffer_implementation.hpp index c01240b429..245d417d86 100644 --- a/rclcpp/include/rclcpp/experimental/buffers/ring_buffer_implementation.hpp +++ b/rclcpp/include/rclcpp/experimental/buffers/ring_buffer_implementation.hpp @@ -86,8 +86,7 @@ class RingBufferImplementation : public BufferImplementationBase std::lock_guard 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_]); diff --git a/rclcpp/include/rclcpp/experimental/subscription_intra_process.hpp b/rclcpp/include/rclcpp/experimental/subscription_intra_process.hpp index 803d940086..91ea91a7c3 100644 --- a/rclcpp/include/rclcpp/experimental/subscription_intra_process.hpp +++ b/rclcpp/include/rclcpp/experimental/subscription_intra_process.hpp @@ -109,8 +109,14 @@ class SubscriptionIntraProcess 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( std::make_shared>( @@ -138,7 +144,7 @@ class SubscriptionIntraProcess execute_impl(std::shared_ptr & data) { if (!data) { - throw std::runtime_error("'data' is empty"); + return; } rmw_message_info_t msg_info; From ce13f1afba11f2b3bb9fe19583f6d2a978118b93 Mon Sep 17 00:00:00 2001 From: "mergify[bot]" <37929162+mergify[bot]@users.noreply.github.com> Date: Fri, 6 Jan 2023 10:19:33 -0800 Subject: [PATCH 09/98] Fix SharedFuture from async_send_request never becomes valid (#2044) (#2076) Signed-off-by: Lei Liu (cherry picked from commit 66b19448b0520b15a9e6c28483863b2a4351c2f6) Co-authored-by: Lei Liu <64953129+llapx@users.noreply.github.com> --- rclcpp/include/rclcpp/client.hpp | 10 ++++------ 1 file changed, 4 insertions(+), 6 deletions(-) diff --git a/rclcpp/include/rclcpp/client.hpp b/rclcpp/include/rclcpp/client.hpp index adc9094f61..9c7f19ffe7 100644 --- a/rclcpp/include/rclcpp/client.hpp +++ b/rclcpp/include/rclcpp/client.hpp @@ -794,16 +794,14 @@ class Client : public ClientBase async_send_request_impl(const Request & request, CallbackInfoVariant value) { int64_t sequence_number; + std::lock_guard 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 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; } From 9171122eae940504200889b2a7b12344fa7c0c8b Mon Sep 17 00:00:00 2001 From: Audrow Nash Date: Tue, 10 Jan 2023 07:58:19 -0600 Subject: [PATCH 10/98] 16.0.3 Signed-off-by: Audrow Nash --- rclcpp/CHANGELOG.rst | 8 ++++++++ rclcpp/package.xml | 2 +- rclcpp_action/CHANGELOG.rst | 3 +++ rclcpp_action/package.xml | 2 +- rclcpp_components/CHANGELOG.rst | 3 +++ rclcpp_components/package.xml | 2 +- rclcpp_lifecycle/CHANGELOG.rst | 3 +++ rclcpp_lifecycle/package.xml | 2 +- 8 files changed, 21 insertions(+), 4 deletions(-) diff --git a/rclcpp/CHANGELOG.rst b/rclcpp/CHANGELOG.rst index d6f3da6eec..c7c4f5e6e9 100644 --- a/rclcpp/CHANGELOG.rst +++ b/rclcpp/CHANGELOG.rst @@ -2,6 +2,14 @@ Changelog for package rclcpp ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +16.0.3 (2023-01-10) +------------------- +* Fix SharedFuture from async_send_request never becomes valid (`#2044 `_) (`#2076 `_) +* do not throw exception if trying to dequeue an empty intra-process buffer (`#2061 `_) (`#2070 `_) +* fix nullptr dereference in prune_requests_older_than (`#2008 `_) (`#2065 `_) +* Fix bug that a callback not reached (`#1640 `_) (`#2033 `_) +* Contributors: mergify[bot] + 16.0.2 (2022-11-07) ------------------- * fix mismatched issue if using zero_allocate (`#1995 `_) (`#2026 `_) diff --git a/rclcpp/package.xml b/rclcpp/package.xml index 9390433faa..398cfd3e37 100644 --- a/rclcpp/package.xml +++ b/rclcpp/package.xml @@ -2,7 +2,7 @@ rclcpp - 16.0.2 + 16.0.3 The ROS client library in C++. Ivan Paunovic Jacob Perron diff --git a/rclcpp_action/CHANGELOG.rst b/rclcpp_action/CHANGELOG.rst index 43ab885467..d642fe421b 100644 --- a/rclcpp_action/CHANGELOG.rst +++ b/rclcpp_action/CHANGELOG.rst @@ -3,6 +3,9 @@ Changelog for package rclcpp_action ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +16.0.3 (2023-01-10) +------------------- + 16.0.2 (2022-11-07) ------------------- diff --git a/rclcpp_action/package.xml b/rclcpp_action/package.xml index a1774eba80..ba17333c0c 100644 --- a/rclcpp_action/package.xml +++ b/rclcpp_action/package.xml @@ -2,7 +2,7 @@ rclcpp_action - 16.0.2 + 16.0.3 Adds action APIs for C++. Ivan Paunovic Jacob Perron diff --git a/rclcpp_components/CHANGELOG.rst b/rclcpp_components/CHANGELOG.rst index 512a0cf9aa..95ab03e63e 100644 --- a/rclcpp_components/CHANGELOG.rst +++ b/rclcpp_components/CHANGELOG.rst @@ -2,6 +2,9 @@ Changelog for package rclcpp_components ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +16.0.3 (2023-01-10) +------------------- + 16.0.2 (2022-11-07) ------------------- diff --git a/rclcpp_components/package.xml b/rclcpp_components/package.xml index c2f6c0a226..fcb54a9794 100644 --- a/rclcpp_components/package.xml +++ b/rclcpp_components/package.xml @@ -2,7 +2,7 @@ rclcpp_components - 16.0.2 + 16.0.3 Package containing tools for dynamically loadable components Ivan Paunovic Jacob Perron diff --git a/rclcpp_lifecycle/CHANGELOG.rst b/rclcpp_lifecycle/CHANGELOG.rst index 3d12db8ba1..5a7a1cb555 100644 --- a/rclcpp_lifecycle/CHANGELOG.rst +++ b/rclcpp_lifecycle/CHANGELOG.rst @@ -3,6 +3,9 @@ Changelog for package rclcpp_lifecycle ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +16.0.3 (2023-01-10) +------------------- + 16.0.2 (2022-11-07) ------------------- diff --git a/rclcpp_lifecycle/package.xml b/rclcpp_lifecycle/package.xml index 39f382beba..134234a9e9 100644 --- a/rclcpp_lifecycle/package.xml +++ b/rclcpp_lifecycle/package.xml @@ -2,7 +2,7 @@ rclcpp_lifecycle - 16.0.2 + 16.0.3 Package containing a prototype for lifecycle implementation Ivan Paunovic Jacob Perron From 4196a2a8b4d523e8bf7601c7630f4385889bc7b2 Mon Sep 17 00:00:00 2001 From: "mergify[bot]" <37929162+mergify[bot]@users.noreply.github.com> Date: Mon, 20 Mar 2023 13:36:32 -0700 Subject: [PATCH 11/98] use allocator via init_options argument. (#2129) (#2131) Signed-off-by: Tomoya Fujita (cherry picked from commit 1a796b5515cde3d8b6a64f8c53c7f49d8a742d32) Co-authored-by: Tomoya Fujita --- rclcpp/src/rclcpp/context.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/rclcpp/src/rclcpp/context.cpp b/rclcpp/src/rclcpp/context.cpp index bea4eeb583..b9410ca089 100644 --- a/rclcpp/src/rclcpp/context.cpp +++ b/rclcpp/src/rclcpp/context.cpp @@ -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(); From c8ac675035a86ab7805900e556fd7ae04d4d3938 Mon Sep 17 00:00:00 2001 From: Tomoya Fujita Date: Thu, 30 Mar 2023 08:43:03 -0700 Subject: [PATCH 12/98] extract the result response before the callback is issued. (#2133) backport of https://github.com/ros2/rclcpp/pull/2132 Signed-off-by: Tomoya Fujita Co-authored-by: Chen Lihui --- rclcpp_action/src/client.cpp | 18 +++++++++++------- 1 file changed, 11 insertions(+), 7 deletions(-) diff --git a/rclcpp_action/src/client.cpp b/rclcpp_action/src/client.cpp index f9144ecf49..6122725ca6 100644 --- a/rclcpp_action/src/client.cpp +++ b/rclcpp_action/src/client.cpp @@ -319,14 +319,18 @@ ClientBase::handle_result_response( const rmw_request_id_t & response_header, std::shared_ptr response) { - std::lock_guard 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 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 From 19a666f1c9393e86f0f0cc5a8213cbd18b01fc03 Mon Sep 17 00:00:00 2001 From: Tomoya Fujita Date: Fri, 31 Mar 2023 08:45:10 -0700 Subject: [PATCH 13/98] Revert "extract the result response before the callback is issued. (#2133)" (#2148) This reverts commit c8ac675035a86ab7805900e556fd7ae04d4d3938. Signed-off-by: Tomoya Fujita --- rclcpp_action/src/client.cpp | 18 +++++++----------- 1 file changed, 7 insertions(+), 11 deletions(-) diff --git a/rclcpp_action/src/client.cpp b/rclcpp_action/src/client.cpp index 6122725ca6..f9144ecf49 100644 --- a/rclcpp_action/src/client.cpp +++ b/rclcpp_action/src/client.cpp @@ -319,18 +319,14 @@ ClientBase::handle_result_response( const rmw_request_id_t & response_header, std::shared_ptr response) { - ResponseCallback response_callback; - { - std::lock_guard 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); + std::lock_guard 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(response); + pimpl_->pending_result_responses[sequence_number](response); + pimpl_->pending_result_responses.erase(sequence_number); } void From b2b7bdeac16f6a3bd62d1f03fd18af32d1ba29f6 Mon Sep 17 00:00:00 2001 From: Tomoya Fujita Date: Sun, 2 Apr 2023 18:03:05 -0700 Subject: [PATCH 14/98] Revert "Revert "extract the result response before the callback is issued. (#2133)" (#2148)" (#2152) This reverts commit 19a666f1c9393e86f0f0cc5a8213cbd18b01fc03. Signed-off-by: Tomoya Fujita --- rclcpp_action/src/client.cpp | 18 +++++++++++------- 1 file changed, 11 insertions(+), 7 deletions(-) diff --git a/rclcpp_action/src/client.cpp b/rclcpp_action/src/client.cpp index f9144ecf49..6122725ca6 100644 --- a/rclcpp_action/src/client.cpp +++ b/rclcpp_action/src/client.cpp @@ -319,14 +319,18 @@ ClientBase::handle_result_response( const rmw_request_id_t & response_header, std::shared_ptr response) { - std::lock_guard 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 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 From 00ef09cbf34563db27b9aac9b0206eb142cf3995 Mon Sep 17 00:00:00 2001 From: Audrow Nash Date: Tue, 25 Apr 2023 20:55:32 +0000 Subject: [PATCH 15/98] 16.0.4 Signed-off-by: Audrow Nash --- rclcpp/CHANGELOG.rst | 5 +++++ rclcpp/package.xml | 2 +- rclcpp_action/CHANGELOG.rst | 7 +++++++ rclcpp_action/package.xml | 2 +- rclcpp_components/CHANGELOG.rst | 3 +++ rclcpp_components/package.xml | 2 +- rclcpp_lifecycle/CHANGELOG.rst | 3 +++ rclcpp_lifecycle/package.xml | 2 +- 8 files changed, 22 insertions(+), 4 deletions(-) diff --git a/rclcpp/CHANGELOG.rst b/rclcpp/CHANGELOG.rst index c7c4f5e6e9..27176c0305 100644 --- a/rclcpp/CHANGELOG.rst +++ b/rclcpp/CHANGELOG.rst @@ -2,6 +2,11 @@ Changelog for package rclcpp ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +16.0.4 (2023-04-25) +------------------- +* use allocator via init_options argument. (`#2129 `_) (`#2131 `_) +* Contributors: mergify[bot] + 16.0.3 (2023-01-10) ------------------- * Fix SharedFuture from async_send_request never becomes valid (`#2044 `_) (`#2076 `_) diff --git a/rclcpp/package.xml b/rclcpp/package.xml index 398cfd3e37..840faae35f 100644 --- a/rclcpp/package.xml +++ b/rclcpp/package.xml @@ -2,7 +2,7 @@ rclcpp - 16.0.3 + 16.0.4 The ROS client library in C++. Ivan Paunovic Jacob Perron diff --git a/rclcpp_action/CHANGELOG.rst b/rclcpp_action/CHANGELOG.rst index d642fe421b..41b7f671dc 100644 --- a/rclcpp_action/CHANGELOG.rst +++ b/rclcpp_action/CHANGELOG.rst @@ -3,6 +3,13 @@ Changelog for package rclcpp_action ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +16.0.4 (2023-04-25) +------------------- +* Revert "Revert "extract the result response before the callback is issued. (`#2133 `_)" (`#2148 `_)" (`#2152 `_) +* Revert "extract the result response before the callback is issued. (`#2133 `_)" (`#2148 `_) +* extract the result response before the callback is issued. (`#2133 `_) +* Contributors: Tomoya Fujita + 16.0.3 (2023-01-10) ------------------- diff --git a/rclcpp_action/package.xml b/rclcpp_action/package.xml index ba17333c0c..ce9668287b 100644 --- a/rclcpp_action/package.xml +++ b/rclcpp_action/package.xml @@ -2,7 +2,7 @@ rclcpp_action - 16.0.3 + 16.0.4 Adds action APIs for C++. Ivan Paunovic Jacob Perron diff --git a/rclcpp_components/CHANGELOG.rst b/rclcpp_components/CHANGELOG.rst index 95ab03e63e..403197e020 100644 --- a/rclcpp_components/CHANGELOG.rst +++ b/rclcpp_components/CHANGELOG.rst @@ -2,6 +2,9 @@ Changelog for package rclcpp_components ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +16.0.4 (2023-04-25) +------------------- + 16.0.3 (2023-01-10) ------------------- diff --git a/rclcpp_components/package.xml b/rclcpp_components/package.xml index fcb54a9794..8b0822aa8b 100644 --- a/rclcpp_components/package.xml +++ b/rclcpp_components/package.xml @@ -2,7 +2,7 @@ rclcpp_components - 16.0.3 + 16.0.4 Package containing tools for dynamically loadable components Ivan Paunovic Jacob Perron diff --git a/rclcpp_lifecycle/CHANGELOG.rst b/rclcpp_lifecycle/CHANGELOG.rst index 5a7a1cb555..e1c58a03d5 100644 --- a/rclcpp_lifecycle/CHANGELOG.rst +++ b/rclcpp_lifecycle/CHANGELOG.rst @@ -3,6 +3,9 @@ Changelog for package rclcpp_lifecycle ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +16.0.4 (2023-04-25) +------------------- + 16.0.3 (2023-01-10) ------------------- diff --git a/rclcpp_lifecycle/package.xml b/rclcpp_lifecycle/package.xml index 134234a9e9..b230eaa82f 100644 --- a/rclcpp_lifecycle/package.xml +++ b/rclcpp_lifecycle/package.xml @@ -2,7 +2,7 @@ rclcpp_lifecycle - 16.0.3 + 16.0.4 Package containing a prototype for lifecycle implementation Ivan Paunovic Jacob Perron From 613d192cd623f0f980bc597cb0f8acef82011ccb Mon Sep 17 00:00:00 2001 From: "mergify[bot]" <37929162+mergify[bot]@users.noreply.github.com> Date: Wed, 14 Jun 2023 14:04:50 -0700 Subject: [PATCH 16/98] Implement validity checks for rclcpp::Clock (#2040) (#2210) (cherry picked from commit c091fe1a4538dbb370a31d0e590bd44ae4194483) Co-authored-by: methylDragon --- rclcpp/include/rclcpp/clock.hpp | 45 ++++++++++++++++++++ rclcpp/src/rclcpp/clock.cpp | 65 +++++++++++++++++++++++++++++ rclcpp/test/rclcpp/test_time.cpp | 70 ++++++++++++++++++++++++++++++++ 3 files changed, 180 insertions(+) diff --git a/rclcpp/include/rclcpp/clock.hpp b/rclcpp/include/rclcpp/clock.hpp index 42d3e01e7e..c41f17312b 100644 --- a/rclcpp/include/rclcpp/clock.hpp +++ b/rclcpp/include/rclcpp/clock.hpp @@ -137,6 +137,51 @@ class Clock Duration rel_time, Context::SharedPtr context = contexts::get_global_default_context()); + /** + * Check if the clock is started. + * + * A started clock is a clock that reflects non-zero time. + * Typically a clock will be unstarted if it is using RCL_ROS_TIME with ROS time and + * nothing has been published on the clock topic yet. + * + * \return true if clock is started + * \throws std::runtime_error if the clock is not rcl_clock_valid + */ + RCLCPP_PUBLIC + bool + started(); + + /** + * Wait until clock to start. + * + * \rclcpp::Clock::started + * \param context the context to wait in + * \return true if clock was already started or became started + * \throws std::runtime_error if the context is invalid or clock is not rcl_clock_valid + */ + RCLCPP_PUBLIC + bool + wait_until_started(Context::SharedPtr context = contexts::get_global_default_context()); + + /** + * Wait for clock to start, with timeout. + * + * The timeout is waited in steady time. + * + * \rclcpp::Clock::started + * \param timeout the maximum time to wait for. + * \param context the context to wait in. + * \param wait_tick_ns the time to wait between each iteration of the wait loop (in nanoseconds). + * \return true if clock was or became valid + * \throws std::runtime_error if the context is invalid or clock is not rcl_clock_valid + */ + RCLCPP_PUBLIC + bool + wait_until_started( + const rclcpp::Duration & timeout, + Context::SharedPtr context = contexts::get_global_default_context(), + const rclcpp::Duration & wait_tick_ns = rclcpp::Duration(0, static_cast(1e7))); + /** * Returns the clock of the type `RCL_ROS_TIME` is active. * diff --git a/rclcpp/src/rclcpp/clock.cpp b/rclcpp/src/rclcpp/clock.cpp index 66c8db70e1..7955d119c0 100644 --- a/rclcpp/src/rclcpp/clock.cpp +++ b/rclcpp/src/rclcpp/clock.cpp @@ -182,6 +182,71 @@ Clock::sleep_for(Duration rel_time, Context::SharedPtr context) return sleep_until(now() + rel_time, context); } +bool +Clock::started() +{ + if (!rcl_clock_valid(get_clock_handle())) { + throw std::runtime_error("clock is not rcl_clock_valid"); + } + return rcl_clock_time_started(get_clock_handle()); +} + +bool +Clock::wait_until_started(Context::SharedPtr context) +{ + if (!context || !context->is_valid()) { + throw std::runtime_error("context cannot be slept with because it's invalid"); + } + if (!rcl_clock_valid(get_clock_handle())) { + throw std::runtime_error("clock cannot be waited on as it is not rcl_clock_valid"); + } + + if (started()) { + return true; + } else { + // Wait until the first non-zero time + return sleep_until(rclcpp::Time(0, 1, get_clock_type()), context); + } +} + +bool +Clock::wait_until_started( + const Duration & timeout, + Context::SharedPtr context, + const Duration & wait_tick_ns) +{ + if (!context || !context->is_valid()) { + throw std::runtime_error("context cannot be slept with because it's invalid"); + } + if (!rcl_clock_valid(get_clock_handle())) { + throw std::runtime_error("clock cannot be waited on as it is not rcl_clock_valid"); + } + + Clock timeout_clock = Clock(RCL_STEADY_TIME); + Time start = timeout_clock.now(); + + // Check if the clock has started every wait_tick_ns nanoseconds + // Context check checks for rclcpp::shutdown() + while (!started() && context->is_valid()) { + if (timeout < wait_tick_ns) { + timeout_clock.sleep_for(timeout); + } else { + Duration time_left = start + timeout - timeout_clock.now(); + if (time_left > wait_tick_ns) { + timeout_clock.sleep_for(Duration(wait_tick_ns)); + } else { + timeout_clock.sleep_for(time_left); + } + } + + if (timeout_clock.now() - start > timeout) { + return started(); + } + } + return started(); +} + + bool Clock::ros_time_is_active() { diff --git a/rclcpp/test/rclcpp/test_time.cpp b/rclcpp/test/rclcpp/test_time.cpp index 2f188b2d7c..f3969d3886 100644 --- a/rclcpp/test/rclcpp/test_time.cpp +++ b/rclcpp/test/rclcpp/test_time.cpp @@ -19,6 +19,7 @@ #include #include #include +#include #include "rcl/error_handling.h" #include "rcl/time.h" @@ -848,3 +849,72 @@ TEST_F(TestClockSleep, sleep_for_basic_ros) { sleep_thread.join(); EXPECT_TRUE(sleep_succeeded); } + +class TestClockStarted : public ::testing::Test +{ +protected: + void SetUp() + { + rclcpp::init(0, nullptr); + } + + void TearDown() + { + rclcpp::shutdown(); + } +}; + +TEST_F(TestClockStarted, started) { + // rclcpp::Clock ros_clock(RCL_ROS_TIME); + // auto ros_clock_handle = ros_clock.get_clock_handle(); + // + // // At this point, the ROS clock is reading system time since the ROS time override isn't on + // // So we expect it to be started (it's extremely unlikely that system time is at epoch start) + // EXPECT_TRUE(ros_clock.started()); + // EXPECT_TRUE(ros_clock.wait_until_started()); + // EXPECT_TRUE(ros_clock.wait_until_started(rclcpp::Duration(0, static_cast(1e7)))); + // EXPECT_EQ(RCL_RET_OK, rcl_enable_ros_time_override(ros_clock_handle)); + // EXPECT_TRUE(ros_clock.ros_time_is_active()); + // EXPECT_FALSE(ros_clock.started()); + // EXPECT_EQ(RCL_RET_OK, rcl_set_ros_time_override(ros_clock_handle, 1)); + // EXPECT_TRUE(ros_clock.started()); + // + // rclcpp::Clock system_clock(RCL_SYSTEM_TIME); + // EXPECT_TRUE(system_clock.started()); + // EXPECT_TRUE(system_clock.wait_until_started()); + // EXPECT_TRUE(system_clock.wait_until_started(rclcpp::Duration(0, static_cast(1e7)))); + // + // rclcpp::Clock steady_clock(RCL_STEADY_TIME); + // EXPECT_TRUE(steady_clock.started()); + // EXPECT_TRUE(steady_clock.wait_until_started()); + // EXPECT_TRUE(steady_clock.wait_until_started(rclcpp::Duration(0, static_cast(1e7)))); + // + // rclcpp::Clock uninit_clock(RCL_CLOCK_UNINITIALIZED); + // RCLCPP_EXPECT_THROW_EQ( + // uninit_clock.started(), std::runtime_error("clock is not rcl_clock_valid")); + // RCLCPP_EXPECT_THROW_EQ( + // uninit_clock.wait_until_started(rclcpp::Duration(0, static_cast(1e7))), + // std::runtime_error("clock cannot be waited on as it is not rcl_clock_valid")); +} + +TEST_F(TestClockStarted, started_timeout) { + rclcpp::Clock ros_clock(RCL_ROS_TIME); + auto ros_clock_handle = ros_clock.get_clock_handle(); + + EXPECT_EQ(RCL_RET_OK, rcl_enable_ros_time_override(ros_clock_handle)); + EXPECT_TRUE(ros_clock.ros_time_is_active()); + + EXPECT_EQ(RCL_RET_OK, rcl_set_ros_time_override(ros_clock_handle, 0)); + + EXPECT_FALSE(ros_clock.started()); + EXPECT_FALSE(ros_clock.wait_until_started(rclcpp::Duration(0, static_cast(1e7)))); + + std::thread t([]() { + std::this_thread::sleep_for(std::chrono::seconds(1)); + rclcpp::shutdown(); + }); + + // Test rclcpp shutdown escape hatch (otherwise this waits indefinitely) + EXPECT_FALSE(ros_clock.wait_until_started()); + t.join(); +} From 5f7485f4fd5dea50cb83133e9ff9f0e36bcce35c Mon Sep 17 00:00:00 2001 From: Tomoya Fujita Date: Thu, 15 Jun 2023 15:19:56 -0700 Subject: [PATCH 17/98] Trigger the intraprocess guard condition with data (#2164) (#2167) If the intraprocess buffer still has data after taking, re-trigger the guard condition to ensure that the executor will continue to service it, even if incoming publications stop. Signed-off-by: Michael Carroll (cherry picked from commit 5f9695afb02f178ec739fa1591bb018a9f9b2be0) Co-authored-by: Michael Carroll --- .../subscription_intra_process.hpp | 7 ++ .../test/rclcpp/executors/test_executors.cpp | 103 ++++++++++++++++++ 2 files changed, 110 insertions(+) diff --git a/rclcpp/include/rclcpp/experimental/subscription_intra_process.hpp b/rclcpp/include/rclcpp/experimental/subscription_intra_process.hpp index 91ea91a7c3..ec89ebc5ef 100644 --- a/rclcpp/include/rclcpp/experimental/subscription_intra_process.hpp +++ b/rclcpp/include/rclcpp/experimental/subscription_intra_process.hpp @@ -118,6 +118,13 @@ class SubscriptionIntraProcess 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( std::make_shared>( std::pair( diff --git a/rclcpp/test/rclcpp/executors/test_executors.cpp b/rclcpp/test/rclcpp/executors/test_executors.cpp index 1fa2cbb4dd..eb6652f19b 100644 --- a/rclcpp/test/rclcpp/executors/test_executors.cpp +++ b/rclcpp/test/rclcpp/executors/test_executors.cpp @@ -593,3 +593,106 @@ TEST(TestExecutors, testSpinUntilFutureCompleteNodePtr) { rclcpp::shutdown(); } + +template +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("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(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( + 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::SharedPtr publisher; + rclcpp::Subscription::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()); +} From a75baa6b26638cd18bbdce7634182cfa7ad781c8 Mon Sep 17 00:00:00 2001 From: "mergify[bot]" <37929162+mergify[bot]@users.noreply.github.com> Date: Fri, 23 Jun 2023 13:00:14 -0400 Subject: [PATCH 18/98] warning: comparison of integer expressions of different signedness (#2219) (#2223) https://github.com/ros2/rclcpp/pull/2167#issuecomment-1597197552 Signed-off-by: Tomoya Fujita (cherry picked from commit fe2e0e4c646545625ad9f82e929be651b3a5fd95) Co-authored-by: Tomoya Fujita --- rclcpp/test/rclcpp/executors/test_executors.cpp | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/rclcpp/test/rclcpp/executors/test_executors.cpp b/rclcpp/test/rclcpp/executors/test_executors.cpp index eb6652f19b..143068601a 100644 --- a/rclcpp/test/rclcpp/executors/test_executors.cpp +++ b/rclcpp/test/rclcpp/executors/test_executors.cpp @@ -615,7 +615,7 @@ class TestIntraprocessExecutors : public ::testing::Test test_name << test_info->test_case_name() << "_" << test_info->name(); node = std::make_shared("node", test_name.str()); - callback_count = 0; + callback_count = 0u; const std::string topic_name = std::string("topic_") + test_name.str(); @@ -624,7 +624,7 @@ class TestIntraprocessExecutors : public ::testing::Test publisher = node->create_publisher(topic_name, rclcpp::QoS(1), po); auto callback = [this](test_msgs::msg::Empty::ConstSharedPtr) { - this->callback_count.fetch_add(1); + this->callback_count.fetch_add(1u); }; rclcpp::SubscriptionOptions so; @@ -646,7 +646,7 @@ class TestIntraprocessExecutors : public ::testing::Test rclcpp::Node::SharedPtr node; rclcpp::Publisher::SharedPtr publisher; rclcpp::Subscription::SharedPtr subscription; - std::atomic_int callback_count; + std::atomic_size_t callback_count; }; TYPED_TEST_SUITE(TestIntraprocessExecutors, ExecutorTypes, ExecutorTypeNames); @@ -662,7 +662,7 @@ TYPED_TEST(TestIntraprocessExecutors, testIntraprocessRetrigger) { ExecutorType executor; executor.add_node(this->node); - EXPECT_EQ(0, this->callback_count.load()); + EXPECT_EQ(0u, this->callback_count.load()); this->publisher->publish(test_msgs::msg::Empty()); // Wait for up to 5 seconds for the first message to come available. @@ -676,7 +676,7 @@ TYPED_TEST(TestIntraprocessExecutors, testIntraprocessRetrigger) { EXPECT_EQ(1u, this->callback_count.load()); // reset counter - this->callback_count.store(0); + this->callback_count.store(0u); for (size_t ii = 0; ii < kNumMessages; ++ii) { this->publisher->publish(test_msgs::msg::Empty()); From 25263e838d03b32b07cf1b3d13d0306a5d590642 Mon Sep 17 00:00:00 2001 From: Joseph Schornak Date: Mon, 26 Jun 2023 20:46:58 -0400 Subject: [PATCH 19/98] Fix thread safety in LifecycleNode::get_current_state() for Humble (#2183) * add initially-failing test case * apply changes to LifecycleNodeInterfaceImpl from #1756 * add static member to State for managing state_handle_ access * allow parallel read access in MutexMap Signed-off-by: Joe Schornak --- rclcpp_lifecycle/CMakeLists.txt | 2 + .../include/rclcpp_lifecycle/state.hpp | 17 +++ .../src/lifecycle_node_interface_impl.hpp | 138 +++++++++++------- rclcpp_lifecycle/src/mutex_map.cpp | 43 ++++++ rclcpp_lifecycle/src/mutex_map.hpp | 65 +++++++++ rclcpp_lifecycle/src/state.cpp | 20 ++- rclcpp_lifecycle/test/test_lifecycle_node.cpp | 22 +++ 7 files changed, 254 insertions(+), 53 deletions(-) create mode 100644 rclcpp_lifecycle/src/mutex_map.cpp create mode 100644 rclcpp_lifecycle/src/mutex_map.hpp diff --git a/rclcpp_lifecycle/CMakeLists.txt b/rclcpp_lifecycle/CMakeLists.txt index a823d44a68..1f852ade09 100644 --- a/rclcpp_lifecycle/CMakeLists.txt +++ b/rclcpp_lifecycle/CMakeLists.txt @@ -20,6 +20,8 @@ find_package(lifecycle_msgs REQUIRED) add_library(rclcpp_lifecycle src/lifecycle_node.cpp src/managed_entity.cpp + src/mutex_map.cpp + src/mutex_map.hpp src/node_interfaces/lifecycle_node_interface.cpp src/state.cpp src/transition.cpp diff --git a/rclcpp_lifecycle/include/rclcpp_lifecycle/state.hpp b/rclcpp_lifecycle/include/rclcpp_lifecycle/state.hpp index a0ac997ff3..6e402692cf 100644 --- a/rclcpp_lifecycle/include/rclcpp_lifecycle/state.hpp +++ b/rclcpp_lifecycle/include/rclcpp_lifecycle/state.hpp @@ -24,6 +24,8 @@ namespace rclcpp_lifecycle { +/// Forward declaration of mutex helper class +class MutexMap; /// Abstract class for the Lifecycle's states. /** @@ -92,6 +94,21 @@ class State bool owns_rcl_state_handle_; rcl_lifecycle_state_t * state_handle_; + +private: + /// Maps state handle mutexes to each instance of State. + /** + * \details A mutex is added to this map when each new instance of State is constructed. + * + * The mutex is removed when the instance of State is destroyed. + * + * The mutex is locked while state_handle_ is being accessed. + * + * This static member exists to allow implementing the fix described in ros2/rclcpp#1756 + * in Humble without breaking ABI compatibility, since adding a new static data + * member is permitted under REP-0009. + */ + static MutexMap state_handle_mutex_map_; }; } // namespace rclcpp_lifecycle diff --git a/rclcpp_lifecycle/src/lifecycle_node_interface_impl.hpp b/rclcpp_lifecycle/src/lifecycle_node_interface_impl.hpp index 51de8eab07..60570d4059 100644 --- a/rclcpp_lifecycle/src/lifecycle_node_interface_impl.hpp +++ b/rclcpp_lifecycle/src/lifecycle_node_interface_impl.hpp @@ -20,6 +20,7 @@ #include #include #include +#include #include #include #include @@ -64,7 +65,11 @@ class LifecycleNode::LifecycleNodeInterfaceImpl ~LifecycleNodeInterfaceImpl() { rcl_node_t * node_handle = node_base_interface_->get_rcl_node_handle(); - auto ret = rcl_lifecycle_state_machine_fini(&state_machine_, node_handle); + rcl_ret_t ret; + { + std::lock_guard lock(state_machine_mutex_); + ret = rcl_lifecycle_state_machine_fini(&state_machine_, node_handle); + } if (ret != RCL_RET_OK) { RCUTILS_LOG_FATAL_NAMED( "rclcpp_lifecycle", @@ -78,7 +83,6 @@ class LifecycleNode::LifecycleNodeInterfaceImpl rcl_node_t * node_handle = node_base_interface_->get_rcl_node_handle(); const rcl_node_options_t * node_options = rcl_node_get_options(node_base_interface_->get_rcl_node_handle()); - state_machine_ = rcl_lifecycle_get_zero_initialized_state_machine(); auto state_machine_options = rcl_lifecycle_get_default_state_machine_options(); state_machine_options.enable_com_interface = enable_communication_interface; state_machine_options.allocator = node_options->allocator; @@ -89,6 +93,8 @@ class LifecycleNode::LifecycleNodeInterfaceImpl // The publisher takes a C-Typesupport since the publishing (i.e. creating // the message) is done fully in RCL. // Services are handled in C++, so that it needs a C++ typesupport structure. + std::lock_guard lock(state_machine_mutex_); + state_machine_ = rcl_lifecycle_get_zero_initialized_state_machine(); rcl_ret_t ret = rcl_lifecycle_state_machine_init( &state_machine_, node_handle, @@ -105,6 +111,8 @@ class LifecycleNode::LifecycleNodeInterfaceImpl node_base_interface_->get_name()); } + current_state_ = State(state_machine_.current_state); + if (enable_communication_interface) { { // change_state auto cb = std::bind( @@ -206,28 +214,30 @@ class LifecycleNode::LifecycleNodeInterfaceImpl std::shared_ptr resp) { (void)header; - if (rcl_lifecycle_state_machine_is_initialized(&state_machine_) != RCL_RET_OK) { - throw std::runtime_error( - "Can't get state. State machine is not initialized."); - } - - auto transition_id = req->transition.id; - // if there's a label attached to the request, - // we check the transition attached to this label. - // we further can't compare the id of the looked up transition - // because ros2 service call defaults all intergers to zero. - // that means if we call ros2 service call ... {transition: {label: shutdown}} - // the id of the request is 0 (zero) whereas the id from the lookup up transition - // can be different. - // the result of this is that the label takes presedence of the id. - if (req->transition.label.size() != 0) { - auto rcl_transition = rcl_lifecycle_get_transition_by_label( - state_machine_.current_state, req->transition.label.c_str()); - if (rcl_transition == nullptr) { - resp->success = false; - return; + std::uint8_t transition_id; + { + std::lock_guard lock(state_machine_mutex_); + if (rcl_lifecycle_state_machine_is_initialized(&state_machine_) != RCL_RET_OK) { + throw std::runtime_error("Can't get state. State machine is not initialized."); + } + transition_id = req->transition.id; + // if there's a label attached to the request, + // we check the transition attached to this label. + // we further can't compare the id of the looked up transition + // because ros2 service call defaults all intergers to zero. + // that means if we call ros2 service call ... {transition: {label: shutdown}} + // the id of the request is 0 (zero) whereas the id from the lookup up transition + // can be different. + // the result of this is that the label takes presedence of the id. + if (req->transition.label.size() != 0) { + auto rcl_transition = rcl_lifecycle_get_transition_by_label( + state_machine_.current_state, req->transition.label.c_str()); + if (rcl_transition == nullptr) { + resp->success = false; + return; + } + transition_id = static_cast(rcl_transition->id); } - transition_id = static_cast(rcl_transition->id); } node_interfaces::LifecycleNodeInterface::CallbackReturn cb_return_code; @@ -248,6 +258,7 @@ class LifecycleNode::LifecycleNodeInterfaceImpl { (void)header; (void)req; + std::lock_guard lock(state_machine_mutex_); if (rcl_lifecycle_state_machine_is_initialized(&state_machine_) != RCL_RET_OK) { throw std::runtime_error( "Can't get state. State machine is not initialized."); @@ -264,6 +275,7 @@ class LifecycleNode::LifecycleNodeInterfaceImpl { (void)header; (void)req; + std::lock_guard lock(state_machine_mutex_); if (rcl_lifecycle_state_machine_is_initialized(&state_machine_) != RCL_RET_OK) { throw std::runtime_error( "Can't get available states. State machine is not initialized."); @@ -286,6 +298,7 @@ class LifecycleNode::LifecycleNodeInterfaceImpl { (void)header; (void)req; + std::lock_guard lock(state_machine_mutex_); if (rcl_lifecycle_state_machine_is_initialized(&state_machine_) != RCL_RET_OK) { throw std::runtime_error( "Can't get available transitions. State machine is not initialized."); @@ -313,6 +326,7 @@ class LifecycleNode::LifecycleNodeInterfaceImpl { (void)header; (void)req; + std::lock_guard lock(state_machine_mutex_); if (rcl_lifecycle_state_machine_is_initialized(&state_machine_) != RCL_RET_OK) { throw std::runtime_error( "Can't get available transitions. State machine is not initialized."); @@ -343,6 +357,7 @@ class LifecycleNode::LifecycleNodeInterfaceImpl get_available_states() { std::vector states; + std::lock_guard lock(state_machine_mutex_); states.reserve(state_machine_.transition_map.states_size); for (unsigned int i = 0; i < state_machine_.transition_map.states_size; ++i) { @@ -355,6 +370,7 @@ class LifecycleNode::LifecycleNodeInterfaceImpl get_available_transitions() { std::vector transitions; + std::lock_guard lock(state_machine_mutex_); transitions.reserve(state_machine_.current_state->valid_transition_size); for (unsigned int i = 0; i < state_machine_.current_state->valid_transition_size; ++i) { @@ -367,6 +383,7 @@ class LifecycleNode::LifecycleNodeInterfaceImpl get_transition_graph() { std::vector transitions; + std::lock_guard lock(state_machine_mutex_); transitions.reserve(state_machine_.transition_map.transitions_size); for (unsigned int i = 0; i < state_machine_.transition_map.transitions_size; ++i) { @@ -378,26 +395,32 @@ class LifecycleNode::LifecycleNodeInterfaceImpl rcl_ret_t change_state(std::uint8_t transition_id, LifecycleNodeInterface::CallbackReturn & cb_return_code) { - if (rcl_lifecycle_state_machine_is_initialized(&state_machine_) != RCL_RET_OK) { - RCUTILS_LOG_ERROR( - "Unable to change state for state machine for %s: %s", - node_base_interface_->get_name(), rcl_get_error_string().str); - return RCL_RET_ERROR; - } - constexpr bool publish_update = true; - // keep the initial state to pass to a transition callback - State initial_state(state_machine_.current_state); + State initial_state; + unsigned int current_state_id; - if ( - rcl_lifecycle_trigger_transition_by_id( - &state_machine_, transition_id, publish_update) != RCL_RET_OK) { - RCUTILS_LOG_ERROR( - "Unable to start transition %u from current state %s: %s", - transition_id, state_machine_.current_state->label, rcl_get_error_string().str); - rcutils_reset_error(); - return RCL_RET_ERROR; + std::lock_guard lock(state_machine_mutex_); + if (rcl_lifecycle_state_machine_is_initialized(&state_machine_) != RCL_RET_OK) { + RCUTILS_LOG_ERROR( + "Unable to change state for state machine for %s: %s", + node_base_interface_->get_name(), rcl_get_error_string().str); + return RCL_RET_ERROR; + } + // keep the initial state to pass to a transition callback + initial_state = State(state_machine_.current_state); + + if ( + rcl_lifecycle_trigger_transition_by_id( + &state_machine_, transition_id, publish_update) != RCL_RET_OK) + { + RCUTILS_LOG_ERROR( + "Unable to start transition %u from current state %s: %s", + transition_id, state_machine_.current_state->label, rcl_get_error_string().str); + rcutils_reset_error(); + return RCL_RET_ERROR; + } + current_state_id = state_machine_.current_state->id; } auto get_label_for_return_code = @@ -411,18 +434,22 @@ class LifecycleNode::LifecycleNodeInterfaceImpl return rcl_lifecycle_transition_error_label; }; - cb_return_code = execute_callback(state_machine_.current_state->id, initial_state); + cb_return_code = execute_callback(current_state_id, initial_state); auto transition_label = get_label_for_return_code(cb_return_code); - if ( - rcl_lifecycle_trigger_transition_by_label( - &state_machine_, transition_label, publish_update) != RCL_RET_OK) { - RCUTILS_LOG_ERROR( - "Failed to finish transition %u. Current state is now: %s (%s)", - transition_id, state_machine_.current_state->label, rcl_get_error_string().str); - rcutils_reset_error(); - return RCL_RET_ERROR; + std::lock_guard lock(state_machine_mutex_); + if ( + rcl_lifecycle_trigger_transition_by_label( + &state_machine_, transition_label, publish_update) != RCL_RET_OK) + { + RCUTILS_LOG_ERROR( + "Failed to finish transition %u. Current state is now: %s (%s)", + transition_id, state_machine_.current_state->label, rcl_get_error_string().str); + rcutils_reset_error(); + return RCL_RET_ERROR; + } + current_state_id = state_machine_.current_state->id; } // error handling ?! @@ -430,8 +457,9 @@ class LifecycleNode::LifecycleNodeInterfaceImpl if (cb_return_code == node_interfaces::LifecycleNodeInterface::CallbackReturn::ERROR) { RCUTILS_LOG_WARN("Error occurred while doing error handling."); - auto error_cb_code = execute_callback(state_machine_.current_state->id, initial_state); + auto error_cb_code = execute_callback(current_state_id, initial_state); auto error_cb_label = get_label_for_return_code(error_cb_code); + std::lock_guard lock(state_machine_mutex_); if ( rcl_lifecycle_trigger_transition_by_label( &state_machine_, error_cb_label, publish_update) != RCL_RET_OK) @@ -476,8 +504,13 @@ class LifecycleNode::LifecycleNodeInterfaceImpl const State & trigger_transition( const char * transition_label, LifecycleNodeInterface::CallbackReturn & cb_return_code) { - auto transition = - rcl_lifecycle_get_transition_by_label(state_machine_.current_state, transition_label); + const rcl_lifecycle_transition_t * transition; + { + std::lock_guard lock(state_machine_mutex_); + + transition = + rcl_lifecycle_get_transition_by_label(state_machine_.current_state, transition_label); + } if (transition) { change_state(static_cast(transition->id), cb_return_code); } @@ -534,6 +567,7 @@ class LifecycleNode::LifecycleNodeInterfaceImpl } } + mutable std::recursive_mutex state_machine_mutex_; rcl_lifecycle_state_machine_t state_machine_; State current_state_; std::map< diff --git a/rclcpp_lifecycle/src/mutex_map.cpp b/rclcpp_lifecycle/src/mutex_map.cpp new file mode 100644 index 0000000000..89f2714c73 --- /dev/null +++ b/rclcpp_lifecycle/src/mutex_map.cpp @@ -0,0 +1,43 @@ +// Copyright 2023 PickNik, 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 "mutex_map.hpp" + +#include +#include +#include + +namespace rclcpp_lifecycle +{ +void MutexMap::add(const State * key) +{ + // Adding a new mutex to the map requires exclusive access + std::unique_lock lock(map_access_mutex_); + mutex_map_.emplace(key, std::make_unique()); +} + +std::recursive_mutex & MutexMap::getMutex(const State * key) const +{ + // Multiple threads can retrieve mutexes from the map at the same time + std::shared_lock lock(map_access_mutex_); + return *(mutex_map_.at(key)); +} + +void MutexMap::remove(const State * key) +{ + // Removing a mutex from the map requires exclusive access + std::unique_lock lock(map_access_mutex_); + mutex_map_.erase(key); +} +} // namespace rclcpp_lifecycle diff --git a/rclcpp_lifecycle/src/mutex_map.hpp b/rclcpp_lifecycle/src/mutex_map.hpp new file mode 100644 index 0000000000..f189ac6194 --- /dev/null +++ b/rclcpp_lifecycle/src/mutex_map.hpp @@ -0,0 +1,65 @@ +// Copyright 2023 PickNik, 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 MUTEX_MAP_HPP_ +#define MUTEX_MAP_HPP_ + +#include +#include +#include +#include + +#include "rclcpp_lifecycle/state.hpp" + +namespace rclcpp_lifecycle +{ +/// @brief Associates instances of recursive_mutex with instances of State. +class MutexMap +{ +public: + MutexMap() = default; + + /// \brief Add a new mutex for an instance of State. + /** + * \param[in] key Raw pointer to the instance of State which will use the mutex. + */ + void add(const State * key); + + /// \brief Retrieve the mutex for an instance of State. + /** + * \param key Raw pointer to an instance of State. + * \return A reference to the mutex associated with the key. + */ + std::recursive_mutex & getMutex(const State * key) const; + + /// \brief Remove the mutex for an instance of State. + /** + * \param key Raw pointer to an instance of State. + */ + void remove(const State * key); + +private: + /// \brief Map that stores the mutexes + /** + * \details The mutexes are emplaced as unique_ptrs because mutexes are + * not copyable or movable. + */ + std::map> mutex_map_; + + /// @brief Controls access to mutex_map_. + mutable std::shared_mutex map_access_mutex_; +}; +} // namespace rclcpp_lifecycle + +#endif // MUTEX_MAP_HPP_ diff --git a/rclcpp_lifecycle/src/state.cpp b/rclcpp_lifecycle/src/state.cpp index f7aca2688e..7f399399cd 100644 --- a/rclcpp_lifecycle/src/state.cpp +++ b/rclcpp_lifecycle/src/state.cpp @@ -25,12 +25,17 @@ #include "rcutils/allocator.h" +#include "mutex_map.hpp" + namespace rclcpp_lifecycle { +MutexMap State::state_handle_mutex_map_; State::State(rcutils_allocator_t allocator) : State(lifecycle_msgs::msg::State::PRIMARY_STATE_UNKNOWN, "unknown", allocator) -{} +{ + state_handle_mutex_map_.add(this); +} State::State( uint8_t id, @@ -40,6 +45,8 @@ State::State( owns_rcl_state_handle_(true), state_handle_(nullptr) { + state_handle_mutex_map_.add(this); + if (label.empty()) { throw std::runtime_error("Lifecycle State cannot have an empty label."); } @@ -67,6 +74,8 @@ State::State( owns_rcl_state_handle_(false), state_handle_(nullptr) { + state_handle_mutex_map_.add(this); + if (!rcl_lifecycle_state_handle) { throw std::runtime_error("rcl_lifecycle_state_handle is null"); } @@ -78,12 +87,15 @@ State::State(const State & rhs) owns_rcl_state_handle_(false), state_handle_(nullptr) { + state_handle_mutex_map_.add(this); + *this = rhs; } State::~State() { reset(); + state_handle_mutex_map_.remove(this); } State & @@ -93,6 +105,8 @@ State::operator=(const State & rhs) return *this; } + const auto lock = std::lock_guard(state_handle_mutex_map_.getMutex(this)); + // reset all currently used resources reset(); @@ -128,6 +142,7 @@ State::operator=(const State & rhs) uint8_t State::id() const { + const auto lock = std::lock_guard(state_handle_mutex_map_.getMutex(this)); if (!state_handle_) { throw std::runtime_error("Error in state! Internal state_handle is NULL."); } @@ -137,6 +152,7 @@ State::id() const std::string State::label() const { + const auto lock = std::lock_guard(state_handle_mutex_map_.getMutex(this)); if (!state_handle_) { throw std::runtime_error("Error in state! Internal state_handle is NULL."); } @@ -146,6 +162,8 @@ State::label() const void State::reset() noexcept { + const auto lock = std::lock_guard(state_handle_mutex_map_.getMutex(this)); + if (!owns_rcl_state_handle_) { state_handle_ = nullptr; } diff --git a/rclcpp_lifecycle/test/test_lifecycle_node.cpp b/rclcpp_lifecycle/test/test_lifecycle_node.cpp index e1863a4d39..5a3054781b 100644 --- a/rclcpp_lifecycle/test/test_lifecycle_node.cpp +++ b/rclcpp_lifecycle/test/test_lifecycle_node.cpp @@ -377,6 +377,28 @@ TEST_F(TestDefaultStateMachine, call_transitions_without_code) { EXPECT_EQ(finalized.id(), State::PRIMARY_STATE_FINALIZED); } +TEST_F(TestDefaultStateMachine, get_current_state_thread_safety) { + auto test_node = std::make_shared("testnode"); + test_node->trigger_transition(lifecycle_msgs::msg::Transition::TRANSITION_CONFIGURE); + + const auto check_state_fn = [](std::shared_ptr node) + { + std::size_t count = 0; + while (count < 100000) { + node->get_current_state().id(); + count++; + } + }; + + // Call get_current_state() on the same node repeatedly from two different threads. + std::thread thread_object_1(check_state_fn, test_node); + std::thread thread_object_2(check_state_fn, test_node); + + // Test has succeeded if both threads finish without exceptions. + thread_object_1.join(); + thread_object_2.join(); +} + TEST_F(TestDefaultStateMachine, good_mood) { auto test_node = std::make_shared>("testnode"); From 52327dd3a3b0fc47446ce86ecaf16fdde4fc4a60 Mon Sep 17 00:00:00 2001 From: Audrow Nash Date: Mon, 17 Jul 2023 22:40:27 +0000 Subject: [PATCH 20/98] 16.0.5 Signed-off-by: Audrow Nash --- rclcpp/CHANGELOG.rst | 7 +++++++ rclcpp/package.xml | 2 +- rclcpp_action/CHANGELOG.rst | 3 +++ rclcpp_action/package.xml | 2 +- rclcpp_components/CHANGELOG.rst | 3 +++ rclcpp_components/package.xml | 2 +- rclcpp_lifecycle/CHANGELOG.rst | 9 +++++++++ rclcpp_lifecycle/package.xml | 2 +- 8 files changed, 26 insertions(+), 4 deletions(-) diff --git a/rclcpp/CHANGELOG.rst b/rclcpp/CHANGELOG.rst index 27176c0305..b8bbb988f8 100644 --- a/rclcpp/CHANGELOG.rst +++ b/rclcpp/CHANGELOG.rst @@ -2,6 +2,13 @@ Changelog for package rclcpp ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +16.0.5 (2023-07-17) +------------------- +* warning: comparison of integer expressions of different signedness (`#2219 `_) (`#2223 `_) +* Trigger the intraprocess guard condition with data (`#2164 `_) (`#2167 `_) +* Implement validity checks for rclcpp::Clock (`#2040 `_) (`#2210 `_) +* Contributors: Tomoya Fujita, mergify[bot] + 16.0.4 (2023-04-25) ------------------- * use allocator via init_options argument. (`#2129 `_) (`#2131 `_) diff --git a/rclcpp/package.xml b/rclcpp/package.xml index 840faae35f..480c1d91f5 100644 --- a/rclcpp/package.xml +++ b/rclcpp/package.xml @@ -2,7 +2,7 @@ rclcpp - 16.0.4 + 16.0.5 The ROS client library in C++. Ivan Paunovic Jacob Perron diff --git a/rclcpp_action/CHANGELOG.rst b/rclcpp_action/CHANGELOG.rst index 41b7f671dc..b703108895 100644 --- a/rclcpp_action/CHANGELOG.rst +++ b/rclcpp_action/CHANGELOG.rst @@ -3,6 +3,9 @@ Changelog for package rclcpp_action ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +16.0.5 (2023-07-17) +------------------- + 16.0.4 (2023-04-25) ------------------- * Revert "Revert "extract the result response before the callback is issued. (`#2133 `_)" (`#2148 `_)" (`#2152 `_) diff --git a/rclcpp_action/package.xml b/rclcpp_action/package.xml index ce9668287b..8b6d6dc3cc 100644 --- a/rclcpp_action/package.xml +++ b/rclcpp_action/package.xml @@ -2,7 +2,7 @@ rclcpp_action - 16.0.4 + 16.0.5 Adds action APIs for C++. Ivan Paunovic Jacob Perron diff --git a/rclcpp_components/CHANGELOG.rst b/rclcpp_components/CHANGELOG.rst index 403197e020..e595370aa9 100644 --- a/rclcpp_components/CHANGELOG.rst +++ b/rclcpp_components/CHANGELOG.rst @@ -2,6 +2,9 @@ Changelog for package rclcpp_components ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +16.0.5 (2023-07-17) +------------------- + 16.0.4 (2023-04-25) ------------------- diff --git a/rclcpp_components/package.xml b/rclcpp_components/package.xml index 8b0822aa8b..25b5614eb8 100644 --- a/rclcpp_components/package.xml +++ b/rclcpp_components/package.xml @@ -2,7 +2,7 @@ rclcpp_components - 16.0.4 + 16.0.5 Package containing tools for dynamically loadable components Ivan Paunovic Jacob Perron diff --git a/rclcpp_lifecycle/CHANGELOG.rst b/rclcpp_lifecycle/CHANGELOG.rst index e1c58a03d5..e881968e54 100644 --- a/rclcpp_lifecycle/CHANGELOG.rst +++ b/rclcpp_lifecycle/CHANGELOG.rst @@ -3,6 +3,15 @@ Changelog for package rclcpp_lifecycle ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +16.0.5 (2023-07-17) +------------------- +* Fix thread safety in LifecycleNode::get_current_state() for Humble (`#2183 `_) + * add initially-failing test case + * apply changes to LifecycleNodeInterfaceImpl from `#1756 `_ + * add static member to State for managing state_handle\_ access + * allow parallel read access in MutexMap +* Contributors: Joseph Schornak + 16.0.4 (2023-04-25) ------------------- diff --git a/rclcpp_lifecycle/package.xml b/rclcpp_lifecycle/package.xml index b230eaa82f..a7345ffa95 100644 --- a/rclcpp_lifecycle/package.xml +++ b/rclcpp_lifecycle/package.xml @@ -2,7 +2,7 @@ rclcpp_lifecycle - 16.0.4 + 16.0.5 Package containing a prototype for lifecycle implementation Ivan Paunovic Jacob Perron From ec4d00e405fae4f739655d8107e0f7c90ec3163d Mon Sep 17 00:00:00 2001 From: Tony Najjar Date: Mon, 24 Jul 2023 05:59:20 +0200 Subject: [PATCH 21/98] Switch lifecycle to use the RCLCPP macros Signed-off-by: Tony Najjar (#2234) Signed-off-by: Tony Najjar --- rclcpp_lifecycle/src/lifecycle_node.cpp | 2 +- .../src/lifecycle_node_interface_impl.hpp | 38 +++++++++++++------ 2 files changed, 28 insertions(+), 12 deletions(-) diff --git a/rclcpp_lifecycle/src/lifecycle_node.cpp b/rclcpp_lifecycle/src/lifecycle_node.cpp index 06378b594b..333edf41b8 100644 --- a/rclcpp_lifecycle/src/lifecycle_node.cpp +++ b/rclcpp_lifecycle/src/lifecycle_node.cpp @@ -107,7 +107,7 @@ LifecycleNode::LifecycleNode( )), node_waitables_(new rclcpp::node_interfaces::NodeWaitables(node_base_.get())), node_options_(options), - impl_(new LifecycleNodeInterfaceImpl(node_base_, node_services_)) + impl_(new LifecycleNodeInterfaceImpl(node_base_, node_services_, node_logging_)) { impl_->init(enable_communication_interface); diff --git a/rclcpp_lifecycle/src/lifecycle_node_interface_impl.hpp b/rclcpp_lifecycle/src/lifecycle_node_interface_impl.hpp index 60570d4059..13110d29c7 100644 --- a/rclcpp_lifecycle/src/lifecycle_node_interface_impl.hpp +++ b/rclcpp_lifecycle/src/lifecycle_node_interface_impl.hpp @@ -39,6 +39,7 @@ #include "rcl_lifecycle/transition_map.h" #include "rclcpp/node_interfaces/node_base_interface.hpp" +#include "rclcpp/node_interfaces/node_logging_interface.hpp" #include "rclcpp/node_interfaces/node_services_interface.hpp" #include "rcutils/logging_macros.h" @@ -57,9 +58,11 @@ class LifecycleNode::LifecycleNodeInterfaceImpl public: LifecycleNodeInterfaceImpl( std::shared_ptr node_base_interface, - std::shared_ptr node_services_interface) + std::shared_ptr node_services_interface, + std::shared_ptr node_logging_interface) : node_base_interface_(node_base_interface), - node_services_interface_(node_services_interface) + node_services_interface_(node_services_interface), + node_logging_interface_(node_logging_interface) {} ~LifecycleNodeInterfaceImpl() @@ -71,8 +74,8 @@ class LifecycleNode::LifecycleNodeInterfaceImpl ret = rcl_lifecycle_state_machine_fini(&state_machine_, node_handle); } if (ret != RCL_RET_OK) { - RCUTILS_LOG_FATAL_NAMED( - "rclcpp_lifecycle", + RCLCPP_FATAL( + node_logging_interface_->get_logger(), "failed to destroy rcl_state_machine"); } } @@ -402,7 +405,8 @@ class LifecycleNode::LifecycleNodeInterfaceImpl { std::lock_guard lock(state_machine_mutex_); if (rcl_lifecycle_state_machine_is_initialized(&state_machine_) != RCL_RET_OK) { - RCUTILS_LOG_ERROR( + RCLCPP_ERROR( + node_logging_interface_->get_logger(), "Unable to change state for state machine for %s: %s", node_base_interface_->get_name(), rcl_get_error_string().str); return RCL_RET_ERROR; @@ -414,7 +418,8 @@ class LifecycleNode::LifecycleNodeInterfaceImpl rcl_lifecycle_trigger_transition_by_id( &state_machine_, transition_id, publish_update) != RCL_RET_OK) { - RCUTILS_LOG_ERROR( + RCLCPP_ERROR( + node_logging_interface_->get_logger(), "Unable to start transition %u from current state %s: %s", transition_id, state_machine_.current_state->label, rcl_get_error_string().str); rcutils_reset_error(); @@ -443,7 +448,8 @@ class LifecycleNode::LifecycleNodeInterfaceImpl rcl_lifecycle_trigger_transition_by_label( &state_machine_, transition_label, publish_update) != RCL_RET_OK) { - RCUTILS_LOG_ERROR( + RCLCPP_ERROR( + node_logging_interface_->get_logger(), "Failed to finish transition %u. Current state is now: %s (%s)", transition_id, state_machine_.current_state->label, rcl_get_error_string().str); rcutils_reset_error(); @@ -455,7 +461,9 @@ class LifecycleNode::LifecycleNodeInterfaceImpl // error handling ?! // TODO(karsten1987): iterate over possible ret value if (cb_return_code == node_interfaces::LifecycleNodeInterface::CallbackReturn::ERROR) { - RCUTILS_LOG_WARN("Error occurred while doing error handling."); + RCLCPP_WARN( + node_logging_interface_->get_logger(), + "Error occurred while doing error handling."); auto error_cb_code = execute_callback(current_state_id, initial_state); auto error_cb_label = get_label_for_return_code(error_cb_code); @@ -464,7 +472,9 @@ class LifecycleNode::LifecycleNodeInterfaceImpl rcl_lifecycle_trigger_transition_by_label( &state_machine_, error_cb_label, publish_update) != RCL_RET_OK) { - RCUTILS_LOG_ERROR("Failed to call cleanup on error state: %s", rcl_get_error_string().str); + RCLCPP_ERROR( + node_logging_interface_->get_logger(), + "Failed to call cleanup on error state: %s", rcl_get_error_string().str); rcutils_reset_error(); return RCL_RET_ERROR; } @@ -487,8 +497,12 @@ class LifecycleNode::LifecycleNodeInterfaceImpl try { cb_success = callback(State(previous_state)); } catch (const std::exception & e) { - RCUTILS_LOG_ERROR("Caught exception in callback for transition %d", it->first); - RCUTILS_LOG_ERROR("Original error: %s", e.what()); + RCLCPP_ERROR( + node_logging_interface_->get_logger(), + "Caught exception in callback for transition %d", it->first); + RCLCPP_ERROR( + node_logging_interface_->get_logger(), + "Original error: %s", e.what()); cb_success = node_interfaces::LifecycleNodeInterface::CallbackReturn::ERROR; } } @@ -576,6 +590,7 @@ class LifecycleNode::LifecycleNodeInterfaceImpl using NodeBasePtr = std::shared_ptr; using NodeServicesPtr = std::shared_ptr; + using NodeLoggingPtr = std::shared_ptr; using ChangeStateSrvPtr = std::shared_ptr>; using GetStateSrvPtr = std::shared_ptr>; using GetAvailableStatesSrvPtr = @@ -587,6 +602,7 @@ class LifecycleNode::LifecycleNodeInterfaceImpl NodeBasePtr node_base_interface_; NodeServicesPtr node_services_interface_; + NodeLoggingPtr node_logging_interface_; ChangeStateSrvPtr srv_change_state_; GetStateSrvPtr srv_get_state_; GetAvailableStatesSrvPtr srv_get_available_states_; From 689e510cf0d58257752127750b6460f1f66872c5 Mon Sep 17 00:00:00 2001 From: "mergify[bot]" <37929162+mergify[bot]@users.noreply.github.com> Date: Fri, 1 Sep 2023 08:25:35 -0700 Subject: [PATCH 22/98] Do not crash Executor when send_response fails due to client failure. (#2276) (#2280) * Do not crash Executor when send_response fails due to client failure. Related to https://github.com/ros2/ros2/issues/1253 It is not sane that a faulty client can crash our service Executor, as discussed in the referred issue, if the client is not setup properly, send_response may return RCL_RET_TIMEOUT, we should not throw an error in this case. Signed-off-by: Zang MingJie * Update rclcpp/include/rclcpp/service.hpp Co-authored-by: Tomoya Fujita Signed-off-by: Zang MingJie * address review comments. Signed-off-by: Tomoya Fujita --------- Signed-off-by: Zang MingJie Signed-off-by: Tomoya Fujita Co-authored-by: Zang MingJie (cherry picked from commit fbe8f28cd13710c5c643a4e7149e509f3a952677) Co-authored-by: Tomoya Fujita --- rclcpp/include/rclcpp/service.hpp | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/rclcpp/include/rclcpp/service.hpp b/rclcpp/include/rclcpp/service.hpp index 3f500eaa09..1b69d4a89e 100644 --- a/rclcpp/include/rclcpp/service.hpp +++ b/rclcpp/include/rclcpp/service.hpp @@ -481,6 +481,14 @@ class Service { rcl_ret_t ret = rcl_send_response(get_service_handle().get(), &req_id, &response); + if (ret == RCL_RET_TIMEOUT) { + RCLCPP_WARN( + node_logger_.get_child("rclcpp"), + "failed to send response to %s (timeout): %s", + this->get_service_name(), rcl_get_error_string().str); + rcl_reset_error(); + return; + } if (ret != RCL_RET_OK) { rclcpp::exceptions::throw_from_rcl_error(ret, "failed to send response"); } From 2ae824e8e8e54bff08203eea244a3bb90c642136 Mon Sep 17 00:00:00 2001 From: "mergify[bot]" <37929162+mergify[bot]@users.noreply.github.com> Date: Wed, 6 Sep 2023 09:18:32 -0400 Subject: [PATCH 23/98] check thread whether joinable before join (#2019) (#2275) Signed-off-by: uupks (cherry picked from commit b9b1468d15c7ddc697c079e6934d54f183294280) Co-authored-by: uupks --- rclcpp/src/rclcpp/signal_handler.cpp | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/rclcpp/src/rclcpp/signal_handler.cpp b/rclcpp/src/rclcpp/signal_handler.cpp index 35ceb56781..7085c63bdf 100644 --- a/rclcpp/src/rclcpp/signal_handler.cpp +++ b/rclcpp/src/rclcpp/signal_handler.cpp @@ -191,7 +191,9 @@ SignalHandler::uninstall() signal_handlers_options_ = SignalHandlerOptions::None; RCLCPP_DEBUG(get_logger(), "SignalHandler::uninstall(): notifying deferred signal handler"); notify_signal_handler(); - signal_handler_thread_.join(); + if (signal_handler_thread_.joinable()) { + signal_handler_thread_.join(); + } teardown_wait_for_signal(); } catch (...) { installed_.exchange(true); From 724b4588ecbab3d2bb1ce4fbf800defa0e5a842e Mon Sep 17 00:00:00 2001 From: "mergify[bot]" <37929162+mergify[bot]@users.noreply.github.com> Date: Sat, 16 Sep 2023 11:43:32 -0700 Subject: [PATCH 24/98] Topic correct typeadapter deduction (#2294) (#2297) * fix TypeAdapter deduction Signed-off-by: Chen Lihui (cherry picked from commit 5e152d77d8144c074894de9a5bc7025aac7f5813) Co-authored-by: Chen Lihui --- .../experimental/intra_process_manager.hpp | 4 +- .../test_publisher_with_type_adapter.cpp | 57 ++++++++++++++----- 2 files changed, 45 insertions(+), 16 deletions(-) diff --git a/rclcpp/include/rclcpp/experimental/intra_process_manager.hpp b/rclcpp/include/rclcpp/experimental/intra_process_manager.hpp index d8054be2d4..62144ea9bd 100644 --- a/rclcpp/include/rclcpp/experimental/intra_process_manager.hpp +++ b/rclcpp/include/rclcpp/experimental/intra_process_manager.hpp @@ -479,13 +479,13 @@ class IntraProcessManager "subscription use different allocator types, which is not supported"); } - if constexpr (rclcpp::TypeAdapter::is_specialized::value) { + if constexpr (rclcpp::TypeAdapter::is_specialized::value) { ROSMessageTypeAllocator ros_message_alloc(allocator); auto ptr = ros_message_alloc.allocate(1); ros_message_alloc.construct(ptr); ROSMessageTypeDeleter deleter; allocator::set_allocator_for_deleter(&deleter, &allocator); - rclcpp::TypeAdapter::convert_to_ros_message(*message, *ptr); + rclcpp::TypeAdapter::convert_to_ros_message(*message, *ptr); auto ros_msg = std::unique_ptr(ptr, deleter); ros_message_subscription->provide_intra_process_message(std::move(ros_msg)); } else { diff --git a/rclcpp/test/rclcpp/test_publisher_with_type_adapter.cpp b/rclcpp/test/rclcpp/test_publisher_with_type_adapter.cpp index c041d86aee..e96e1e3a38 100644 --- a/rclcpp/test/rclcpp/test_publisher_with_type_adapter.cpp +++ b/rclcpp/test/rclcpp/test_publisher_with_type_adapter.cpp @@ -152,33 +152,54 @@ TEST_F(TestPublisher, conversion_exception_is_passed_up) { } } +using UseTakeSharedMethod = bool; +class TestPublisherFixture + : public TestPublisher, + public ::testing::WithParamInterface +{ +}; + /* * Testing that publisher sends type adapted types and ROS message types with intra proccess communications. */ -TEST_F( - TestPublisher, +TEST_P( + TestPublisherFixture, check_type_adapted_message_is_sent_and_received_intra_process) { using StringTypeAdapter = rclcpp::TypeAdapter; const std::string message_data = "Message Data"; const std::string topic_name = "topic_name"; bool is_received; - auto callback = - [message_data, &is_received]( - const rclcpp::msg::String::ConstSharedPtr msg, - const rclcpp::MessageInfo & message_info - ) -> void - { - is_received = true; - ASSERT_STREQ(message_data.c_str(), msg->data.c_str()); - ASSERT_TRUE(message_info.get_rmw_message_info().from_intra_process); - }; - auto node = rclcpp::Node::make_shared( "test_intra_process", rclcpp::NodeOptions().use_intra_process_comms(true)); auto pub = node->create_publisher(topic_name, 10); - auto sub = node->create_subscription(topic_name, 1, callback); + rclcpp::Subscription::SharedPtr sub; + if (GetParam()) { + auto callback = + [message_data, &is_received]( + const rclcpp::msg::String::ConstSharedPtr msg, + const rclcpp::MessageInfo & message_info + ) -> void + { + is_received = true; + ASSERT_STREQ(message_data.c_str(), msg->data.c_str()); + ASSERT_TRUE(message_info.get_rmw_message_info().from_intra_process); + }; + sub = node->create_subscription(topic_name, 1, callback); + } else { + auto callback_unique = + [message_data, &is_received]( + rclcpp::msg::String::UniquePtr msg, + const rclcpp::MessageInfo & message_info + ) -> void + { + is_received = true; + ASSERT_STREQ(message_data.c_str(), msg->data.c_str()); + ASSERT_TRUE(message_info.get_rmw_message_info().from_intra_process); + }; + sub = node->create_subscription(topic_name, 1, callback_unique); + } auto wait_for_message_to_be_received = [&is_received, &node]() { rclcpp::executors::SingleThreadedExecutor executor; @@ -239,6 +260,14 @@ TEST_F( } } +INSTANTIATE_TEST_SUITE_P( + TestPublisherFixtureWithParam, + TestPublisherFixture, + ::testing::Values( + true, // use take shared method + false // not use take shared method +)); + /* * Testing that publisher sends type adapted types and ROS message types with inter proccess communications. */ From 0f6b5449f66f131735a423be4a84d6f14751d3b2 Mon Sep 17 00:00:00 2001 From: Audrow Nash Date: Tue, 19 Sep 2023 13:39:23 +0000 Subject: [PATCH 25/98] 16.0.6 Signed-off-by: Audrow Nash --- rclcpp/CHANGELOG.rst | 7 +++++++ rclcpp/package.xml | 2 +- rclcpp_action/CHANGELOG.rst | 3 +++ rclcpp_action/package.xml | 2 +- rclcpp_components/CHANGELOG.rst | 3 +++ rclcpp_components/package.xml | 2 +- rclcpp_lifecycle/CHANGELOG.rst | 5 +++++ rclcpp_lifecycle/package.xml | 2 +- 8 files changed, 22 insertions(+), 4 deletions(-) diff --git a/rclcpp/CHANGELOG.rst b/rclcpp/CHANGELOG.rst index b8bbb988f8..9bf7a2b697 100644 --- a/rclcpp/CHANGELOG.rst +++ b/rclcpp/CHANGELOG.rst @@ -2,6 +2,13 @@ Changelog for package rclcpp ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +16.0.6 (2023-09-19) +------------------- +* Topic correct typeadapter deduction (`#2294 `_) (`#2297 `_) +* check thread whether joinable before join (`#2019 `_) (`#2275 `_) +* Do not crash Executor when send_response fails due to client failure. (`#2276 `_) (`#2280 `_) +* Contributors: mergify[bot] + 16.0.5 (2023-07-17) ------------------- * warning: comparison of integer expressions of different signedness (`#2219 `_) (`#2223 `_) diff --git a/rclcpp/package.xml b/rclcpp/package.xml index 480c1d91f5..573dceb812 100644 --- a/rclcpp/package.xml +++ b/rclcpp/package.xml @@ -2,7 +2,7 @@ rclcpp - 16.0.5 + 16.0.6 The ROS client library in C++. Ivan Paunovic Jacob Perron diff --git a/rclcpp_action/CHANGELOG.rst b/rclcpp_action/CHANGELOG.rst index b703108895..9f35a5224a 100644 --- a/rclcpp_action/CHANGELOG.rst +++ b/rclcpp_action/CHANGELOG.rst @@ -3,6 +3,9 @@ Changelog for package rclcpp_action ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +16.0.6 (2023-09-19) +------------------- + 16.0.5 (2023-07-17) ------------------- diff --git a/rclcpp_action/package.xml b/rclcpp_action/package.xml index 8b6d6dc3cc..f7bb86f956 100644 --- a/rclcpp_action/package.xml +++ b/rclcpp_action/package.xml @@ -2,7 +2,7 @@ rclcpp_action - 16.0.5 + 16.0.6 Adds action APIs for C++. Ivan Paunovic Jacob Perron diff --git a/rclcpp_components/CHANGELOG.rst b/rclcpp_components/CHANGELOG.rst index e595370aa9..0d4b589073 100644 --- a/rclcpp_components/CHANGELOG.rst +++ b/rclcpp_components/CHANGELOG.rst @@ -2,6 +2,9 @@ Changelog for package rclcpp_components ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +16.0.6 (2023-09-19) +------------------- + 16.0.5 (2023-07-17) ------------------- diff --git a/rclcpp_components/package.xml b/rclcpp_components/package.xml index 25b5614eb8..e66626520a 100644 --- a/rclcpp_components/package.xml +++ b/rclcpp_components/package.xml @@ -2,7 +2,7 @@ rclcpp_components - 16.0.5 + 16.0.6 Package containing tools for dynamically loadable components Ivan Paunovic Jacob Perron diff --git a/rclcpp_lifecycle/CHANGELOG.rst b/rclcpp_lifecycle/CHANGELOG.rst index e881968e54..9ac2802693 100644 --- a/rclcpp_lifecycle/CHANGELOG.rst +++ b/rclcpp_lifecycle/CHANGELOG.rst @@ -3,6 +3,11 @@ Changelog for package rclcpp_lifecycle ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +16.0.6 (2023-09-19) +------------------- +* Switch lifecycle to use the RCLCPP macros Signed-off-by: Tony Najjar (`#2234 `_) +* Contributors: Tony Najjar + 16.0.5 (2023-07-17) ------------------- * Fix thread safety in LifecycleNode::get_current_state() for Humble (`#2183 `_) diff --git a/rclcpp_lifecycle/package.xml b/rclcpp_lifecycle/package.xml index a7345ffa95..5517fafa7d 100644 --- a/rclcpp_lifecycle/package.xml +++ b/rclcpp_lifecycle/package.xml @@ -2,7 +2,7 @@ rclcpp_lifecycle - 16.0.5 + 16.0.6 Package containing a prototype for lifecycle implementation Ivan Paunovic Jacob Perron From 37f38e30a99f84a2a4b6a41d9119b5fff75c989a Mon Sep 17 00:00:00 2001 From: "mergify[bot]" <37929162+mergify[bot]@users.noreply.github.com> Date: Wed, 27 Sep 2023 15:50:23 -0400 Subject: [PATCH 26/98] Fix C++20 allocator construct deprecation (#2292) (#2319) Signed-off-by: Guilherme Rodrigues (cherry picked from commit fa732b9ee8000714831568e14486099f2714003d) Co-authored-by: AiVerisimilitude <133206333+AiVerisimilitude@users.noreply.github.com> --- rclcpp/include/rclcpp/experimental/intra_process_manager.hpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/rclcpp/include/rclcpp/experimental/intra_process_manager.hpp b/rclcpp/include/rclcpp/experimental/intra_process_manager.hpp index 62144ea9bd..a9a35ff624 100644 --- a/rclcpp/include/rclcpp/experimental/intra_process_manager.hpp +++ b/rclcpp/include/rclcpp/experimental/intra_process_manager.hpp @@ -481,8 +481,8 @@ class IntraProcessManager if constexpr (rclcpp::TypeAdapter::is_specialized::value) { ROSMessageTypeAllocator ros_message_alloc(allocator); - auto ptr = ros_message_alloc.allocate(1); - ros_message_alloc.construct(ptr); + auto ptr = ROSMessageTypeAllocatorTraits::allocate(ros_message_alloc, 1); + ROSMessageTypeAllocatorTraits::construct(ros_message_alloc, ptr); ROSMessageTypeDeleter deleter; allocator::set_allocator_for_deleter(&deleter, &allocator); rclcpp::TypeAdapter::convert_to_ros_message(*message, *ptr); From adfc5464083470b80e4978a5f400c83e7616da73 Mon Sep 17 00:00:00 2001 From: "mergify[bot]" <37929162+mergify[bot]@users.noreply.github.com> Date: Wed, 4 Oct 2023 01:03:21 -0700 Subject: [PATCH 27/98] Update SignalHandler get_global_signal_handler to avoid complex types in static memory (#2316) (#2321) * Update SignalHandler get_global_signal_handler to avoid complex types in static memory This was flagged by msan as a problem. There's a description of why this is a potential problem here: https://google.github.io/styleguide/cppguide.html#Static_and_Global_Variables Signed-off-by: Tully Foote Co-authored-by: William Woodall (cherry picked from commit 7c1143dc1502d5dda99a3dfa17124a72dbaea90c) --- rclcpp/src/rclcpp/signal_handler.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/rclcpp/src/rclcpp/signal_handler.cpp b/rclcpp/src/rclcpp/signal_handler.cpp index 7085c63bdf..cf26d06df4 100644 --- a/rclcpp/src/rclcpp/signal_handler.cpp +++ b/rclcpp/src/rclcpp/signal_handler.cpp @@ -113,7 +113,7 @@ SignalHandler::get_logger() SignalHandler & SignalHandler::get_global_signal_handler() { - static SignalHandler signal_handler; + static SignalHandler & signal_handler = *new SignalHandler(); return signal_handler; } From 8709146df87ab860c88f03a83f6df7d0347d0085 Mon Sep 17 00:00:00 2001 From: "mergify[bot]" <37929162+mergify[bot]@users.noreply.github.com> Date: Thu, 26 Oct 2023 07:45:47 -0700 Subject: [PATCH 28/98] address rate related flaky tests. (#2329) (#2342) Signed-off-by: Tomoya Fujita (cherry picked from commit fcbe64cff4bea3109531254ceb2955dc4b1bb320) Co-authored-by: Tomoya Fujita --- rclcpp/src/rclcpp/context.cpp | 2 +- rclcpp/test/rclcpp/test_rate.cpp | 20 +++++++++++++++++--- 2 files changed, 18 insertions(+), 4 deletions(-) diff --git a/rclcpp/src/rclcpp/context.cpp b/rclcpp/src/rclcpp/context.cpp index b9410ca089..33bd0bf0b9 100644 --- a/rclcpp/src/rclcpp/context.cpp +++ b/rclcpp/src/rclcpp/context.cpp @@ -497,7 +497,7 @@ Context::sleep_for(const std::chrono::nanoseconds & nanoseconds) std::unique_lock lock(interrupt_mutex_); auto start = std::chrono::steady_clock::now(); // this will release the lock while waiting - interrupt_condition_variable_.wait_for(lock, nanoseconds); + interrupt_condition_variable_.wait_for(lock, time_left); time_left -= std::chrono::steady_clock::now() - start; } } while (time_left > std::chrono::nanoseconds::zero() && this->is_valid()); diff --git a/rclcpp/test/rclcpp/test_rate.cpp b/rclcpp/test/rclcpp/test_rate.cpp index d6608d59f6..7120805429 100644 --- a/rclcpp/test/rclcpp/test_rate.cpp +++ b/rclcpp/test/rclcpp/test_rate.cpp @@ -18,10 +18,24 @@ #include "rclcpp/rate.hpp" +class TestRate : public ::testing::Test +{ +public: + void SetUp() + { + rclcpp::init(0, nullptr); + } + + void TearDown() + { + rclcpp::shutdown(); + } +}; + /* Basic tests for the Rate and WallRate classes. */ -TEST(TestRate, rate_basics) { +TEST_F(TestRate, rate_basics) { auto period = std::chrono::milliseconds(1000); auto offset = std::chrono::milliseconds(500); auto epsilon = std::chrono::milliseconds(100); @@ -61,7 +75,7 @@ TEST(TestRate, rate_basics) { ASSERT_TRUE(epsilon > delta); } -TEST(TestRate, wall_rate_basics) { +TEST_F(TestRate, wall_rate_basics) { auto period = std::chrono::milliseconds(100); auto offset = std::chrono::milliseconds(50); auto epsilon = std::chrono::milliseconds(1); @@ -101,7 +115,7 @@ TEST(TestRate, wall_rate_basics) { EXPECT_GT(epsilon, delta); } -TEST(TestRate, from_double) { +TEST_F(TestRate, from_double) { { rclcpp::WallRate rate(1.0); EXPECT_EQ(std::chrono::seconds(1), rate.period()); From c1bf0d382eaf6dfe8429020b07611ffb6d552a97 Mon Sep 17 00:00:00 2001 From: "mergify[bot]" <37929162+mergify[bot]@users.noreply.github.com> Date: Tue, 31 Oct 2023 08:17:51 -0700 Subject: [PATCH 29/98] Add missing 'enable_rosout' comments (#2345) (#2347) Signed-off-by: Jiaqi Li (cherry picked from commit fff009a75100f2afd8ef1c3863620bf5ebe67708) Co-authored-by: Jiaqi Li --- rclcpp/include/rclcpp/node_options.hpp | 1 + 1 file changed, 1 insertion(+) diff --git a/rclcpp/include/rclcpp/node_options.hpp b/rclcpp/include/rclcpp/node_options.hpp index d9735357dd..53bdedeaab 100644 --- a/rclcpp/include/rclcpp/node_options.hpp +++ b/rclcpp/include/rclcpp/node_options.hpp @@ -42,6 +42,7 @@ class NodeOptions * - arguments = {} * - parameter_overrides = {} * - use_global_arguments = true + * - enable_rosout = true * - use_intra_process_comms = false * - enable_topic_statistics = false * - start_parameter_services = true From 24f059c5aa636c54fc82daedf9dd9464c6ea3578 Mon Sep 17 00:00:00 2001 From: "mergify[bot]" <37929162+mergify[bot]@users.noreply.github.com> Date: Fri, 10 Nov 2023 09:00:03 -0500 Subject: [PATCH 30/98] Disable the loaned messages inside the executor. (backport #2335) (#2364) * Disable the loaned messages inside the executor. (#2335) * Disable the loaned messages inside the executor. They are currently unsafe to use; see the comment in the commit for more information. Signed-off-by: Chris Lalancette (cherry picked from commit f294488e17921034ebbbca75e8604a56684874e7) --- rclcpp/src/rclcpp/executor.cpp | 5 +++++ rclcpp/src/rclcpp/subscription_base.cpp | 15 ++++++++++++++- 2 files changed, 19 insertions(+), 1 deletion(-) diff --git a/rclcpp/src/rclcpp/executor.cpp b/rclcpp/src/rclcpp/executor.cpp index 401beb0a73..a406cb6e8e 100644 --- a/rclcpp/src/rclcpp/executor.cpp +++ b/rclcpp/src/rclcpp/executor.cpp @@ -646,6 +646,11 @@ Executor::execute_subscription(rclcpp::SubscriptionBase::SharedPtr subscription) subscription->get_topic_name(), [&]() {return subscription->take_type_erased(message.get(), message_info);}, [&]() {subscription->handle_message(message, message_info);}); + // TODO(clalancette): In the case that the user is using the MessageMemoryPool, + // and they take a shared_ptr reference to the message in the callback, this can + // inadvertently return the message to the pool when the user is still using it. + // This is a bug that needs to be fixed in the pool, and we should probably have + // a custom deleter for the message that actually does the return_message(). subscription->return_message(message); } } diff --git a/rclcpp/src/rclcpp/subscription_base.cpp b/rclcpp/src/rclcpp/subscription_base.cpp index 300f465a41..ee2ec11da0 100644 --- a/rclcpp/src/rclcpp/subscription_base.cpp +++ b/rclcpp/src/rclcpp/subscription_base.cpp @@ -229,7 +229,20 @@ SubscriptionBase::setup_intra_process( bool SubscriptionBase::can_loan_messages() const { - return rcl_subscription_can_loan_messages(subscription_handle_.get()); + bool retval = rcl_subscription_can_loan_messages(subscription_handle_.get()); + if (retval) { + // TODO(clalancette): The loaned message interface is currently not safe to use with + // shared_ptr callbacks. If a user takes a copy of the shared_ptr, it can get freed from + // underneath them via rcl_return_loaned_message_from_subscription(). The correct solution is + // to return the loaned message in a custom deleter, but that needs to be carefully handled + // with locking. Warn the user about this until we fix it. + RCLCPP_WARN_ONCE( + this->node_logger_, + "Loaned messages are only safe with const ref subscription callbacks. " + "If you are using any other kind of subscriptions, " + "set the ROS_DISABLE_LOANED_MESSAGES environment variable to 1 (the default)."); + } + return retval; } rclcpp::Waitable::SharedPtr From 47712ecf5861f05320acb6ba33c1ce461722b935 Mon Sep 17 00:00:00 2001 From: Audrow Nash Date: Mon, 13 Nov 2023 21:57:33 +0000 Subject: [PATCH 31/98] 16.0.7 Signed-off-by: Audrow Nash --- rclcpp/CHANGELOG.rst | 9 +++++++++ rclcpp/package.xml | 2 +- rclcpp_action/CHANGELOG.rst | 3 +++ rclcpp_action/package.xml | 2 +- rclcpp_components/CHANGELOG.rst | 3 +++ rclcpp_components/package.xml | 2 +- rclcpp_lifecycle/CHANGELOG.rst | 3 +++ rclcpp_lifecycle/package.xml | 2 +- 8 files changed, 22 insertions(+), 4 deletions(-) diff --git a/rclcpp/CHANGELOG.rst b/rclcpp/CHANGELOG.rst index 9bf7a2b697..a315b24342 100644 --- a/rclcpp/CHANGELOG.rst +++ b/rclcpp/CHANGELOG.rst @@ -2,6 +2,15 @@ Changelog for package rclcpp ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +16.0.7 (2023-11-13) +------------------- +* Disable the loaned messages inside the executor. (backport `#2335 `_) (`#2364 `_) +* Add missing 'enable_rosout' comments (`#2345 `_) (`#2347 `_) +* address rate related flaky tests. (`#2329 `_) (`#2342 `_) +* Update SignalHandler get_global_signal_handler to avoid complex types in static memory (`#2316 `_) (`#2321 `_) +* Fix C++20 allocator construct deprecation (`#2292 `_) (`#2319 `_) +* Contributors: mergify[bot] + 16.0.6 (2023-09-19) ------------------- * Topic correct typeadapter deduction (`#2294 `_) (`#2297 `_) diff --git a/rclcpp/package.xml b/rclcpp/package.xml index 573dceb812..caccd592e4 100644 --- a/rclcpp/package.xml +++ b/rclcpp/package.xml @@ -2,7 +2,7 @@ rclcpp - 16.0.6 + 16.0.7 The ROS client library in C++. Ivan Paunovic Jacob Perron diff --git a/rclcpp_action/CHANGELOG.rst b/rclcpp_action/CHANGELOG.rst index 9f35a5224a..c0c879ff1e 100644 --- a/rclcpp_action/CHANGELOG.rst +++ b/rclcpp_action/CHANGELOG.rst @@ -3,6 +3,9 @@ Changelog for package rclcpp_action ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +16.0.7 (2023-11-13) +------------------- + 16.0.6 (2023-09-19) ------------------- diff --git a/rclcpp_action/package.xml b/rclcpp_action/package.xml index f7bb86f956..27a3312a31 100644 --- a/rclcpp_action/package.xml +++ b/rclcpp_action/package.xml @@ -2,7 +2,7 @@ rclcpp_action - 16.0.6 + 16.0.7 Adds action APIs for C++. Ivan Paunovic Jacob Perron diff --git a/rclcpp_components/CHANGELOG.rst b/rclcpp_components/CHANGELOG.rst index 0d4b589073..ff896a1f66 100644 --- a/rclcpp_components/CHANGELOG.rst +++ b/rclcpp_components/CHANGELOG.rst @@ -2,6 +2,9 @@ Changelog for package rclcpp_components ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +16.0.7 (2023-11-13) +------------------- + 16.0.6 (2023-09-19) ------------------- diff --git a/rclcpp_components/package.xml b/rclcpp_components/package.xml index e66626520a..893ab8ca48 100644 --- a/rclcpp_components/package.xml +++ b/rclcpp_components/package.xml @@ -2,7 +2,7 @@ rclcpp_components - 16.0.6 + 16.0.7 Package containing tools for dynamically loadable components Ivan Paunovic Jacob Perron diff --git a/rclcpp_lifecycle/CHANGELOG.rst b/rclcpp_lifecycle/CHANGELOG.rst index 9ac2802693..747fcb6cfe 100644 --- a/rclcpp_lifecycle/CHANGELOG.rst +++ b/rclcpp_lifecycle/CHANGELOG.rst @@ -3,6 +3,9 @@ Changelog for package rclcpp_lifecycle ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +16.0.7 (2023-11-13) +------------------- + 16.0.6 (2023-09-19) ------------------- * Switch lifecycle to use the RCLCPP macros Signed-off-by: Tony Najjar (`#2234 `_) diff --git a/rclcpp_lifecycle/package.xml b/rclcpp_lifecycle/package.xml index 5517fafa7d..c03bfec5b7 100644 --- a/rclcpp_lifecycle/package.xml +++ b/rclcpp_lifecycle/package.xml @@ -2,7 +2,7 @@ rclcpp_lifecycle - 16.0.6 + 16.0.7 Package containing a prototype for lifecycle implementation Ivan Paunovic Jacob Perron From 2c8d2aa4532e5d429b53f21eb911cc090e29057a Mon Sep 17 00:00:00 2001 From: "mergify[bot]" <37929162+mergify[bot]@users.noreply.github.com> Date: Fri, 1 Dec 2023 17:05:03 -0500 Subject: [PATCH 32/98] fix(rclcpp_components): increase the service queue sizes in component_container (backport #2363) (#2380) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * fix(rclcpp_components): increase the service queue sizes in component_container (#2363) * Use rmw_qos_profile_t. Humble doesn't support create_service with the rclcpp::QoS object. Signed-off-by: M. Fatih Cırıt (cherry picked from commit 9c098e544ecf191b7c61f63f7f4fac6f6449cedd) Signed-off-by: Chris Lalancette --- rclcpp_components/src/component_manager.cpp | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/rclcpp_components/src/component_manager.cpp b/rclcpp_components/src/component_manager.cpp index 036350a64f..16329a4247 100644 --- a/rclcpp_components/src/component_manager.cpp +++ b/rclcpp_components/src/component_manager.cpp @@ -37,12 +37,16 @@ ComponentManager::ComponentManager( : Node(std::move(node_name), node_options), executor_(executor) { + rmw_qos_profile_t service_qos = rmw_qos_profile_services_default; + service_qos.depth = 200; loadNode_srv_ = create_service( "~/_container/load_node", - std::bind(&ComponentManager::on_load_node, this, _1, _2, _3)); + std::bind(&ComponentManager::on_load_node, this, _1, _2, _3), + service_qos); unloadNode_srv_ = create_service( "~/_container/unload_node", - std::bind(&ComponentManager::on_unload_node, this, _1, _2, _3)); + std::bind(&ComponentManager::on_unload_node, this, _1, _2, _3), + service_qos); listNodes_srv_ = create_service( "~/_container/list_nodes", std::bind(&ComponentManager::on_list_nodes, this, _1, _2, _3)); From f279b707fefc2b602dacd0a97814cb09047f6501 Mon Sep 17 00:00:00 2001 From: gentoo90 Date: Thu, 21 Dec 2023 02:49:31 +0200 Subject: [PATCH 33/98] Add missing stdexcept include (#2186) (#2394) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Øystein Sture Signed-off-by: gentoo90 Co-authored-by: Øystein Sture --- rclcpp/include/rclcpp/context.hpp | 1 + rclcpp/src/rclcpp/logging_mutex.cpp | 1 + 2 files changed, 2 insertions(+) diff --git a/rclcpp/include/rclcpp/context.hpp b/rclcpp/include/rclcpp/context.hpp index 2d8db29d21..1251e58b62 100644 --- a/rclcpp/include/rclcpp/context.hpp +++ b/rclcpp/include/rclcpp/context.hpp @@ -26,6 +26,7 @@ #include #include #include +#include #include "rcl/context.h" #include "rcl/guard_condition.h" diff --git a/rclcpp/src/rclcpp/logging_mutex.cpp b/rclcpp/src/rclcpp/logging_mutex.cpp index 308a21fe73..bbbe9bbeed 100644 --- a/rclcpp/src/rclcpp/logging_mutex.cpp +++ b/rclcpp/src/rclcpp/logging_mutex.cpp @@ -14,6 +14,7 @@ #include #include +#include #include "rcutils/macros.h" From 3594381e04ae882b073875963c843c656fbf24f3 Mon Sep 17 00:00:00 2001 From: "mergify[bot]" <37929162+mergify[bot]@users.noreply.github.com> Date: Thu, 18 Jan 2024 08:39:18 -0500 Subject: [PATCH 34/98] Add missing header required by the rclcpp::NodeOptions type (#2324) (#2407) Signed-off-by: Ignacio Vizzo (cherry picked from commit d6bd8baac5bc050ab31e4e7e8ee8b482fd469c14) Co-authored-by: Ignacio Vizzo --- rclcpp_components/include/rclcpp_components/node_factory.hpp | 1 + 1 file changed, 1 insertion(+) diff --git a/rclcpp_components/include/rclcpp_components/node_factory.hpp b/rclcpp_components/include/rclcpp_components/node_factory.hpp index 67e6cd7331..7b1f2dcae6 100644 --- a/rclcpp_components/include/rclcpp_components/node_factory.hpp +++ b/rclcpp_components/include/rclcpp_components/node_factory.hpp @@ -15,6 +15,7 @@ #ifndef RCLCPP_COMPONENTS__NODE_FACTORY_HPP__ #define RCLCPP_COMPONENTS__NODE_FACTORY_HPP__ +#include "rclcpp/node_options.hpp" #include "rclcpp_components/node_instance_wrapper.hpp" namespace rclcpp_components From 47c977d1bc82fc76dd21f870bcd3ea473eca2f59 Mon Sep 17 00:00:00 2001 From: Audrow Nash Date: Wed, 24 Jan 2024 00:42:12 +0000 Subject: [PATCH 35/98] 16.0.8 Signed-off-by: Audrow Nash --- rclcpp/CHANGELOG.rst | 5 +++++ rclcpp/package.xml | 2 +- rclcpp_action/CHANGELOG.rst | 3 +++ rclcpp_action/package.xml | 2 +- rclcpp_components/CHANGELOG.rst | 6 ++++++ rclcpp_components/package.xml | 2 +- rclcpp_lifecycle/CHANGELOG.rst | 3 +++ rclcpp_lifecycle/package.xml | 2 +- 8 files changed, 21 insertions(+), 4 deletions(-) diff --git a/rclcpp/CHANGELOG.rst b/rclcpp/CHANGELOG.rst index a315b24342..dcf65e8011 100644 --- a/rclcpp/CHANGELOG.rst +++ b/rclcpp/CHANGELOG.rst @@ -2,6 +2,11 @@ Changelog for package rclcpp ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +16.0.8 (2024-01-24) +------------------- +* Add missing stdexcept include (`#2186 `_) (`#2394 `_) +* Contributors: gentoo90 + 16.0.7 (2023-11-13) ------------------- * Disable the loaned messages inside the executor. (backport `#2335 `_) (`#2364 `_) diff --git a/rclcpp/package.xml b/rclcpp/package.xml index caccd592e4..5ad552bb47 100644 --- a/rclcpp/package.xml +++ b/rclcpp/package.xml @@ -2,7 +2,7 @@ rclcpp - 16.0.7 + 16.0.8 The ROS client library in C++. Ivan Paunovic Jacob Perron diff --git a/rclcpp_action/CHANGELOG.rst b/rclcpp_action/CHANGELOG.rst index c0c879ff1e..dec7e9dd8d 100644 --- a/rclcpp_action/CHANGELOG.rst +++ b/rclcpp_action/CHANGELOG.rst @@ -3,6 +3,9 @@ Changelog for package rclcpp_action ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +16.0.8 (2024-01-24) +------------------- + 16.0.7 (2023-11-13) ------------------- diff --git a/rclcpp_action/package.xml b/rclcpp_action/package.xml index 27a3312a31..2a8cececa2 100644 --- a/rclcpp_action/package.xml +++ b/rclcpp_action/package.xml @@ -2,7 +2,7 @@ rclcpp_action - 16.0.7 + 16.0.8 Adds action APIs for C++. Ivan Paunovic Jacob Perron diff --git a/rclcpp_components/CHANGELOG.rst b/rclcpp_components/CHANGELOG.rst index ff896a1f66..3875f10a35 100644 --- a/rclcpp_components/CHANGELOG.rst +++ b/rclcpp_components/CHANGELOG.rst @@ -2,6 +2,12 @@ Changelog for package rclcpp_components ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +16.0.8 (2024-01-24) +------------------- +* Add missing header required by the rclcpp::NodeOptions type (`#2324 `_) (`#2407 `_) +* fix(rclcpp_components): increase the service queue sizes in component_container (backport `#2363 `_) (`#2380 `_) +* Contributors: mergify[bot] + 16.0.7 (2023-11-13) ------------------- diff --git a/rclcpp_components/package.xml b/rclcpp_components/package.xml index 893ab8ca48..fa640a86d0 100644 --- a/rclcpp_components/package.xml +++ b/rclcpp_components/package.xml @@ -2,7 +2,7 @@ rclcpp_components - 16.0.7 + 16.0.8 Package containing tools for dynamically loadable components Ivan Paunovic Jacob Perron diff --git a/rclcpp_lifecycle/CHANGELOG.rst b/rclcpp_lifecycle/CHANGELOG.rst index 747fcb6cfe..a6b2813ead 100644 --- a/rclcpp_lifecycle/CHANGELOG.rst +++ b/rclcpp_lifecycle/CHANGELOG.rst @@ -3,6 +3,9 @@ Changelog for package rclcpp_lifecycle ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +16.0.8 (2024-01-24) +------------------- + 16.0.7 (2023-11-13) ------------------- diff --git a/rclcpp_lifecycle/package.xml b/rclcpp_lifecycle/package.xml index c03bfec5b7..840f1dfea3 100644 --- a/rclcpp_lifecycle/package.xml +++ b/rclcpp_lifecycle/package.xml @@ -2,7 +2,7 @@ rclcpp_lifecycle - 16.0.7 + 16.0.8 Package containing a prototype for lifecycle implementation Ivan Paunovic Jacob Perron From c1cfcb6880c2c70d0d9bf9e717c67143b61faeaf Mon Sep 17 00:00:00 2001 From: Tamaki Nishino Date: Fri, 29 Mar 2024 22:25:53 +0900 Subject: [PATCH 36/98] Fix clang warning: bugprone-use-after-move (#2116) (#2459) Signed-off-by: Mauro Passerino Signed-off-by: Tamaki Nishino Co-authored-by: mauropasse Co-authored-by: Mauro Passerino --- rclcpp/include/rclcpp/experimental/intra_process_manager.hpp | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/rclcpp/include/rclcpp/experimental/intra_process_manager.hpp b/rclcpp/include/rclcpp/experimental/intra_process_manager.hpp index a9a35ff624..cfd82eebcf 100644 --- a/rclcpp/include/rclcpp/experimental/intra_process_manager.hpp +++ b/rclcpp/include/rclcpp/experimental/intra_process_manager.hpp @@ -454,6 +454,8 @@ class IntraProcessManager if (std::next(it) == subscription_ids.end()) { // If this is the last subscription, give up ownership subscription->provide_intra_process_data(std::move(message)); + // Last message delivered, break from for loop + break; } else { // Copy the message since we have additional subscriptions to serve Deleter deleter = message.get_deleter(); @@ -493,6 +495,8 @@ class IntraProcessManager if (std::next(it) == subscription_ids.end()) { // If this is the last subscription, give up ownership ros_message_subscription->provide_intra_process_message(std::move(message)); + // Last message delivered, break from for loop + break; } else { // Copy the message since we have additional subscriptions to serve Deleter deleter = message.get_deleter(); From 4fb589eea5ef128a2325df44f7555cf4bcd6f7b9 Mon Sep 17 00:00:00 2001 From: "mergify[bot]" <37929162+mergify[bot]@users.noreply.github.com> Date: Thu, 4 Apr 2024 16:59:30 -0700 Subject: [PATCH 37/98] address ambiguous auto variable. (#2481) (#2485) Signed-off-by: Tomoya Fujita Signed-off-by: Steve Nogar (cherry picked from commit 3cdb25934ed261c78bdfbcf5ec9f06e0573be81e) Co-authored-by: Tomoya Fujita --- rclcpp/include/rclcpp/client.hpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/rclcpp/include/rclcpp/client.hpp b/rclcpp/include/rclcpp/client.hpp index 9c7f19ffe7..d1751ae120 100644 --- a/rclcpp/include/rclcpp/client.hpp +++ b/rclcpp/include/rclcpp/client.hpp @@ -816,7 +816,7 @@ class Client : public ClientBase "Received invalid sequence number. Ignoring..."); return std::nullopt; } - auto value = std::move(it->second.second); + std::optional value = std::move(it->second.second); this->pending_requests_.erase(request_number); return value; } From 0f9604d1b712b154cad32dfe4b4a6bfed2924436 Mon Sep 17 00:00:00 2001 From: "mergify[bot]" <37929162+mergify[bot]@users.noreply.github.com> Date: Sun, 7 Apr 2024 14:59:24 -0700 Subject: [PATCH 38/98] =?UTF-8?q?call=20shutdown=20in=20LifecycleNode=20dt?= =?UTF-8?q?or=20to=20avoid=20leaving=20the=20device=20in=20un=E2=80=A6=20(?= =?UTF-8?q?#2450)=20(#2491)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * call shutdown in LifecycleNode dtor to avoid leaving the device in unknown state. Signed-off-by: Tomoya Fujita * add test to verify LifecycleNode::shutdown is called on destructor. Signed-off-by: Tomoya Fujita --------- Signed-off-by: Tomoya Fujita (cherry picked from commit 04ea0bb00293387791522590b7347a2282cda290) Co-authored-by: Tomoya Fujita --- rclcpp_lifecycle/src/lifecycle_node.cpp | 16 ++ rclcpp_lifecycle/test/test_lifecycle_node.cpp | 140 ++++++++++++++++++ 2 files changed, 156 insertions(+) diff --git a/rclcpp_lifecycle/src/lifecycle_node.cpp b/rclcpp_lifecycle/src/lifecycle_node.cpp index 333edf41b8..e812bbcedb 100644 --- a/rclcpp_lifecycle/src/lifecycle_node.cpp +++ b/rclcpp_lifecycle/src/lifecycle_node.cpp @@ -133,6 +133,22 @@ LifecycleNode::LifecycleNode( LifecycleNode::~LifecycleNode() { + // shutdown if necessary to avoid leaving the device in unknown state + if (LifecycleNode::get_current_state().id() != + lifecycle_msgs::msg::State::PRIMARY_STATE_FINALIZED) + { + auto ret = rclcpp_lifecycle::node_interfaces::LifecycleNodeInterface::CallbackReturn::ERROR; + auto finalized = LifecycleNode::shutdown(ret); + if (finalized.id() != lifecycle_msgs::msg::State::PRIMARY_STATE_FINALIZED || + ret != rclcpp_lifecycle::node_interfaces::LifecycleNodeInterface::CallbackReturn::SUCCESS) + { + RCLCPP_WARN( + rclcpp::get_logger("rclcpp_lifecycle"), + "Shutdown error in destruction of LifecycleNode: final state(%s)", + finalized.label().c_str()); + } + } + // release sub-interfaces in an order that allows them to consult with node_base during tear-down node_waitables_.reset(); node_time_source_.reset(); diff --git a/rclcpp_lifecycle/test/test_lifecycle_node.cpp b/rclcpp_lifecycle/test/test_lifecycle_node.cpp index 5a3054781b..5fbe733eb2 100644 --- a/rclcpp_lifecycle/test/test_lifecycle_node.cpp +++ b/rclcpp_lifecycle/test/test_lifecycle_node.cpp @@ -435,6 +435,146 @@ TEST_F(TestDefaultStateMachine, bad_mood) { EXPECT_EQ(1u, test_node->number_of_callbacks); } + +TEST_F(TestDefaultStateMachine, shutdown_from_each_primary_state) { + auto success = rclcpp_lifecycle::node_interfaces::LifecycleNodeInterface::CallbackReturn::SUCCESS; + auto reset_key = rclcpp_lifecycle::node_interfaces::LifecycleNodeInterface::CallbackReturn::ERROR; + + // PRIMARY_STATE_UNCONFIGURED to shutdown + { + auto ret = reset_key; + auto test_node = std::make_shared("testnode"); + auto finalized = test_node->shutdown(ret); + EXPECT_EQ(success, ret); + EXPECT_EQ(finalized.id(), State::PRIMARY_STATE_FINALIZED); + } + + // PRIMARY_STATE_INACTIVE to shutdown + { + auto ret = reset_key; + auto test_node = std::make_shared("testnode"); + auto configured = test_node->configure(ret); + EXPECT_EQ(success, ret); + EXPECT_EQ(configured.id(), State::PRIMARY_STATE_INACTIVE); + ret = reset_key; + auto finalized = test_node->shutdown(ret); + EXPECT_EQ(success, ret); + EXPECT_EQ(finalized.id(), State::PRIMARY_STATE_FINALIZED); + } + + // PRIMARY_STATE_ACTIVE to shutdown + { + auto ret = reset_key; + auto test_node = std::make_shared("testnode"); + auto configured = test_node->configure(ret); + EXPECT_EQ(success, ret); + EXPECT_EQ(configured.id(), State::PRIMARY_STATE_INACTIVE); + ret = reset_key; + auto activated = test_node->activate(ret); + EXPECT_EQ(success, ret); + EXPECT_EQ(activated.id(), State::PRIMARY_STATE_ACTIVE); + ret = reset_key; + auto finalized = test_node->shutdown(ret); + EXPECT_EQ(success, ret); + EXPECT_EQ(finalized.id(), State::PRIMARY_STATE_FINALIZED); + } + + // PRIMARY_STATE_FINALIZED to shutdown + { + auto ret = reset_key; + auto test_node = std::make_shared("testnode"); + auto configured = test_node->configure(ret); + EXPECT_EQ(success, ret); + EXPECT_EQ(configured.id(), State::PRIMARY_STATE_INACTIVE); + ret = reset_key; + auto activated = test_node->activate(ret); + EXPECT_EQ(success, ret); + EXPECT_EQ(activated.id(), State::PRIMARY_STATE_ACTIVE); + ret = reset_key; + auto finalized = test_node->shutdown(ret); + EXPECT_EQ(success, ret); + EXPECT_EQ(finalized.id(), State::PRIMARY_STATE_FINALIZED); + ret = reset_key; + auto finalized_again = test_node->shutdown(ret); + EXPECT_EQ(reset_key, ret); + EXPECT_EQ(finalized_again.id(), State::PRIMARY_STATE_FINALIZED); + } +} + +TEST_F(TestDefaultStateMachine, test_shutdown_on_dtor) { + auto success = rclcpp_lifecycle::node_interfaces::LifecycleNodeInterface::CallbackReturn::SUCCESS; + auto reset_key = rclcpp_lifecycle::node_interfaces::LifecycleNodeInterface::CallbackReturn::ERROR; + + bool shutdown_cb_called = false; + auto on_shutdown_callback = + [&shutdown_cb_called](const rclcpp_lifecycle::State &) -> + rclcpp_lifecycle::node_interfaces::LifecycleNodeInterface::CallbackReturn { + shutdown_cb_called = true; + return rclcpp_lifecycle::node_interfaces::LifecycleNodeInterface::CallbackReturn::SUCCESS; + }; + + // PRIMARY_STATE_UNCONFIGURED to shutdown via dtor + shutdown_cb_called = false; + { + auto test_node = std::make_shared("testnode"); + test_node->register_on_shutdown(std::bind(on_shutdown_callback, std::placeholders::_1)); + EXPECT_EQ(State::PRIMARY_STATE_UNCONFIGURED, test_node->get_current_state().id()); + EXPECT_FALSE(shutdown_cb_called); + } + EXPECT_TRUE(shutdown_cb_called); + + // PRIMARY_STATE_INACTIVE to shutdown via dtor + shutdown_cb_called = false; + { + auto ret = reset_key; + auto test_node = std::make_shared("testnode"); + test_node->register_on_shutdown(std::bind(on_shutdown_callback, std::placeholders::_1)); + auto configured = test_node->configure(ret); + EXPECT_EQ(success, ret); + EXPECT_EQ(configured.id(), State::PRIMARY_STATE_INACTIVE); + EXPECT_FALSE(shutdown_cb_called); + } + EXPECT_TRUE(shutdown_cb_called); + + // PRIMARY_STATE_ACTIVE to shutdown via dtor + shutdown_cb_called = false; + { + auto ret = reset_key; + auto test_node = std::make_shared("testnode"); + test_node->register_on_shutdown(std::bind(on_shutdown_callback, std::placeholders::_1)); + auto configured = test_node->configure(ret); + EXPECT_EQ(success, ret); + EXPECT_EQ(configured.id(), State::PRIMARY_STATE_INACTIVE); + ret = reset_key; + auto activated = test_node->activate(ret); + EXPECT_EQ(success, ret); + EXPECT_EQ(activated.id(), State::PRIMARY_STATE_ACTIVE); + EXPECT_FALSE(shutdown_cb_called); + } + EXPECT_TRUE(shutdown_cb_called); + + // PRIMARY_STATE_FINALIZED to shutdown via dtor + shutdown_cb_called = false; + { + auto ret = reset_key; + auto test_node = std::make_shared("testnode"); + test_node->register_on_shutdown(std::bind(on_shutdown_callback, std::placeholders::_1)); + auto configured = test_node->configure(ret); + EXPECT_EQ(success, ret); + EXPECT_EQ(configured.id(), State::PRIMARY_STATE_INACTIVE); + ret = reset_key; + auto activated = test_node->activate(ret); + EXPECT_EQ(success, ret); + EXPECT_EQ(activated.id(), State::PRIMARY_STATE_ACTIVE); + ret = reset_key; + auto finalized = test_node->shutdown(ret); + EXPECT_EQ(success, ret); + EXPECT_EQ(finalized.id(), State::PRIMARY_STATE_FINALIZED); + EXPECT_TRUE(shutdown_cb_called); // should be called already + } + EXPECT_TRUE(shutdown_cb_called); +} + TEST_F(TestDefaultStateMachine, lifecycle_subscriber) { auto test_node = std::make_shared>("testnode"); From 058b54f7c7d99bc146bc95a497c5d95510b54853 Mon Sep 17 00:00:00 2001 From: "mergify[bot]" <37929162+mergify[bot]@users.noreply.github.com> Date: Fri, 3 May 2024 09:32:53 -0700 Subject: [PATCH 39/98] Do not generate the exception when action service response timeout. (#2464) (#2518) * Do not generate the exception when action service response timeout. Signed-off-by: Tomoya Fujita * address review comment. Signed-off-by: Tomoya Fujita --------- Signed-off-by: Tomoya Fujita (cherry picked from commit 6c7764e9681c3e82eae03262f7a595c13d5a3685) Co-authored-by: Tomoya Fujita --- rclcpp_action/src/server.cpp | 36 ++++++++++++++++++++++++++++++++++-- 1 file changed, 34 insertions(+), 2 deletions(-) diff --git a/rclcpp_action/src/server.cpp b/rclcpp_action/src/server.cpp index b0afb9aa50..2ca88185c3 100644 --- a/rclcpp_action/src/server.cpp +++ b/rclcpp_action/src/server.cpp @@ -345,7 +345,16 @@ ServerBase::execute_goal_request_received(std::shared_ptr & data) } if (RCL_RET_OK != ret) { - rclcpp::exceptions::throw_from_rcl_error(ret); + if (ret == RCL_RET_TIMEOUT) { + RCLCPP_WARN( + pimpl_->logger_, + "Failed to send goal response %s (timeout): %s", + to_string(uuid).c_str(), rcl_get_error_string().str); + rcl_reset_error(); + return; + } else { + rclcpp::exceptions::throw_from_rcl_error(ret); + } } const auto status = response_pair.first; @@ -484,6 +493,15 @@ ServerBase::execute_cancel_request_received(std::shared_ptr & data) pimpl_->action_server_.get(), &request_header, response.get()); } + if (ret == RCL_RET_TIMEOUT) { + GoalUUID uuid = request->goal_info.goal_id.uuid; + RCLCPP_WARN( + pimpl_->logger_, + "Failed to send cancel response %s (timeout): %s", + to_string(uuid).c_str(), rcl_get_error_string().str); + rcl_reset_error(); + return; + } if (RCL_RET_OK != ret) { rclcpp::exceptions::throw_from_rcl_error(ret); } @@ -539,6 +557,14 @@ ServerBase::execute_result_request_received(std::shared_ptr & data) std::lock_guard lock(pimpl_->action_server_reentrant_mutex_); rcl_ret_t rcl_ret = rcl_action_send_result_response( pimpl_->action_server_.get(), &request_header, result_response.get()); + if (rcl_ret == RCL_RET_TIMEOUT) { + RCLCPP_WARN( + pimpl_->logger_, + "Failed to send result response %s (timeout): %s", + to_string(uuid).c_str(), rcl_get_error_string().str); + rcl_reset_error(); + return; + } if (RCL_RET_OK != rcl_ret) { rclcpp::exceptions::throw_from_rcl_error(rcl_ret); } @@ -672,7 +698,13 @@ ServerBase::publish_result(const GoalUUID & uuid, std::shared_ptr result_m for (auto & request_header : iter->second) { rcl_ret_t ret = rcl_action_send_result_response( pimpl_->action_server_.get(), &request_header, result_msg.get()); - if (RCL_RET_OK != ret) { + if (ret == RCL_RET_TIMEOUT) { + RCLCPP_WARN( + pimpl_->logger_, + "Failed to send result response %s (timeout): %s", + to_string(uuid).c_str(), rcl_get_error_string().str); + rcl_reset_error(); + } else if (RCL_RET_OK != ret) { rclcpp::exceptions::throw_from_rcl_error(ret); } } From ecf4ac4b2be1c5a84a7fd0d26cf53acd29295296 Mon Sep 17 00:00:00 2001 From: "mergify[bot]" <37929162+mergify[bot]@users.noreply.github.com> Date: Tue, 14 May 2024 07:46:21 -0700 Subject: [PATCH 40/98] Fix logging macros to build with msvc and cpp20 (#2063) (#2529) Signed-off-by: Mateusz Szczygielski Signed-off-by: Mateusz Szczygielski (cherry picked from commit 86335dd4acd91d5dd973c4e4e97014e5e8a916bc) Co-authored-by: Mateusz Szczygielski <112629916+msz-rai@users.noreply.github.com> --- rclcpp/resource/logging.hpp.em | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/rclcpp/resource/logging.hpp.em b/rclcpp/resource/logging.hpp.em index 7b5b0f349f..693c5c4f7b 100644 --- a/rclcpp/resource/logging.hpp.em +++ b/rclcpp/resource/logging.hpp.em @@ -125,7 +125,7 @@ def get_rclcpp_suffix_from_features(features): ) \ do { \ static_assert( \ - ::std::is_same::type>::type, \ + ::std::is_same>, \ typename ::rclcpp::Logger>::value, \ "First argument to logging macros must be an rclcpp::Logger"); \ @[ if 'throttle' in feature_combination]@ \ From 844ab6b6c56c2753c221093f372029627cd37550 Mon Sep 17 00:00:00 2001 From: Audrow Nash Date: Wed, 15 May 2024 18:03:19 -0500 Subject: [PATCH 41/98] 16.0.9 Signed-off-by: Audrow Nash --- rclcpp/CHANGELOG.rst | 13 +++++++++++++ rclcpp/package.xml | 2 +- rclcpp_action/CHANGELOG.rst | 5 +++++ rclcpp_action/package.xml | 2 +- rclcpp_components/CHANGELOG.rst | 3 +++ rclcpp_components/package.xml | 2 +- rclcpp_lifecycle/CHANGELOG.rst | 5 +++++ rclcpp_lifecycle/package.xml | 2 +- 8 files changed, 30 insertions(+), 4 deletions(-) diff --git a/rclcpp/CHANGELOG.rst b/rclcpp/CHANGELOG.rst index dcf65e8011..ea3fed9d1d 100644 --- a/rclcpp/CHANGELOG.rst +++ b/rclcpp/CHANGELOG.rst @@ -2,6 +2,19 @@ Changelog for package rclcpp ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +16.0.9 (2024-05-15) +------------------- +* Fix logging macros to build with msvc and cpp20 (`#2063 `_) (`#2529 `_) + (cherry picked from commit 86335dd4acd91d5dd973c4e4e97014e5e8a916bc) + Co-authored-by: Mateusz Szczygielski <112629916+msz-rai@users.noreply.github.com> +* address ambiguous auto variable. (`#2481 `_) (`#2485 `_) + (cherry picked from commit 3cdb25934ed261c78bdfbcf5ec9f06e0573be81e) + Co-authored-by: Tomoya Fujita +* Fix clang warning: bugprone-use-after-move (`#2116 `_) (`#2459 `_) + Co-authored-by: mauropasse + Co-authored-by: Mauro Passerino +* Contributors: Tamaki Nishino, mergify[bot] + 16.0.8 (2024-01-24) ------------------- * Add missing stdexcept include (`#2186 `_) (`#2394 `_) diff --git a/rclcpp/package.xml b/rclcpp/package.xml index 5ad552bb47..3027dc6c79 100644 --- a/rclcpp/package.xml +++ b/rclcpp/package.xml @@ -2,7 +2,7 @@ rclcpp - 16.0.8 + 16.0.9 The ROS client library in C++. Ivan Paunovic Jacob Perron diff --git a/rclcpp_action/CHANGELOG.rst b/rclcpp_action/CHANGELOG.rst index dec7e9dd8d..731ac5c8a0 100644 --- a/rclcpp_action/CHANGELOG.rst +++ b/rclcpp_action/CHANGELOG.rst @@ -3,6 +3,11 @@ Changelog for package rclcpp_action ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +16.0.9 (2024-05-15) +------------------- +* Do not generate the exception when action service response timeout. (`#2464 `_) (`#2518 `_) +* Contributors: mergify[bot] + 16.0.8 (2024-01-24) ------------------- diff --git a/rclcpp_action/package.xml b/rclcpp_action/package.xml index 2a8cececa2..c187ee3d28 100644 --- a/rclcpp_action/package.xml +++ b/rclcpp_action/package.xml @@ -2,7 +2,7 @@ rclcpp_action - 16.0.8 + 16.0.9 Adds action APIs for C++. Ivan Paunovic Jacob Perron diff --git a/rclcpp_components/CHANGELOG.rst b/rclcpp_components/CHANGELOG.rst index 3875f10a35..f43a1741df 100644 --- a/rclcpp_components/CHANGELOG.rst +++ b/rclcpp_components/CHANGELOG.rst @@ -2,6 +2,9 @@ Changelog for package rclcpp_components ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +16.0.9 (2024-05-15) +------------------- + 16.0.8 (2024-01-24) ------------------- * Add missing header required by the rclcpp::NodeOptions type (`#2324 `_) (`#2407 `_) diff --git a/rclcpp_components/package.xml b/rclcpp_components/package.xml index fa640a86d0..bbd7d1cf17 100644 --- a/rclcpp_components/package.xml +++ b/rclcpp_components/package.xml @@ -2,7 +2,7 @@ rclcpp_components - 16.0.8 + 16.0.9 Package containing tools for dynamically loadable components Ivan Paunovic Jacob Perron diff --git a/rclcpp_lifecycle/CHANGELOG.rst b/rclcpp_lifecycle/CHANGELOG.rst index a6b2813ead..97d1cdba23 100644 --- a/rclcpp_lifecycle/CHANGELOG.rst +++ b/rclcpp_lifecycle/CHANGELOG.rst @@ -3,6 +3,11 @@ Changelog for package rclcpp_lifecycle ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +16.0.9 (2024-05-15) +------------------- +* call shutdown in LifecycleNode dtor to avoid leaving the device in un… (`#2450 `_) (`#2491 `_) +* Contributors: mergify[bot] + 16.0.8 (2024-01-24) ------------------- diff --git a/rclcpp_lifecycle/package.xml b/rclcpp_lifecycle/package.xml index 840f1dfea3..aeebd588af 100644 --- a/rclcpp_lifecycle/package.xml +++ b/rclcpp_lifecycle/package.xml @@ -2,7 +2,7 @@ rclcpp_lifecycle - 16.0.8 + 16.0.9 Package containing a prototype for lifecycle implementation Ivan Paunovic Jacob Perron From f95ac7cdda63c503457b44ae2054b4583d74eab4 Mon Sep 17 00:00:00 2001 From: "mergify[bot]" <37929162+mergify[bot]@users.noreply.github.com> Date: Thu, 23 May 2024 22:13:23 -0700 Subject: [PATCH 42/98] rclcpp::shutdown should not be called before LifecycleNode dtor. (backport #2527) (#2538) * rclcpp::shutdown should not be called before LifecycleNode dtor. (#2527) Signed-off-by: Tomoya Fujita (cherry picked from commit 22df1d593a3e77917db8a7d7b20f57aee80d7e55) # Conflicts: # rclcpp_lifecycle/test/test_lifecycle_publisher.cpp * resolve conflicts. Signed-off-by: Tomoya Fujita --------- Signed-off-by: Tomoya Fujita Co-authored-by: Tomoya Fujita --- .../test/test_lifecycle_publisher.cpp | 66 +++++++++---------- 1 file changed, 30 insertions(+), 36 deletions(-) diff --git a/rclcpp_lifecycle/test/test_lifecycle_publisher.cpp b/rclcpp_lifecycle/test/test_lifecycle_publisher.cpp index 2a5d08f728..3b623ba47c 100644 --- a/rclcpp_lifecycle/test/test_lifecycle_publisher.cpp +++ b/rclcpp_lifecycle/test/test_lifecycle_publisher.cpp @@ -48,24 +48,10 @@ class EmptyLifecycleNode : public rclcpp_lifecycle::LifecycleNode explicit EmptyLifecycleNode(const std::string & node_name) : rclcpp_lifecycle::LifecycleNode(node_name) { - rclcpp::PublisherOptionsWithAllocator> options; - publisher_ = - std::make_shared>( - get_node_base_interface().get(), std::string("topic"), rclcpp::QoS(10), options); - add_managed_entity(publisher_); - // For coverage this is being added here auto timer = create_wall_timer(std::chrono::seconds(1), []() {}); add_timer_handle(timer); } - - std::shared_ptr> publisher() - { - return publisher_; - } - -private: - std::shared_ptr> publisher_; }; class TestLifecyclePublisher : public ::testing::Test @@ -74,77 +60,85 @@ class TestLifecyclePublisher : public ::testing::Test void SetUp() { rclcpp::init(0, nullptr); - node_ = std::make_shared("node"); } void TearDown() { rclcpp::shutdown(); } - -protected: - std::shared_ptr node_; }; TEST_F(TestLifecyclePublisher, publish_managed_by_node) { + auto node = std::make_shared("node"); + + rclcpp::PublisherOptionsWithAllocator> options; + std::shared_ptr> publisher = + node->create_publisher(std::string("topic"), rclcpp::QoS(10), options); + // transition via LifecycleNode auto success = rclcpp_lifecycle::node_interfaces::LifecycleNodeInterface::CallbackReturn::SUCCESS; auto reset_key = rclcpp_lifecycle::node_interfaces::LifecycleNodeInterface::CallbackReturn::ERROR; auto ret = reset_key; - EXPECT_EQ(State::PRIMARY_STATE_UNCONFIGURED, node_->get_current_state().id()); - node_->trigger_transition( + EXPECT_EQ(State::PRIMARY_STATE_UNCONFIGURED, node->get_current_state().id()); + node->trigger_transition( rclcpp_lifecycle::Transition(Transition::TRANSITION_CONFIGURE), ret); ASSERT_EQ(success, ret); ret = reset_key; - node_->trigger_transition( + node->trigger_transition( rclcpp_lifecycle::Transition(Transition::TRANSITION_ACTIVATE), ret); ASSERT_EQ(success, ret); ret = reset_key; - EXPECT_TRUE(node_->publisher()->is_activated()); + EXPECT_TRUE(publisher->is_activated()); { auto msg_ptr = std::make_unique(); - EXPECT_NO_THROW(node_->publisher()->publish(*msg_ptr)); + EXPECT_NO_THROW(publisher->publish(*msg_ptr)); } { auto msg_ptr = std::make_unique(); - EXPECT_NO_THROW(node_->publisher()->publish(std::move(msg_ptr))); + EXPECT_NO_THROW(publisher->publish(std::move(msg_ptr))); } - node_->trigger_transition( + node->trigger_transition( rclcpp_lifecycle::Transition(Transition::TRANSITION_DEACTIVATE), ret); ASSERT_EQ(success, ret); ret = reset_key; - EXPECT_FALSE(node_->publisher()->is_activated()); + EXPECT_FALSE(publisher->is_activated()); { auto msg_ptr = std::make_unique(); - EXPECT_NO_THROW(node_->publisher()->publish(*msg_ptr)); + EXPECT_NO_THROW(publisher->publish(*msg_ptr)); } { auto msg_ptr = std::make_unique(); - EXPECT_NO_THROW(node_->publisher()->publish(std::move(msg_ptr))); + EXPECT_NO_THROW(publisher->publish(std::move(msg_ptr))); } } TEST_F(TestLifecyclePublisher, publish) { + auto node = std::make_shared("node"); + + rclcpp::PublisherOptionsWithAllocator> options; + std::shared_ptr> publisher = + node->create_publisher(std::string("topic"), rclcpp::QoS(10), options); + // transition via LifecyclePublisher - node_->publisher()->on_deactivate(); - EXPECT_FALSE(node_->publisher()->is_activated()); + publisher->on_deactivate(); + EXPECT_FALSE(publisher->is_activated()); { auto msg_ptr = std::make_unique(); - EXPECT_NO_THROW(node_->publisher()->publish(*msg_ptr)); + EXPECT_NO_THROW(publisher->publish(*msg_ptr)); } { auto msg_ptr = std::make_unique(); - EXPECT_NO_THROW(node_->publisher()->publish(std::move(msg_ptr))); + EXPECT_NO_THROW(publisher->publish(std::move(msg_ptr))); } - node_->publisher()->on_activate(); - EXPECT_TRUE(node_->publisher()->is_activated()); + publisher->on_activate(); + EXPECT_TRUE(publisher->is_activated()); { auto msg_ptr = std::make_unique(); - EXPECT_NO_THROW(node_->publisher()->publish(*msg_ptr)); + EXPECT_NO_THROW(publisher->publish(*msg_ptr)); } { auto msg_ptr = std::make_unique(); - EXPECT_NO_THROW(node_->publisher()->publish(std::move(msg_ptr))); + EXPECT_NO_THROW(publisher->publish(std::move(msg_ptr))); } } From 595badb55ced4e802e90c3d0959c6f0f8475f9b9 Mon Sep 17 00:00:00 2001 From: Tomoya Fujita Date: Thu, 6 Jun 2024 11:04:38 -0700 Subject: [PATCH 43/98] lifecycle node dtor shutdown should be called only in primary state. (#2544) * lifecycle node dtor shutdown should be called only in primary state. Signed-off-by: Tomoya Fujita * LifecycleNode shutdown on dtor only with valid context. (#2545) Signed-off-by: Tomoya Fujita --------- Signed-off-by: Tomoya Fujita --- rclcpp_lifecycle/src/lifecycle_node.cpp | 37 +++++++++++++++++-------- 1 file changed, 25 insertions(+), 12 deletions(-) diff --git a/rclcpp_lifecycle/src/lifecycle_node.cpp b/rclcpp_lifecycle/src/lifecycle_node.cpp index e812bbcedb..4b0bf53a2c 100644 --- a/rclcpp_lifecycle/src/lifecycle_node.cpp +++ b/rclcpp_lifecycle/src/lifecycle_node.cpp @@ -133,20 +133,32 @@ LifecycleNode::LifecycleNode( LifecycleNode::~LifecycleNode() { - // shutdown if necessary to avoid leaving the device in unknown state - if (LifecycleNode::get_current_state().id() != - lifecycle_msgs::msg::State::PRIMARY_STATE_FINALIZED) - { - auto ret = rclcpp_lifecycle::node_interfaces::LifecycleNodeInterface::CallbackReturn::ERROR; - auto finalized = LifecycleNode::shutdown(ret); - if (finalized.id() != lifecycle_msgs::msg::State::PRIMARY_STATE_FINALIZED || - ret != rclcpp_lifecycle::node_interfaces::LifecycleNodeInterface::CallbackReturn::SUCCESS) - { - RCLCPP_WARN( + auto current_state = LifecycleNode::get_current_state().id(); + // shutdown if necessary to avoid leaving the device in any other primary state + if (current_state < lifecycle_msgs::msg::State::PRIMARY_STATE_FINALIZED) { + if (node_base_->get_context()->is_valid()) { + auto ret = rclcpp_lifecycle::node_interfaces::LifecycleNodeInterface::CallbackReturn::ERROR; + auto finalized = LifecycleNode::shutdown(ret); + if (finalized.id() != lifecycle_msgs::msg::State::PRIMARY_STATE_FINALIZED || + ret != rclcpp_lifecycle::node_interfaces::LifecycleNodeInterface::CallbackReturn::SUCCESS) + { + RCLCPP_WARN( + rclcpp::get_logger("rclcpp_lifecycle"), + "Shutdown error in destruction of LifecycleNode: final state(%s)", + finalized.label().c_str()); + } + } else { + // TODO(fujitatomoya): consider when context is gracefully shutdown before. + RCLCPP_DEBUG( rclcpp::get_logger("rclcpp_lifecycle"), - "Shutdown error in destruction of LifecycleNode: final state(%s)", - finalized.label().c_str()); + "Context invalid error in destruction of LifecycleNode: Node still in transition state(%u)", + current_state); } + } else if (current_state > lifecycle_msgs::msg::State::PRIMARY_STATE_FINALIZED) { + RCLCPP_WARN( + rclcpp::get_logger("rclcpp_lifecycle"), + "Shutdown error in destruction of LifecycleNode: Node still in transition state(%u)", + current_state); } // release sub-interfaces in an order that allows them to consult with node_base during tear-down @@ -159,6 +171,7 @@ LifecycleNode::~LifecycleNode() node_timers_.reset(); node_logging_.reset(); node_graph_.reset(); + node_base_.reset(); } const char * From 32f19615bb63d0df760323910dad7543b1b75b5c Mon Sep 17 00:00:00 2001 From: "mergify[bot]" <37929162+mergify[bot]@users.noreply.github.com> Date: Tue, 11 Jun 2024 18:19:37 -0700 Subject: [PATCH 44/98] Add test creating two content filter topics with the same topic name (#2546) (#2549) (#2551) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Mario-DL Co-authored-by: Mario Domínguez López <116071334+Mario-DL@users.noreply.github.com> (cherry picked from commit 7c096888caf92aa7557e1d3efc5448b56d8ce81c) Co-authored-by: Alejandro Hernández Cordero --- .../test_subscription_content_filter.cpp | 22 +++++++++++++++++++ 1 file changed, 22 insertions(+) diff --git a/rclcpp/test/rclcpp/test_subscription_content_filter.cpp b/rclcpp/test/rclcpp/test_subscription_content_filter.cpp index 942aa1274e..372af03611 100644 --- a/rclcpp/test/rclcpp/test_subscription_content_filter.cpp +++ b/rclcpp/test/rclcpp/test_subscription_content_filter.cpp @@ -310,3 +310,25 @@ TEST_F(CLASSNAME(TestContentFilterSubscription, RMW_IMPLEMENTATION), content_fil } } } + +TEST_F( + CLASSNAME( + TestContentFilterSubscription, + RMW_IMPLEMENTATION), create_two_content_filters_with_same_topic_name_and_destroy) { + + // Create another content filter + auto options = rclcpp::SubscriptionOptions(); + + std::string filter_expression = "int32_value > %0"; + std::vector expression_parameters = {"4"}; + + options.content_filter_options.filter_expression = filter_expression; + options.content_filter_options.expression_parameters = expression_parameters; + + auto callback = [](std::shared_ptr) {}; + auto sub_2 = node->create_subscription( + "content_filter_topic", qos, callback, options); + + EXPECT_NE(nullptr, sub_2); + sub_2.reset(); +} From 6737773a5d8831957676586148056cf5307a7da7 Mon Sep 17 00:00:00 2001 From: Tomoya Fujita Date: Wed, 12 Jun 2024 09:51:49 -0700 Subject: [PATCH 45/98] revert call shutdown in LifecycleNode destructor (Humble) (#2560) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * Revert "lifecycle node dtor shutdown should be called only in primary state. (#2544)" This reverts commit 595badb55ced4e802e90c3d0959c6f0f8475f9b9. Signed-off-by: Tomoya Fujita * Revert "call shutdown in LifecycleNode dtor to avoid leaving the device in un… (#2450) (#2491)" This reverts commit 0f9604d1b712b154cad32dfe4b4a6bfed2924436. Signed-off-by: Tomoya Fujita --------- Signed-off-by: Tomoya Fujita --- rclcpp_lifecycle/src/lifecycle_node.cpp | 29 ---- rclcpp_lifecycle/test/test_lifecycle_node.cpp | 140 ------------------ 2 files changed, 169 deletions(-) diff --git a/rclcpp_lifecycle/src/lifecycle_node.cpp b/rclcpp_lifecycle/src/lifecycle_node.cpp index 4b0bf53a2c..333edf41b8 100644 --- a/rclcpp_lifecycle/src/lifecycle_node.cpp +++ b/rclcpp_lifecycle/src/lifecycle_node.cpp @@ -133,34 +133,6 @@ LifecycleNode::LifecycleNode( LifecycleNode::~LifecycleNode() { - auto current_state = LifecycleNode::get_current_state().id(); - // shutdown if necessary to avoid leaving the device in any other primary state - if (current_state < lifecycle_msgs::msg::State::PRIMARY_STATE_FINALIZED) { - if (node_base_->get_context()->is_valid()) { - auto ret = rclcpp_lifecycle::node_interfaces::LifecycleNodeInterface::CallbackReturn::ERROR; - auto finalized = LifecycleNode::shutdown(ret); - if (finalized.id() != lifecycle_msgs::msg::State::PRIMARY_STATE_FINALIZED || - ret != rclcpp_lifecycle::node_interfaces::LifecycleNodeInterface::CallbackReturn::SUCCESS) - { - RCLCPP_WARN( - rclcpp::get_logger("rclcpp_lifecycle"), - "Shutdown error in destruction of LifecycleNode: final state(%s)", - finalized.label().c_str()); - } - } else { - // TODO(fujitatomoya): consider when context is gracefully shutdown before. - RCLCPP_DEBUG( - rclcpp::get_logger("rclcpp_lifecycle"), - "Context invalid error in destruction of LifecycleNode: Node still in transition state(%u)", - current_state); - } - } else if (current_state > lifecycle_msgs::msg::State::PRIMARY_STATE_FINALIZED) { - RCLCPP_WARN( - rclcpp::get_logger("rclcpp_lifecycle"), - "Shutdown error in destruction of LifecycleNode: Node still in transition state(%u)", - current_state); - } - // release sub-interfaces in an order that allows them to consult with node_base during tear-down node_waitables_.reset(); node_time_source_.reset(); @@ -171,7 +143,6 @@ LifecycleNode::~LifecycleNode() node_timers_.reset(); node_logging_.reset(); node_graph_.reset(); - node_base_.reset(); } const char * diff --git a/rclcpp_lifecycle/test/test_lifecycle_node.cpp b/rclcpp_lifecycle/test/test_lifecycle_node.cpp index 5fbe733eb2..5a3054781b 100644 --- a/rclcpp_lifecycle/test/test_lifecycle_node.cpp +++ b/rclcpp_lifecycle/test/test_lifecycle_node.cpp @@ -435,146 +435,6 @@ TEST_F(TestDefaultStateMachine, bad_mood) { EXPECT_EQ(1u, test_node->number_of_callbacks); } - -TEST_F(TestDefaultStateMachine, shutdown_from_each_primary_state) { - auto success = rclcpp_lifecycle::node_interfaces::LifecycleNodeInterface::CallbackReturn::SUCCESS; - auto reset_key = rclcpp_lifecycle::node_interfaces::LifecycleNodeInterface::CallbackReturn::ERROR; - - // PRIMARY_STATE_UNCONFIGURED to shutdown - { - auto ret = reset_key; - auto test_node = std::make_shared("testnode"); - auto finalized = test_node->shutdown(ret); - EXPECT_EQ(success, ret); - EXPECT_EQ(finalized.id(), State::PRIMARY_STATE_FINALIZED); - } - - // PRIMARY_STATE_INACTIVE to shutdown - { - auto ret = reset_key; - auto test_node = std::make_shared("testnode"); - auto configured = test_node->configure(ret); - EXPECT_EQ(success, ret); - EXPECT_EQ(configured.id(), State::PRIMARY_STATE_INACTIVE); - ret = reset_key; - auto finalized = test_node->shutdown(ret); - EXPECT_EQ(success, ret); - EXPECT_EQ(finalized.id(), State::PRIMARY_STATE_FINALIZED); - } - - // PRIMARY_STATE_ACTIVE to shutdown - { - auto ret = reset_key; - auto test_node = std::make_shared("testnode"); - auto configured = test_node->configure(ret); - EXPECT_EQ(success, ret); - EXPECT_EQ(configured.id(), State::PRIMARY_STATE_INACTIVE); - ret = reset_key; - auto activated = test_node->activate(ret); - EXPECT_EQ(success, ret); - EXPECT_EQ(activated.id(), State::PRIMARY_STATE_ACTIVE); - ret = reset_key; - auto finalized = test_node->shutdown(ret); - EXPECT_EQ(success, ret); - EXPECT_EQ(finalized.id(), State::PRIMARY_STATE_FINALIZED); - } - - // PRIMARY_STATE_FINALIZED to shutdown - { - auto ret = reset_key; - auto test_node = std::make_shared("testnode"); - auto configured = test_node->configure(ret); - EXPECT_EQ(success, ret); - EXPECT_EQ(configured.id(), State::PRIMARY_STATE_INACTIVE); - ret = reset_key; - auto activated = test_node->activate(ret); - EXPECT_EQ(success, ret); - EXPECT_EQ(activated.id(), State::PRIMARY_STATE_ACTIVE); - ret = reset_key; - auto finalized = test_node->shutdown(ret); - EXPECT_EQ(success, ret); - EXPECT_EQ(finalized.id(), State::PRIMARY_STATE_FINALIZED); - ret = reset_key; - auto finalized_again = test_node->shutdown(ret); - EXPECT_EQ(reset_key, ret); - EXPECT_EQ(finalized_again.id(), State::PRIMARY_STATE_FINALIZED); - } -} - -TEST_F(TestDefaultStateMachine, test_shutdown_on_dtor) { - auto success = rclcpp_lifecycle::node_interfaces::LifecycleNodeInterface::CallbackReturn::SUCCESS; - auto reset_key = rclcpp_lifecycle::node_interfaces::LifecycleNodeInterface::CallbackReturn::ERROR; - - bool shutdown_cb_called = false; - auto on_shutdown_callback = - [&shutdown_cb_called](const rclcpp_lifecycle::State &) -> - rclcpp_lifecycle::node_interfaces::LifecycleNodeInterface::CallbackReturn { - shutdown_cb_called = true; - return rclcpp_lifecycle::node_interfaces::LifecycleNodeInterface::CallbackReturn::SUCCESS; - }; - - // PRIMARY_STATE_UNCONFIGURED to shutdown via dtor - shutdown_cb_called = false; - { - auto test_node = std::make_shared("testnode"); - test_node->register_on_shutdown(std::bind(on_shutdown_callback, std::placeholders::_1)); - EXPECT_EQ(State::PRIMARY_STATE_UNCONFIGURED, test_node->get_current_state().id()); - EXPECT_FALSE(shutdown_cb_called); - } - EXPECT_TRUE(shutdown_cb_called); - - // PRIMARY_STATE_INACTIVE to shutdown via dtor - shutdown_cb_called = false; - { - auto ret = reset_key; - auto test_node = std::make_shared("testnode"); - test_node->register_on_shutdown(std::bind(on_shutdown_callback, std::placeholders::_1)); - auto configured = test_node->configure(ret); - EXPECT_EQ(success, ret); - EXPECT_EQ(configured.id(), State::PRIMARY_STATE_INACTIVE); - EXPECT_FALSE(shutdown_cb_called); - } - EXPECT_TRUE(shutdown_cb_called); - - // PRIMARY_STATE_ACTIVE to shutdown via dtor - shutdown_cb_called = false; - { - auto ret = reset_key; - auto test_node = std::make_shared("testnode"); - test_node->register_on_shutdown(std::bind(on_shutdown_callback, std::placeholders::_1)); - auto configured = test_node->configure(ret); - EXPECT_EQ(success, ret); - EXPECT_EQ(configured.id(), State::PRIMARY_STATE_INACTIVE); - ret = reset_key; - auto activated = test_node->activate(ret); - EXPECT_EQ(success, ret); - EXPECT_EQ(activated.id(), State::PRIMARY_STATE_ACTIVE); - EXPECT_FALSE(shutdown_cb_called); - } - EXPECT_TRUE(shutdown_cb_called); - - // PRIMARY_STATE_FINALIZED to shutdown via dtor - shutdown_cb_called = false; - { - auto ret = reset_key; - auto test_node = std::make_shared("testnode"); - test_node->register_on_shutdown(std::bind(on_shutdown_callback, std::placeholders::_1)); - auto configured = test_node->configure(ret); - EXPECT_EQ(success, ret); - EXPECT_EQ(configured.id(), State::PRIMARY_STATE_INACTIVE); - ret = reset_key; - auto activated = test_node->activate(ret); - EXPECT_EQ(success, ret); - EXPECT_EQ(activated.id(), State::PRIMARY_STATE_ACTIVE); - ret = reset_key; - auto finalized = test_node->shutdown(ret); - EXPECT_EQ(success, ret); - EXPECT_EQ(finalized.id(), State::PRIMARY_STATE_FINALIZED); - EXPECT_TRUE(shutdown_cb_called); // should be called already - } - EXPECT_TRUE(shutdown_cb_called); -} - TEST_F(TestDefaultStateMachine, lifecycle_subscriber) { auto test_node = std::make_shared>("testnode"); From e97d4e86161d2a693d944fe53b18241ab96c05db Mon Sep 17 00:00:00 2001 From: Audrow Nash Date: Fri, 26 Jul 2024 10:14:48 -0500 Subject: [PATCH 46/98] 16.0.10 Signed-off-by: Audrow Nash --- rclcpp/CHANGELOG.rst | 11 +++++------ rclcpp/package.xml | 2 +- rclcpp_action/CHANGELOG.rst | 3 +++ rclcpp_action/package.xml | 2 +- rclcpp_components/CHANGELOG.rst | 3 +++ rclcpp_components/package.xml | 2 +- rclcpp_lifecycle/CHANGELOG.rst | 7 +++++++ rclcpp_lifecycle/package.xml | 2 +- 8 files changed, 22 insertions(+), 10 deletions(-) diff --git a/rclcpp/CHANGELOG.rst b/rclcpp/CHANGELOG.rst index ea3fed9d1d..8c940b85ef 100644 --- a/rclcpp/CHANGELOG.rst +++ b/rclcpp/CHANGELOG.rst @@ -2,17 +2,16 @@ Changelog for package rclcpp ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +16.0.10 (2024-07-26) +-------------------- +* Add test creating two content filter topics with the same topic name (`#2546 `_) (`#2549 `_) (`#2551 `_) +* Contributors: mergify[bot] + 16.0.9 (2024-05-15) ------------------- * Fix logging macros to build with msvc and cpp20 (`#2063 `_) (`#2529 `_) - (cherry picked from commit 86335dd4acd91d5dd973c4e4e97014e5e8a916bc) - Co-authored-by: Mateusz Szczygielski <112629916+msz-rai@users.noreply.github.com> * address ambiguous auto variable. (`#2481 `_) (`#2485 `_) - (cherry picked from commit 3cdb25934ed261c78bdfbcf5ec9f06e0573be81e) - Co-authored-by: Tomoya Fujita * Fix clang warning: bugprone-use-after-move (`#2116 `_) (`#2459 `_) - Co-authored-by: mauropasse - Co-authored-by: Mauro Passerino * Contributors: Tamaki Nishino, mergify[bot] 16.0.8 (2024-01-24) diff --git a/rclcpp/package.xml b/rclcpp/package.xml index 3027dc6c79..bca3f07905 100644 --- a/rclcpp/package.xml +++ b/rclcpp/package.xml @@ -2,7 +2,7 @@ rclcpp - 16.0.9 + 16.0.10 The ROS client library in C++. Ivan Paunovic Jacob Perron diff --git a/rclcpp_action/CHANGELOG.rst b/rclcpp_action/CHANGELOG.rst index 731ac5c8a0..ddfe99c9d5 100644 --- a/rclcpp_action/CHANGELOG.rst +++ b/rclcpp_action/CHANGELOG.rst @@ -3,6 +3,9 @@ Changelog for package rclcpp_action ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +16.0.10 (2024-07-26) +-------------------- + 16.0.9 (2024-05-15) ------------------- * Do not generate the exception when action service response timeout. (`#2464 `_) (`#2518 `_) diff --git a/rclcpp_action/package.xml b/rclcpp_action/package.xml index c187ee3d28..687d4df1ee 100644 --- a/rclcpp_action/package.xml +++ b/rclcpp_action/package.xml @@ -2,7 +2,7 @@ rclcpp_action - 16.0.9 + 16.0.10 Adds action APIs for C++. Ivan Paunovic Jacob Perron diff --git a/rclcpp_components/CHANGELOG.rst b/rclcpp_components/CHANGELOG.rst index f43a1741df..49f69bdfef 100644 --- a/rclcpp_components/CHANGELOG.rst +++ b/rclcpp_components/CHANGELOG.rst @@ -2,6 +2,9 @@ Changelog for package rclcpp_components ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +16.0.10 (2024-07-26) +-------------------- + 16.0.9 (2024-05-15) ------------------- diff --git a/rclcpp_components/package.xml b/rclcpp_components/package.xml index bbd7d1cf17..951a4d9756 100644 --- a/rclcpp_components/package.xml +++ b/rclcpp_components/package.xml @@ -2,7 +2,7 @@ rclcpp_components - 16.0.9 + 16.0.10 Package containing tools for dynamically loadable components Ivan Paunovic Jacob Perron diff --git a/rclcpp_lifecycle/CHANGELOG.rst b/rclcpp_lifecycle/CHANGELOG.rst index 97d1cdba23..ba1a94afbd 100644 --- a/rclcpp_lifecycle/CHANGELOG.rst +++ b/rclcpp_lifecycle/CHANGELOG.rst @@ -3,6 +3,13 @@ Changelog for package rclcpp_lifecycle ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +16.0.10 (2024-07-26) +-------------------- +* revert call shutdown in LifecycleNode destructor (Humble) (`#2560 `_) +* lifecycle node dtor shutdown should be called only in primary state. (`#2544 `_) +* rclcpp::shutdown should not be called before LifecycleNode dtor. (backport `#2527 `_) (`#2538 `_) +* Contributors: Tomoya Fujita, mergify[bot] + 16.0.9 (2024-05-15) ------------------- * call shutdown in LifecycleNode dtor to avoid leaving the device in un… (`#2450 `_) (`#2491 `_) diff --git a/rclcpp_lifecycle/package.xml b/rclcpp_lifecycle/package.xml index aeebd588af..2b9ec7b48e 100644 --- a/rclcpp_lifecycle/package.xml +++ b/rclcpp_lifecycle/package.xml @@ -2,7 +2,7 @@ rclcpp_lifecycle - 16.0.9 + 16.0.10 Package containing a prototype for lifecycle implementation Ivan Paunovic Jacob Perron From 9be01cf4008b90c7f4d4ded3c17d583c15d83eb5 Mon Sep 17 00:00:00 2001 From: "mergify[bot]" <37929162+mergify[bot]@users.noreply.github.com> Date: Mon, 9 Sep 2024 10:32:23 -0700 Subject: [PATCH 47/98] =?UTF-8?q?Use=20the=20same=20context=20for=20the=20?= =?UTF-8?q?specified=20node=20in=20rclcpp::spin=20functions=E2=80=A6=20(#2?= =?UTF-8?q?618)=20(#2620)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Tomoya Fujita Signed-off-by: Kotaro Yoshimoto (cherry picked from commit 531b2b1a0838a31e604317e4b18877112d5b9378) Co-authored-by: Tomoya Fujita --- rclcpp/include/rclcpp/executors.hpp | 4 ++- rclcpp/src/rclcpp/executors.cpp | 8 +++-- .../test/rclcpp/executors/test_executors.cpp | 29 +++++++++++++++++++ 3 files changed, 38 insertions(+), 3 deletions(-) diff --git a/rclcpp/include/rclcpp/executors.hpp b/rclcpp/include/rclcpp/executors.hpp index 36fb0d63cf..e7e027b9c0 100644 --- a/rclcpp/include/rclcpp/executors.hpp +++ b/rclcpp/include/rclcpp/executors.hpp @@ -107,7 +107,9 @@ spin_until_future_complete( const FutureT & future, std::chrono::duration timeout = std::chrono::duration(-1)) { - rclcpp::executors::SingleThreadedExecutor executor; + rclcpp::ExecutorOptions options; + options.context = node_ptr->get_context(); + rclcpp::executors::SingleThreadedExecutor executor(options); return executors::spin_node_until_future_complete(executor, node_ptr, future, timeout); } diff --git a/rclcpp/src/rclcpp/executors.cpp b/rclcpp/src/rclcpp/executors.cpp index 0a900c07da..4381fbe5f6 100644 --- a/rclcpp/src/rclcpp/executors.cpp +++ b/rclcpp/src/rclcpp/executors.cpp @@ -17,7 +17,9 @@ void rclcpp::spin_some(rclcpp::node_interfaces::NodeBaseInterface::SharedPtr node_ptr) { - rclcpp::executors::SingleThreadedExecutor exec; + rclcpp::ExecutorOptions options; + options.context = node_ptr->get_context(); + rclcpp::executors::SingleThreadedExecutor exec(options); exec.spin_node_some(node_ptr); } @@ -30,7 +32,9 @@ rclcpp::spin_some(rclcpp::Node::SharedPtr node_ptr) void rclcpp::spin(rclcpp::node_interfaces::NodeBaseInterface::SharedPtr node_ptr) { - rclcpp::executors::SingleThreadedExecutor exec; + rclcpp::ExecutorOptions options; + options.context = node_ptr->get_context(); + rclcpp::executors::SingleThreadedExecutor exec(options); exec.add_node(node_ptr); exec.spin(); exec.remove_node(node_ptr); diff --git a/rclcpp/test/rclcpp/executors/test_executors.cpp b/rclcpp/test/rclcpp/executors/test_executors.cpp index 143068601a..7bdd539614 100644 --- a/rclcpp/test/rclcpp/executors/test_executors.cpp +++ b/rclcpp/test/rclcpp/executors/test_executors.cpp @@ -21,6 +21,7 @@ #include #include +#include #include #include #include @@ -696,3 +697,31 @@ TYPED_TEST(TestIntraprocessExecutors, testIntraprocessRetrigger) { executor.spin(); EXPECT_EQ(kNumMessages, this->callback_count.load()); } + +// Check spin functions with non default context +TEST(TestExecutors, testSpinWithNonDefaultContext) +{ + auto non_default_context = std::make_shared(); + non_default_context->init(0, nullptr); + + { + auto node = + std::make_unique("node", rclcpp::NodeOptions().context(non_default_context)); + + EXPECT_NO_THROW(rclcpp::spin_some(node->get_node_base_interface())); + + auto check_spin_until_future_complete = [&]() { + std::promise promise; + std::future future = promise.get_future(); + promise.set_value(true); + + auto shared_future = future.share(); + auto ret = rclcpp::spin_until_future_complete( + node->get_node_base_interface(), shared_future, 1s); + EXPECT_EQ(rclcpp::FutureReturnCode::SUCCESS, ret); + }; + EXPECT_NO_THROW(check_spin_until_future_complete()); + } + + rclcpp::shutdown(non_default_context); +} From 28de27e4ff2bdec3b268001302bc9a631f56d5b0 Mon Sep 17 00:00:00 2001 From: roscan-tech Date: Tue, 8 Oct 2024 01:49:43 +0800 Subject: [PATCH 48/98] Fix subscription.is_serialized() for callbacks with message info (#1950) (#2622) * Fix subscription.is_serialized() for callbacks with message info argument * Add tests + please linters Signed-off-by: Ivan Santiago Paunovic Signed-off-by: roscan-tech Co-authored-by: Ivan Santiago Paunovic --- .../rclcpp/any_subscription_callback.hpp | 8 +- .../rclcpp/test_any_subscription_callback.cpp | 105 ++++++++++++++++++ 2 files changed, 112 insertions(+), 1 deletion(-) diff --git a/rclcpp/include/rclcpp/any_subscription_callback.hpp b/rclcpp/include/rclcpp/any_subscription_callback.hpp index ea26fad397..d4f5fc309b 100644 --- a/rclcpp/include/rclcpp/any_subscription_callback.hpp +++ b/rclcpp/include/rclcpp/any_subscription_callback.hpp @@ -950,7 +950,13 @@ class AnySubscriptionCallback std::holds_alternative(callback_variant_) || std::holds_alternative(callback_variant_) || std::holds_alternative(callback_variant_) || - std::holds_alternative(callback_variant_); + std::holds_alternative(callback_variant_) || + std::holds_alternative(callback_variant_) || + std::holds_alternative(callback_variant_) || + std::holds_alternative(callback_variant_) || + std::holds_alternative( + callback_variant_) || + std::holds_alternative(callback_variant_); } void diff --git a/rclcpp/test/rclcpp/test_any_subscription_callback.cpp b/rclcpp/test/rclcpp/test_any_subscription_callback.cpp index 4fd3f32626..45fe091f07 100644 --- a/rclcpp/test/rclcpp/test_any_subscription_callback.cpp +++ b/rclcpp/test/rclcpp/test_any_subscription_callback.cpp @@ -93,6 +93,111 @@ TEST_F(TestAnySubscriptionCallback, construct_destruct) { rclcpp::AnySubscriptionCallback asc2(allocator); } +TEST_F(TestAnySubscriptionCallback, is_serialized_message_callback) { + { + rclcpp::AnySubscriptionCallback asc; + asc.set([](const rclcpp::SerializedMessage &) {}); + EXPECT_TRUE(asc.is_serialized_message_callback()); + EXPECT_NO_THROW( + asc.dispatch( + std::make_shared(), + rclcpp::MessageInfo{})); + } + { + rclcpp::AnySubscriptionCallback asc; + asc.set([](const rclcpp::SerializedMessage &, const rclcpp::MessageInfo &) {}); + EXPECT_TRUE(asc.is_serialized_message_callback()); + EXPECT_NO_THROW( + asc.dispatch( + std::make_shared(), + rclcpp::MessageInfo{})); + } + { + rclcpp::AnySubscriptionCallback asc; + asc.set([](const rclcpp::SerializedMessage &, const rclcpp::MessageInfo &) {}); + EXPECT_TRUE(asc.is_serialized_message_callback()); + EXPECT_NO_THROW( + asc.dispatch( + std::make_shared(), + rclcpp::MessageInfo{})); + } + { + rclcpp::AnySubscriptionCallback asc; + asc.set([](std::unique_ptr) {}); + EXPECT_TRUE(asc.is_serialized_message_callback()); + EXPECT_NO_THROW( + asc.dispatch( + std::make_shared(), + rclcpp::MessageInfo{})); + } + { + rclcpp::AnySubscriptionCallback asc; + asc.set([](std::unique_ptr, const rclcpp::MessageInfo &) {}); + EXPECT_TRUE(asc.is_serialized_message_callback()); + EXPECT_NO_THROW( + asc.dispatch( + std::make_shared(), + rclcpp::MessageInfo{})); + } + { + rclcpp::AnySubscriptionCallback asc; + asc.set([](std::shared_ptr) {}); + EXPECT_TRUE(asc.is_serialized_message_callback()); + EXPECT_NO_THROW( + asc.dispatch( + std::make_shared(), + rclcpp::MessageInfo{})); + } + { + rclcpp::AnySubscriptionCallback asc; + asc.set([](std::shared_ptr, const rclcpp::MessageInfo &) {}); + EXPECT_TRUE(asc.is_serialized_message_callback()); + EXPECT_NO_THROW( + asc.dispatch( + std::make_shared(), + rclcpp::MessageInfo{})); + } + { + rclcpp::AnySubscriptionCallback asc; + asc.set([](const std::shared_ptr &) {}); + EXPECT_TRUE(asc.is_serialized_message_callback()); + EXPECT_NO_THROW( + asc.dispatch( + std::make_shared(), + rclcpp::MessageInfo{})); + } + { + rclcpp::AnySubscriptionCallback asc; + asc.set( + []( + const std::shared_ptr &, + const rclcpp::MessageInfo &) {}); + EXPECT_TRUE(asc.is_serialized_message_callback()); + EXPECT_NO_THROW( + asc.dispatch( + std::make_shared(), + rclcpp::MessageInfo{})); + } + { + rclcpp::AnySubscriptionCallback asc; + asc.set([](std::shared_ptr) {}); + EXPECT_TRUE(asc.is_serialized_message_callback()); + EXPECT_NO_THROW( + asc.dispatch( + std::make_shared(), + rclcpp::MessageInfo{})); + } + { + rclcpp::AnySubscriptionCallback asc; + asc.set([](std::shared_ptr, const rclcpp::MessageInfo &) {}); + EXPECT_TRUE(asc.is_serialized_message_callback()); + EXPECT_NO_THROW( + asc.dispatch( + std::make_shared(), + rclcpp::MessageInfo{})); + } +} + TEST_F(TestAnySubscriptionCallback, unset_dispatch_throw) { EXPECT_THROW( any_subscription_callback_.dispatch(msg_shared_ptr_, message_info_), From 82ec3f000e833c94395ae5a4cdbf14a46a3e210d Mon Sep 17 00:00:00 2001 From: Camilo Camacho <44010259+edgarcamilocamacho@users.noreply.github.com> Date: Thu, 31 Oct 2024 04:03:26 -0500 Subject: [PATCH 49/98] fix: Fixed race condition in action server between is_ready and take. Backport from iron #2531 (#2635) Signed-off-by: Camilo Camacho Co-authored-by: Janosch Machowinski --- rclcpp_action/src/client.cpp | 373 +++++++++++++++++++++++------------ rclcpp_action/src/server.cpp | 307 +++++++++++++++++----------- 2 files changed, 438 insertions(+), 242 deletions(-) diff --git a/rclcpp_action/src/client.cpp b/rclcpp_action/src/client.cpp index 6122725ca6..a3be59b2ee 100644 --- a/rclcpp_action/src/client.cpp +++ b/rclcpp_action/src/client.cpp @@ -19,6 +19,7 @@ #include #include #include +#include #include "rcl_action/action_client.h" #include "rcl_action/wait.h" @@ -31,6 +32,67 @@ namespace rclcpp_action { +struct ClientBaseData +{ + struct FeedbackReadyData + { + FeedbackReadyData(rcl_ret_t retIn, std::shared_ptr msg) + : ret(retIn), feedback_message(msg) {} + rcl_ret_t ret; + std::shared_ptr feedback_message; + }; + struct StatusReadyData + { + StatusReadyData(rcl_ret_t retIn, std::shared_ptr msg) + : ret(retIn), status_message(msg) {} + rcl_ret_t ret; + std::shared_ptr status_message; + }; + struct GoalResponseData + { + GoalResponseData(rcl_ret_t retIn, rmw_request_id_t header, std::shared_ptr response) + : ret(retIn), response_header(header), goal_response(response) {} + rcl_ret_t ret; + rmw_request_id_t response_header; + std::shared_ptr goal_response; + }; + struct CancelResponseData + { + CancelResponseData(rcl_ret_t retIn, rmw_request_id_t header, std::shared_ptr response) + : ret(retIn), response_header(header), cancel_response(response) {} + rcl_ret_t ret; + rmw_request_id_t response_header; + std::shared_ptr cancel_response; + }; + struct ResultResponseData + { + ResultResponseData(rcl_ret_t retIn, rmw_request_id_t header, std::shared_ptr response) + : ret(retIn), response_header(header), result_response(response) {} + rcl_ret_t ret; + rmw_request_id_t response_header; + std::shared_ptr result_response; + }; + + std::variant< + FeedbackReadyData, + StatusReadyData, + GoalResponseData, + CancelResponseData, + ResultResponseData + > data; + + explicit ClientBaseData(FeedbackReadyData && data_in) + : data(std::move(data_in)) {} + explicit ClientBaseData(StatusReadyData && data_in) + : data(std::move(data_in)) {} + explicit ClientBaseData(GoalResponseData && data_in) + : data(std::move(data_in)) {} + explicit ClientBaseData(CancelResponseData && data_in) + : data(std::move(data_in)) {} + explicit ClientBaseData(ResultResponseData && data_in) + : data(std::move(data_in)) {} +}; + class ClientBaseImpl { public: @@ -94,11 +156,13 @@ class ClientBaseImpl size_t num_clients{0u}; size_t num_services{0u}; - bool is_feedback_ready{false}; - bool is_status_ready{false}; - bool is_goal_response_ready{false}; - bool is_cancel_response_ready{false}; - bool is_result_response_ready{false}; + // Lock for action_client_ + std::recursive_mutex action_client_mutex_; + + // next ready event for taking, will be set by is_ready and will be processed by take_data + std::atomic next_ready_event; + // used to indicate that next_ready_event has no ready event for processing + static constexpr size_t NO_EVENT_READY = std::numeric_limits::max(); rclcpp::Context::SharedPtr context_; rclcpp::node_interfaces::NodeGraphInterface::WeakPtr node_graph_; @@ -143,6 +207,7 @@ bool ClientBase::action_server_is_ready() const { bool is_ready; + std::lock_guard lock(pimpl_->action_client_mutex_); rcl_ret_t ret = rcl_action_server_is_available( this->pimpl_->node_handle.get(), this->pimpl_->client_handle.get(), @@ -256,6 +321,7 @@ ClientBase::get_number_of_ready_services() void ClientBase::add_to_wait_set(rcl_wait_set_t * wait_set) { + std::lock_guard lock(pimpl_->action_client_mutex_); rcl_ret_t ret = rcl_action_wait_set_add_action_client( wait_set, pimpl_->client_handle.get(), nullptr, nullptr); if (RCL_RET_OK != ret) { @@ -266,23 +332,56 @@ ClientBase::add_to_wait_set(rcl_wait_set_t * wait_set) bool ClientBase::is_ready(rcl_wait_set_t * wait_set) { - rcl_ret_t ret = rcl_action_client_wait_set_get_entities_ready( - wait_set, pimpl_->client_handle.get(), - &pimpl_->is_feedback_ready, - &pimpl_->is_status_ready, - &pimpl_->is_goal_response_ready, - &pimpl_->is_cancel_response_ready, - &pimpl_->is_result_response_ready); - if (RCL_RET_OK != ret) { - rclcpp::exceptions::throw_from_rcl_error( - ret, "failed to check for any ready entities"); + bool is_feedback_ready{false}; + bool is_status_ready{false}; + bool is_goal_response_ready{false}; + bool is_cancel_response_ready{false}; + bool is_result_response_ready{false}; + + rcl_ret_t ret; + { + std::lock_guard lock(pimpl_->action_client_mutex_); + ret = rcl_action_client_wait_set_get_entities_ready( + wait_set, pimpl_->client_handle.get(), + &is_feedback_ready, + &is_status_ready, + &is_goal_response_ready, + &is_cancel_response_ready, + &is_result_response_ready); + if (RCL_RET_OK != ret) { + rclcpp::exceptions::throw_from_rcl_error( + ret, "failed to check for any ready entities"); + } + } + + pimpl_->next_ready_event = ClientBaseImpl::NO_EVENT_READY; + + if (is_feedback_ready) { + pimpl_->next_ready_event = static_cast(EntityType::FeedbackSubscription); + return true; + } + + if (is_status_ready) { + pimpl_->next_ready_event = static_cast(EntityType::StatusSubscription); + return true; + } + + if (is_goal_response_ready) { + pimpl_->next_ready_event = static_cast(EntityType::GoalClient); + return true; + } + + if (is_result_response_ready) { + pimpl_->next_ready_event = static_cast(EntityType::ResultClient); + return true; + } + + if (is_cancel_response_ready) { + pimpl_->next_ready_event = static_cast(EntityType::CancelClient); + return true; } - return - pimpl_->is_feedback_ready || - pimpl_->is_status_ready || - pimpl_->is_goal_response_ready || - pimpl_->is_cancel_response_ready || - pimpl_->is_result_response_ready; + + return false; } void @@ -432,7 +531,6 @@ ClientBase::set_callback_to_entity( } }; - // Set it temporarily to the new callback, while we replace the old one. // This two-step setting, prevents a gap where the old std::function has // been replaced but the middleware hasn't been told about the new one yet. @@ -550,140 +648,155 @@ ClientBase::clear_on_ready_callback() std::shared_ptr ClientBase::take_data() { - if (pimpl_->is_feedback_ready) { - std::shared_ptr feedback_message = this->create_feedback_message(); - rcl_ret_t ret = rcl_action_take_feedback( - pimpl_->client_handle.get(), feedback_message.get()); - return std::static_pointer_cast( - std::make_shared>>( - ret, feedback_message)); - } else if (pimpl_->is_status_ready) { - std::shared_ptr status_message = this->create_status_message(); - rcl_ret_t ret = rcl_action_take_status( - pimpl_->client_handle.get(), status_message.get()); - return std::static_pointer_cast( - std::make_shared>>( - ret, status_message)); - } else if (pimpl_->is_goal_response_ready) { - rmw_request_id_t response_header; - std::shared_ptr goal_response = this->create_goal_response(); - rcl_ret_t ret = rcl_action_take_goal_response( - pimpl_->client_handle.get(), &response_header, goal_response.get()); - return std::static_pointer_cast( - std::make_shared>>( - ret, response_header, goal_response)); - } else if (pimpl_->is_result_response_ready) { - rmw_request_id_t response_header; - std::shared_ptr result_response = this->create_result_response(); - rcl_ret_t ret = rcl_action_take_result_response( - pimpl_->client_handle.get(), &response_header, result_response.get()); - return std::static_pointer_cast( - std::make_shared>>( - ret, response_header, result_response)); - } else if (pimpl_->is_cancel_response_ready) { - rmw_request_id_t response_header; - std::shared_ptr cancel_response = this->create_cancel_response(); - rcl_ret_t ret = rcl_action_take_cancel_response( - pimpl_->client_handle.get(), &response_header, cancel_response.get()); - return std::static_pointer_cast( - std::make_shared>>( - ret, response_header, cancel_response)); - } else { - throw std::runtime_error("Taking data from action client but nothing is ready"); + // next_ready_event is an atomic, caching localy + size_t next_ready_event = pimpl_->next_ready_event.exchange(ClientBaseImpl::NO_EVENT_READY); + + if (next_ready_event == ClientBaseImpl::NO_EVENT_READY) { + throw std::runtime_error("Taking data from action client but no ready event"); } + + return take_data_by_entity_id(next_ready_event); } std::shared_ptr ClientBase::take_data_by_entity_id(size_t id) { + std::shared_ptr data_ptr; + rcl_ret_t ret; + // Mark as ready the entity from which we want to take data switch (static_cast(id)) { case EntityType::GoalClient: - pimpl_->is_goal_response_ready = true; + { + rmw_request_id_t response_header; + std::shared_ptr goal_response; + { + std::lock_guard lock(pimpl_->action_client_mutex_); + + goal_response = this->create_goal_response(); + ret = rcl_action_take_goal_response( + pimpl_->client_handle.get(), &response_header, goal_response.get()); + } + data_ptr = std::make_shared( + ClientBaseData::GoalResponseData( + ret, response_header, goal_response)); + } break; case EntityType::ResultClient: - pimpl_->is_result_response_ready = true; + { + rmw_request_id_t response_header; + std::shared_ptr result_response; + { + std::lock_guard lock(pimpl_->action_client_mutex_); + result_response = this->create_result_response(); + ret = rcl_action_take_result_response( + pimpl_->client_handle.get(), &response_header, result_response.get()); + } + data_ptr = + std::make_shared( + ClientBaseData::ResultResponseData( + ret, response_header, result_response)); + } break; case EntityType::CancelClient: - pimpl_->is_cancel_response_ready = true; + { + rmw_request_id_t response_header; + std::shared_ptr cancel_response; + { + std::lock_guard lock(pimpl_->action_client_mutex_); + cancel_response = this->create_cancel_response(); + ret = rcl_action_take_cancel_response( + pimpl_->client_handle.get(), &response_header, cancel_response.get()); + } + data_ptr = + std::make_shared( + ClientBaseData::CancelResponseData( + ret, response_header, cancel_response)); + } break; case EntityType::FeedbackSubscription: - pimpl_->is_feedback_ready = true; + { + std::shared_ptr feedback_message; + { + std::lock_guard lock(pimpl_->action_client_mutex_); + feedback_message = this->create_feedback_message(); + ret = rcl_action_take_feedback( + pimpl_->client_handle.get(), feedback_message.get()); + } + data_ptr = + std::make_shared( + ClientBaseData::FeedbackReadyData( + ret, feedback_message)); + } break; case EntityType::StatusSubscription: - pimpl_->is_status_ready = true; + { + std::shared_ptr status_message; + { + std::lock_guard lock(pimpl_->action_client_mutex_); + status_message = this->create_status_message(); + ret = rcl_action_take_status( + pimpl_->client_handle.get(), status_message.get()); + } + data_ptr = + std::make_shared( + ClientBaseData::StatusReadyData( + ret, status_message)); + } break; } - return take_data(); + return std::static_pointer_cast(data_ptr); } void -ClientBase::execute(std::shared_ptr & data) +ClientBase::execute(std::shared_ptr & data_in) { - if (!data) { - throw std::runtime_error("'data' is empty"); + if (!data_in) { + throw std::runtime_error("Executing action client but 'data' is empty"); } - if (pimpl_->is_feedback_ready) { - auto shared_ptr = std::static_pointer_cast>>(data); - auto ret = std::get<0>(*shared_ptr); - pimpl_->is_feedback_ready = false; - if (RCL_RET_OK == ret) { - auto feedback_message = std::get<1>(*shared_ptr); - this->handle_feedback_message(feedback_message); - } else if (RCL_RET_ACTION_CLIENT_TAKE_FAILED != ret) { - rclcpp::exceptions::throw_from_rcl_error(ret, "error taking feedback"); - } - } else if (pimpl_->is_status_ready) { - auto shared_ptr = std::static_pointer_cast>>(data); - auto ret = std::get<0>(*shared_ptr); - pimpl_->is_status_ready = false; - if (RCL_RET_OK == ret) { - auto status_message = std::get<1>(*shared_ptr); - this->handle_status_message(status_message); - } else if (RCL_RET_ACTION_CLIENT_TAKE_FAILED != ret) { - rclcpp::exceptions::throw_from_rcl_error(ret, "error taking status"); - } - } else if (pimpl_->is_goal_response_ready) { - auto shared_ptr = std::static_pointer_cast< - std::tuple>>(data); - auto ret = std::get<0>(*shared_ptr); - pimpl_->is_goal_response_ready = false; - if (RCL_RET_OK == ret) { - auto response_header = std::get<1>(*shared_ptr); - auto goal_response = std::get<2>(*shared_ptr); - this->handle_goal_response(response_header, goal_response); - } else if (RCL_RET_ACTION_CLIENT_TAKE_FAILED != ret) { - rclcpp::exceptions::throw_from_rcl_error(ret, "error taking goal response"); - } - } else if (pimpl_->is_result_response_ready) { - auto shared_ptr = std::static_pointer_cast< - std::tuple>>(data); - auto ret = std::get<0>(*shared_ptr); - pimpl_->is_result_response_ready = false; - if (RCL_RET_OK == ret) { - auto response_header = std::get<1>(*shared_ptr); - auto result_response = std::get<2>(*shared_ptr); - this->handle_result_response(response_header, result_response); - } else if (RCL_RET_ACTION_CLIENT_TAKE_FAILED != ret) { - rclcpp::exceptions::throw_from_rcl_error(ret, "error taking result response"); - } - } else if (pimpl_->is_cancel_response_ready) { - auto shared_ptr = std::static_pointer_cast< - std::tuple>>(data); - auto ret = std::get<0>(*shared_ptr); - pimpl_->is_cancel_response_ready = false; - if (RCL_RET_OK == ret) { - auto response_header = std::get<1>(*shared_ptr); - auto cancel_response = std::get<2>(*shared_ptr); - this->handle_cancel_response(response_header, cancel_response); - } else if (RCL_RET_ACTION_CLIENT_TAKE_FAILED != ret) { - rclcpp::exceptions::throw_from_rcl_error(ret, "error taking cancel response"); - } - } else { - throw std::runtime_error("Executing action client but nothing is ready"); - } + std::shared_ptr data_ptr = std::static_pointer_cast(data_in); + + std::visit( + [&](auto && data) -> void { + using T = std::decay_t; + if constexpr (std::is_same_v) { + if (RCL_RET_OK == data.ret) { + this->handle_feedback_message(data.feedback_message); + } else if (RCL_RET_ACTION_CLIENT_TAKE_FAILED != data.ret) { + rclcpp::exceptions::throw_from_rcl_error(data.ret, "error taking feedback"); + } + } + if constexpr (std::is_same_v) { + if (RCL_RET_OK == data.ret) { + this->handle_status_message(data.status_message); + } else if (RCL_RET_ACTION_CLIENT_TAKE_FAILED != data.ret) { + rclcpp::exceptions::throw_from_rcl_error(data.ret, "error taking status"); + } + } + if constexpr (std::is_same_v) { + if (RCL_RET_OK == data.ret) { + this->handle_goal_response(data.response_header, data.goal_response); + } else if (RCL_RET_ACTION_CLIENT_TAKE_FAILED != data.ret) { + rclcpp::exceptions::throw_from_rcl_error(data.ret, "error taking goal response"); + } + } + if constexpr (std::is_same_v) { + if (RCL_RET_OK == data.ret) { + this->handle_result_response(data.response_header, data.result_response); + } else if (RCL_RET_ACTION_CLIENT_TAKE_FAILED != data.ret) { + rclcpp::exceptions::throw_from_rcl_error(data.ret, "error taking result response"); + } + } + if constexpr (std::is_same_v) { + if (RCL_RET_OK == data.ret) { + this->handle_cancel_response(data.response_header, data.cancel_response); + } else if (RCL_RET_ACTION_CLIENT_TAKE_FAILED != data.ret) { + rclcpp::exceptions::throw_from_rcl_error(data.ret, "error taking cancel response"); + } + } + }, data_ptr->data); } } // namespace rclcpp_action diff --git a/rclcpp_action/src/server.cpp b/rclcpp_action/src/server.cpp index 2ca88185c3..065e40b1f8 100644 --- a/rclcpp_action/src/server.cpp +++ b/rclcpp_action/src/server.cpp @@ -18,6 +18,7 @@ #include #include #include +#include #include #include "rcl_action/action_server.h" @@ -33,8 +34,50 @@ using rclcpp_action::ServerBase; using rclcpp_action::GoalUUID; +struct ServerBaseData; + namespace rclcpp_action { + +struct ServerBaseData +{ + using GoalRequestData = std::tuple< + rcl_ret_t, + const rcl_action_goal_info_t, + rmw_request_id_t, + std::shared_ptr + >; + + using CancelRequestData = std::tuple< + rcl_ret_t, + std::shared_ptr, + rmw_request_id_t + >; + + using ResultRequestData = std::tuple, rmw_request_id_t>; + + using GoalExpiredData = struct Empty {}; + + std::variant data; + + explicit ServerBaseData(GoalRequestData && data_in) + : data(std::move(data_in)) {} + explicit ServerBaseData(CancelRequestData && data_in) + : data(std::move(data_in)) {} + explicit ServerBaseData(ResultRequestData && data_in) + : data(std::move(data_in)) {} + explicit ServerBaseData(GoalExpiredData && data_in) + : data(std::move(data_in)) {} +}; + +enum class ActionEventType : std::size_t +{ + GoalService, + ResultService, + CancelService, + Expired, +}; + class ServerBaseImpl { public: @@ -60,11 +103,6 @@ class ServerBaseImpl size_t num_services_ = 0; size_t num_guard_conditions_ = 0; - std::atomic goal_request_ready_{false}; - std::atomic cancel_request_ready_{false}; - std::atomic result_request_ready_{false}; - std::atomic goal_expired_{false}; - // Lock for unordered_maps std::recursive_mutex unordered_map_mutex_; @@ -75,8 +113,15 @@ class ServerBaseImpl // rcl goal handles are kept so api to send result doesn't try to access freed memory std::unordered_map> goal_handles_; + + // next ready event for taking, will be set by is_ready and will be processed by take_data + std::atomic next_ready_event; + // used to indicate that next_ready_event has no ready event for processing + static constexpr size_t NO_EVENT_READY = std::numeric_limits::max(); + rclcpp::Logger logger_; }; + } // namespace rclcpp_action ServerBase::ServerBase( @@ -195,124 +240,166 @@ ServerBase::is_ready(rcl_wait_set_t * wait_set) &goal_expired); } - pimpl_->goal_request_ready_ = goal_request_ready; - pimpl_->cancel_request_ready_ = cancel_request_ready; - pimpl_->result_request_ready_ = result_request_ready; - pimpl_->goal_expired_ = goal_expired; - if (RCL_RET_OK != ret) { rclcpp::exceptions::throw_from_rcl_error(ret); } - return pimpl_->goal_request_ready_.load() || - pimpl_->cancel_request_ready_.load() || - pimpl_->result_request_ready_.load() || - pimpl_->goal_expired_.load(); -} + pimpl_->next_ready_event = ServerBaseImpl::NO_EVENT_READY; -std::shared_ptr -ServerBase::take_data() -{ - if (pimpl_->goal_request_ready_.load()) { - rcl_ret_t ret; - rcl_action_goal_info_t goal_info = rcl_action_get_zero_initialized_goal_info(); - rmw_request_id_t request_header; + if (goal_request_ready) { + pimpl_->next_ready_event = static_cast(ActionEventType::GoalService); + return true; + } - std::lock_guard lock(pimpl_->action_server_reentrant_mutex_); + if (cancel_request_ready) { + pimpl_->next_ready_event = static_cast(ActionEventType::CancelService); + return true; + } - std::shared_ptr message = create_goal_request(); - ret = rcl_action_take_goal_request( - pimpl_->action_server_.get(), - &request_header, - message.get()); - - return std::static_pointer_cast( - std::make_shared - >>( - ret, - goal_info, - request_header, message)); - } else if (pimpl_->cancel_request_ready_.load()) { - rcl_ret_t ret; - rmw_request_id_t request_header; + if (result_request_ready) { + pimpl_->next_ready_event = static_cast(ActionEventType::ResultService); + return true; + } - // Initialize cancel request - auto request = std::make_shared(); + if (goal_expired) { + pimpl_->next_ready_event = static_cast(ActionEventType::Expired); + return true; + } - std::lock_guard lock(pimpl_->action_server_reentrant_mutex_); - ret = rcl_action_take_cancel_request( - pimpl_->action_server_.get(), - &request_header, - request.get()); + return false; +} - return std::static_pointer_cast( - std::make_shared - , - rmw_request_id_t>>(ret, request, request_header)); - } else if (pimpl_->result_request_ready_.load()) { - rcl_ret_t ret; - // Get the result request message - rmw_request_id_t request_header; - std::shared_ptr result_request = create_result_request(); - std::lock_guard lock(pimpl_->action_server_reentrant_mutex_); - ret = rcl_action_take_result_request( - pimpl_->action_server_.get(), &request_header, result_request.get()); - - return std::static_pointer_cast( - std::make_shared, rmw_request_id_t>>( - ret, result_request, request_header)); - } else if (pimpl_->goal_expired_.load()) { - return nullptr; - } else { - throw std::runtime_error("Taking data from action server but nothing is ready"); +std::shared_ptr +ServerBase::take_data() +{ + size_t next_ready_event = pimpl_->next_ready_event.exchange(ServerBaseImpl::NO_EVENT_READY); + + if (next_ready_event == ServerBaseImpl::NO_EVENT_READY) { + throw std::runtime_error("Taking data from action server but no ready event"); } + + return take_data_by_entity_id(next_ready_event); } std::shared_ptr ServerBase::take_data_by_entity_id(size_t id) { + static_assert( + static_cast(EntityType::GoalService) == + static_cast(ActionEventType::GoalService)); + static_assert( + static_cast(EntityType::ResultService) == + static_cast(ActionEventType::ResultService)); + static_assert( + static_cast(EntityType::CancelService) == + static_cast(ActionEventType::CancelService)); + + std::shared_ptr data_ptr; // Mark as ready the entity from which we want to take data - switch (static_cast(id)) { - case EntityType::GoalService: - pimpl_->goal_request_ready_ = true; + switch (static_cast(id)) { + case ActionEventType::GoalService: + { + rcl_ret_t ret; + rcl_action_goal_info_t goal_info = rcl_action_get_zero_initialized_goal_info(); + rmw_request_id_t request_header; + + std::lock_guard lock(pimpl_->action_server_reentrant_mutex_); + + std::shared_ptr message = create_goal_request(); + ret = rcl_action_take_goal_request( + pimpl_->action_server_.get(), + &request_header, + message.get()); + + data_ptr = std::make_shared( + ServerBaseData::GoalRequestData(ret, goal_info, request_header, message)); + } break; - case EntityType::ResultService: - pimpl_->result_request_ready_ = true; + case ActionEventType::ResultService: + { + rcl_ret_t ret; + // Get the result request message + rmw_request_id_t request_header; + std::shared_ptr result_request = create_result_request(); + std::lock_guard lock(pimpl_->action_server_reentrant_mutex_); + ret = rcl_action_take_result_request( + pimpl_->action_server_.get(), &request_header, result_request.get()); + + data_ptr = + std::make_shared( + ServerBaseData::ResultRequestData(ret, result_request, request_header)); + } break; - case EntityType::CancelService: - pimpl_->cancel_request_ready_ = true; + case ActionEventType::CancelService: + { + rcl_ret_t ret; + rmw_request_id_t request_header; + + // Initialize cancel request + auto request = std::make_shared(); + + std::lock_guard lock(pimpl_->action_server_reentrant_mutex_); + ret = rcl_action_take_cancel_request( + pimpl_->action_server_.get(), + &request_header, + request.get()); + + data_ptr = + std::make_shared( + ServerBaseData::CancelRequestData(ret, request, request_header)); + } + break; + case ActionEventType::Expired: + { + data_ptr = + std::make_shared(ServerBaseData::GoalExpiredData()); + } break; } - return take_data(); + return std::static_pointer_cast(data_ptr); } void -ServerBase::execute(std::shared_ptr & data) +ServerBase::execute(std::shared_ptr & data_in) { - if (!data && !pimpl_->goal_expired_.load()) { - throw std::runtime_error("'data' is empty"); - } - - if (pimpl_->goal_request_ready_.load()) { - execute_goal_request_received(data); - } else if (pimpl_->cancel_request_ready_.load()) { - execute_cancel_request_received(data); - } else if (pimpl_->result_request_ready_.load()) { - execute_result_request_received(data); - } else if (pimpl_->goal_expired_.load()) { - execute_check_expired_goals(); - } else { - throw std::runtime_error("Executing action server but nothing is ready"); + if (!data_in) { + throw std::runtime_error("Executing action server but 'data' is empty"); } + + std::shared_ptr data_ptr = std::static_pointer_cast(data_in); + + std::visit( + [&](auto && data) -> void { + using T = std::decay_t; + if constexpr (std::is_same_v) { + execute_goal_request_received(data_in); + } + if constexpr (std::is_same_v) { + execute_cancel_request_received(data_in); + } + if constexpr (std::is_same_v) { + execute_result_request_received(data_in); + } + if constexpr (std::is_same_v) { + execute_check_expired_goals(); + } + }, + data_ptr->data); } void ServerBase::execute_goal_request_received(std::shared_ptr & data) { - auto shared_ptr = std::static_pointer_cast - >>(data); - rcl_ret_t ret = std::get<0>(*shared_ptr); + std::shared_ptr data_ptr = std::static_pointer_cast(data); + const ServerBaseData::GoalRequestData & gData( + std::get(data_ptr->data)); + + rcl_ret_t ret = std::get<0>(gData); + rcl_action_goal_info_t goal_info = std::get<1>(gData); + rmw_request_id_t request_header = std::get<2>(gData); + const std::shared_ptr message = std::get<3>(gData); + if (RCL_RET_ACTION_SERVER_TAKE_FAILED == ret) { // Ignore take failure because connext fails if it receives a sample without valid data. // This happens when a client shuts down and connext receives a sample saying the client is @@ -321,14 +408,6 @@ ServerBase::execute_goal_request_received(std::shared_ptr & data) } else if (RCL_RET_OK != ret) { rclcpp::exceptions::throw_from_rcl_error(ret); } - rcl_action_goal_info_t goal_info = std::get<1>(*shared_ptr); - rmw_request_id_t request_header = std::get<2>(*shared_ptr); - std::shared_ptr message = std::get<3>(*shared_ptr); - - bool expected = true; - if (!pimpl_->goal_request_ready_.compare_exchange_strong(expected, false)) { - return; - } GoalUUID uuid = get_goal_id_from_goal_request(message.get()); convert(uuid, &goal_info); @@ -412,10 +491,15 @@ ServerBase::execute_goal_request_received(std::shared_ptr & data) void ServerBase::execute_cancel_request_received(std::shared_ptr & data) { - auto shared_ptr = std::static_pointer_cast - , - rmw_request_id_t>>(data); - auto ret = std::get<0>(*shared_ptr); + std::shared_ptr data_ptr = std::static_pointer_cast(data); + const ServerBaseData::CancelRequestData & gData( + std::get(data_ptr->data)); + + rcl_ret_t ret = std::get<0>(gData); + std::shared_ptr request = std::get<1>(gData); + rmw_request_id_t request_header = std::get<2>(gData); + + if (RCL_RET_ACTION_SERVER_TAKE_FAILED == ret) { // Ignore take failure because connext fails if it receives a sample without valid data. // This happens when a client shuts down and connext receives a sample saying the client is @@ -424,9 +508,6 @@ ServerBase::execute_cancel_request_received(std::shared_ptr & data) } else if (RCL_RET_OK != ret) { rclcpp::exceptions::throw_from_rcl_error(ret); } - auto request = std::get<1>(*shared_ptr); - auto request_header = std::get<2>(*shared_ptr); - pimpl_->cancel_request_ready_ = false; // Convert c++ message to C message rcl_action_cancel_request_t cancel_request = rcl_action_get_zero_initialized_cancel_request(); @@ -511,9 +592,14 @@ ServerBase::execute_cancel_request_received(std::shared_ptr & data) void ServerBase::execute_result_request_received(std::shared_ptr & data) { - auto shared_ptr = std::static_pointer_cast - , rmw_request_id_t>>(data); - auto ret = std::get<0>(*shared_ptr); + std::shared_ptr data_ptr = std::static_pointer_cast(data); + const ServerBaseData::ResultRequestData & gData( + std::get(data_ptr->data)); + + rcl_ret_t ret = std::get<0>(gData); + std::shared_ptr result_request = std::get<1>(gData); + rmw_request_id_t request_header = std::get<2>(gData); + if (RCL_RET_ACTION_SERVER_TAKE_FAILED == ret) { // Ignore take failure because connext fails if it receives a sample without valid data. // This happens when a client shuts down and connext receives a sample saying the client is @@ -522,10 +608,7 @@ ServerBase::execute_result_request_received(std::shared_ptr & data) } else if (RCL_RET_OK != ret) { rclcpp::exceptions::throw_from_rcl_error(ret); } - auto result_request = std::get<1>(*shared_ptr); - auto request_header = std::get<2>(*shared_ptr); - pimpl_->result_request_ready_ = false; std::shared_ptr result_response; // check if the goal exists From e9edc3fda0ec9fbdcb64669e7c6dfbb60924feab Mon Sep 17 00:00:00 2001 From: Audrow Nash Date: Mon, 25 Nov 2024 11:41:24 -0600 Subject: [PATCH 50/98] 16.0.11 Signed-off-by: Audrow Nash --- rclcpp/CHANGELOG.rst | 6 ++++++ rclcpp/package.xml | 2 +- rclcpp_action/CHANGELOG.rst | 5 +++++ rclcpp_action/package.xml | 2 +- rclcpp_components/CHANGELOG.rst | 3 +++ rclcpp_components/package.xml | 2 +- rclcpp_lifecycle/CHANGELOG.rst | 3 +++ rclcpp_lifecycle/package.xml | 2 +- 8 files changed, 21 insertions(+), 4 deletions(-) diff --git a/rclcpp/CHANGELOG.rst b/rclcpp/CHANGELOG.rst index 8c940b85ef..31334a86fe 100644 --- a/rclcpp/CHANGELOG.rst +++ b/rclcpp/CHANGELOG.rst @@ -2,6 +2,12 @@ Changelog for package rclcpp ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +16.0.11 (2024-11-25) +-------------------- +* Fix subscription.is_serialized() for callbacks with message info (`#1950 `_) (`#2622 `_) +* Use the same context for the specified node in rclcpp::spin functions… (`#2618 `_) (`#2620 `_) +* Contributors: mergify[bot], roscan-tech + 16.0.10 (2024-07-26) -------------------- * Add test creating two content filter topics with the same topic name (`#2546 `_) (`#2549 `_) (`#2551 `_) diff --git a/rclcpp/package.xml b/rclcpp/package.xml index bca3f07905..3dd8dbf296 100644 --- a/rclcpp/package.xml +++ b/rclcpp/package.xml @@ -2,7 +2,7 @@ rclcpp - 16.0.10 + 16.0.11 The ROS client library in C++. Ivan Paunovic Jacob Perron diff --git a/rclcpp_action/CHANGELOG.rst b/rclcpp_action/CHANGELOG.rst index ddfe99c9d5..72fb4c86ca 100644 --- a/rclcpp_action/CHANGELOG.rst +++ b/rclcpp_action/CHANGELOG.rst @@ -3,6 +3,11 @@ Changelog for package rclcpp_action ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +16.0.11 (2024-11-25) +-------------------- +* fix: Fixed race condition in action server between is_ready and take. Backport from iron `#2531 `_ (`#2635 `_) +* Contributors: Camilo Camacho + 16.0.10 (2024-07-26) -------------------- diff --git a/rclcpp_action/package.xml b/rclcpp_action/package.xml index 687d4df1ee..8bf9020501 100644 --- a/rclcpp_action/package.xml +++ b/rclcpp_action/package.xml @@ -2,7 +2,7 @@ rclcpp_action - 16.0.10 + 16.0.11 Adds action APIs for C++. Ivan Paunovic Jacob Perron diff --git a/rclcpp_components/CHANGELOG.rst b/rclcpp_components/CHANGELOG.rst index 49f69bdfef..fd3c1033c5 100644 --- a/rclcpp_components/CHANGELOG.rst +++ b/rclcpp_components/CHANGELOG.rst @@ -2,6 +2,9 @@ Changelog for package rclcpp_components ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +16.0.11 (2024-11-25) +-------------------- + 16.0.10 (2024-07-26) -------------------- diff --git a/rclcpp_components/package.xml b/rclcpp_components/package.xml index 951a4d9756..b963006b32 100644 --- a/rclcpp_components/package.xml +++ b/rclcpp_components/package.xml @@ -2,7 +2,7 @@ rclcpp_components - 16.0.10 + 16.0.11 Package containing tools for dynamically loadable components Ivan Paunovic Jacob Perron diff --git a/rclcpp_lifecycle/CHANGELOG.rst b/rclcpp_lifecycle/CHANGELOG.rst index ba1a94afbd..1cf408511c 100644 --- a/rclcpp_lifecycle/CHANGELOG.rst +++ b/rclcpp_lifecycle/CHANGELOG.rst @@ -3,6 +3,9 @@ Changelog for package rclcpp_lifecycle ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +16.0.11 (2024-11-25) +-------------------- + 16.0.10 (2024-07-26) -------------------- * revert call shutdown in LifecycleNode destructor (Humble) (`#2560 `_) diff --git a/rclcpp_lifecycle/package.xml b/rclcpp_lifecycle/package.xml index 2b9ec7b48e..fca0807b25 100644 --- a/rclcpp_lifecycle/package.xml +++ b/rclcpp_lifecycle/package.xml @@ -2,7 +2,7 @@ rclcpp_lifecycle - 16.0.10 + 16.0.11 Package containing a prototype for lifecycle implementation Ivan Paunovic Jacob Perron From 1a353f09c05b4f4d2f5b37466f2239ad30aa5fd4 Mon Sep 17 00:00:00 2001 From: "mergify[bot]" <37929162+mergify[bot]@users.noreply.github.com> Date: Sat, 14 Dec 2024 15:17:16 -0800 Subject: [PATCH 51/98] Adding in topic name to logging on IPC issues (#2706) (#2709) * Adding in topic name to logging on IPC issues Signed-off-by: Steve Macenski * Update test matching output logging Signed-off-by: Steve Macenski * adding in single quotes Signed-off-by: Steve Macenski --------- Signed-off-by: Steve Macenski (cherry picked from commit a13e16e2cbaeacb14ff31272d01cbb21bd8ac037) Co-authored-by: Steve Macenski --- rclcpp/include/rclcpp/publisher.hpp | 7 ++++--- rclcpp/include/rclcpp/subscription.hpp | 6 ++++-- rclcpp/test/rclcpp/test_publisher.cpp | 3 ++- 3 files changed, 10 insertions(+), 6 deletions(-) diff --git a/rclcpp/include/rclcpp/publisher.hpp b/rclcpp/include/rclcpp/publisher.hpp index 78b5700611..111d965183 100644 --- a/rclcpp/include/rclcpp/publisher.hpp +++ b/rclcpp/include/rclcpp/publisher.hpp @@ -178,7 +178,6 @@ class Publisher : public PublisherBase const rclcpp::PublisherOptionsWithAllocator & options) { // Topic is unused for now. - (void)topic; (void)options; // If needed, setup intra process communication. @@ -189,11 +188,13 @@ class Publisher : public PublisherBase // Register the publisher with the intra process manager. if (qos.history() != rclcpp::HistoryPolicy::KeepLast) { throw std::invalid_argument( - "intraprocess communication allowed only with keep last history qos policy"); + "intraprocess communication on topic '" + topic + + "' allowed only with keep last history qos policy"); } if (qos.depth() == 0) { throw std::invalid_argument( - "intraprocess communication is not allowed with a zero qos history depth value"); + "intraprocess communication on topic '" + topic + + "' is not allowed with a zero qos history depth value"); } if (qos.durability() != rclcpp::DurabilityPolicy::Volatile) { throw std::invalid_argument( diff --git a/rclcpp/include/rclcpp/subscription.hpp b/rclcpp/include/rclcpp/subscription.hpp index 11bf9c6e43..d00ec4d584 100644 --- a/rclcpp/include/rclcpp/subscription.hpp +++ b/rclcpp/include/rclcpp/subscription.hpp @@ -186,11 +186,13 @@ class Subscription : public SubscriptionBase auto qos_profile = get_actual_qos(); if (qos_profile.history() != rclcpp::HistoryPolicy::KeepLast) { throw std::invalid_argument( - "intraprocess communication allowed only with keep last history qos policy"); + "intraprocess communication on topic '" + topic_name + + "' allowed only with keep last history qos policy"); } if (qos_profile.depth() == 0) { throw std::invalid_argument( - "intraprocess communication is not allowed with 0 depth qos policy"); + "intraprocess communication on topic '" + topic_name + + "' is not allowed with 0 depth qos policy"); } if (qos_profile.durability() != rclcpp::DurabilityPolicy::Volatile) { throw std::invalid_argument( diff --git a/rclcpp/test/rclcpp/test_publisher.cpp b/rclcpp/test/rclcpp/test_publisher.cpp index 1679ca76f0..424615a1f2 100644 --- a/rclcpp/test/rclcpp/test_publisher.cpp +++ b/rclcpp/test/rclcpp/test_publisher.cpp @@ -422,7 +422,8 @@ TEST_F(TestPublisher, intra_process_publish_failures) { node->create_publisher( "topic", rclcpp::QoS(0), options), std::invalid_argument( - "intraprocess communication is not allowed with a zero qos history depth value")); + "intraprocess communication on topic 'topic' " + "is not allowed with a zero qos history depth value")); } TEST_F(TestPublisher, inter_process_publish_failures) { From 99f1d8d1249db9b5b576b326718c58ded59d6caf Mon Sep 17 00:00:00 2001 From: "mergify[bot]" <37929162+mergify[bot]@users.noreply.github.com> Date: Mon, 16 Dec 2024 21:51:50 -0800 Subject: [PATCH 52/98] apply actual QoS from rmw to the IPC publisher. (backport #2707) (#2711) * apply actual QoS from rmw to the IPC publisher. (#2707) * apply actual QoS from rmw to the IPC publisher. Signed-off-by: Tomoya Fujita * address uncrustify warning. Signed-off-by: Tomoya Fujita --------- Signed-off-by: Tomoya Fujita (cherry picked from commit 016cfeac99e4b67f58abdf247e57f05b85c09ec4) # Conflicts: # rclcpp/include/rclcpp/publisher.hpp * resolve conflicts for backport humble. Signed-off-by: Tomoya Fujita * address uncrustify failure. Signed-off-by: Tomoya Fujita --------- Signed-off-by: Tomoya Fujita Co-authored-by: Tomoya Fujita --- rclcpp/include/rclcpp/publisher.hpp | 12 +++++---- .../test/rclcpp/test_create_subscription.cpp | 16 ++++++++++++ rclcpp/test/rclcpp/test_publisher.cpp | 25 ++++++++++++++----- 3 files changed, 42 insertions(+), 11 deletions(-) diff --git a/rclcpp/include/rclcpp/publisher.hpp b/rclcpp/include/rclcpp/publisher.hpp index 111d965183..2e4aa479c0 100644 --- a/rclcpp/include/rclcpp/publisher.hpp +++ b/rclcpp/include/rclcpp/publisher.hpp @@ -177,7 +177,7 @@ class Publisher : public PublisherBase const rclcpp::QoS & qos, const rclcpp::PublisherOptionsWithAllocator & options) { - // Topic is unused for now. + (void)qos; (void)options; // If needed, setup intra process communication. @@ -185,21 +185,23 @@ class Publisher : public PublisherBase auto context = node_base->get_context(); // Get the intra process manager instance for this context. auto ipm = context->get_sub_context(); - // Register the publisher with the intra process manager. - if (qos.history() != rclcpp::HistoryPolicy::KeepLast) { + // Check if the QoS is compatible with intra-process. + auto qos_profile = get_actual_qos(); + if (qos_profile.history() != rclcpp::HistoryPolicy::KeepLast) { throw std::invalid_argument( "intraprocess communication on topic '" + topic + "' allowed only with keep last history qos policy"); } - if (qos.depth() == 0) { + if (qos_profile.depth() == 0) { throw std::invalid_argument( "intraprocess communication on topic '" + topic + "' is not allowed with a zero qos history depth value"); } - if (qos.durability() != rclcpp::DurabilityPolicy::Volatile) { + if (qos_profile.durability() != rclcpp::DurabilityPolicy::Volatile) { throw std::invalid_argument( "intraprocess communication allowed only with volatile durability"); } + // Register the publisher with the intra process manager. uint64_t intra_process_publisher_id = ipm->add_publisher(this->shared_from_this()); this->setup_intra_process( intra_process_publisher_id, diff --git a/rclcpp/test/rclcpp/test_create_subscription.cpp b/rclcpp/test/rclcpp/test_create_subscription.cpp index fd947485e2..a5e14fdb89 100644 --- a/rclcpp/test/rclcpp/test_create_subscription.cpp +++ b/rclcpp/test/rclcpp/test_create_subscription.cpp @@ -93,3 +93,19 @@ TEST_F(TestCreateSubscription, create_with_statistics) { ASSERT_NE(nullptr, subscription); EXPECT_STREQ("/ns/topic_name", subscription->get_topic_name()); } + +TEST_F(TestCreateSubscription, create_with_intra_process_com) { + auto node = std::make_shared("my_node", "/ns"); + auto options = rclcpp::SubscriptionOptions(); + options.use_intra_process_comm = rclcpp::IntraProcessSetting::Enable; + + auto callback = [](test_msgs::msg::Empty::ConstSharedPtr) {}; + rclcpp::Subscription::SharedPtr subscription; + ASSERT_NO_THROW( + { + subscription = rclcpp::create_subscription( + node, "topic_name", rclcpp::SystemDefaultsQoS(), callback, options); + }); + ASSERT_NE(nullptr, subscription); + EXPECT_STREQ("/ns/topic_name", subscription->get_topic_name()); +} diff --git a/rclcpp/test/rclcpp/test_publisher.cpp b/rclcpp/test/rclcpp/test_publisher.cpp index 424615a1f2..31a518f8aa 100644 --- a/rclcpp/test/rclcpp/test_publisher.cpp +++ b/rclcpp/test/rclcpp/test_publisher.cpp @@ -176,6 +176,21 @@ TEST_F(TestPublisher, various_creation_signatures) { } } +/* + Testing publisher with intraprocess enabled and SystemDefaultQoS + */ +TEST_F(TestPublisher, test_publisher_with_system_default_qos) { + initialize(rclcpp::NodeOptions().use_intra_process_comms(false)); + // explicitly enable intra-process comm with publisher option + auto options = rclcpp::PublisherOptions(); + options.use_intra_process_comm = rclcpp::IntraProcessSetting::Enable; + using test_msgs::msg::Empty; + ASSERT_NO_THROW( + { + auto publisher = node->create_publisher("topic", rclcpp::SystemDefaultsQoS()); + }); +} + /* Testing publisher with intraprocess enabled and invalid QoS */ @@ -418,12 +433,10 @@ TEST_F(TestPublisher, intra_process_publish_failures) { publisher->publish(std::move(loaned_msg)), std::runtime_error("loaned message is not valid")); } - RCLCPP_EXPECT_THROW_EQ( - node->create_publisher( - "topic", rclcpp::QoS(0), options), - std::invalid_argument( - "intraprocess communication on topic 'topic' " - "is not allowed with a zero qos history depth value")); + // a zero depth with KEEP_LAST doesn't make sense, + // this will be interpreted as SystemDefaultQoS by rclcpp. + EXPECT_NO_THROW( + node->create_publisher("topic", rclcpp::QoS(0), options)); } TEST_F(TestPublisher, inter_process_publish_failures) { From 19773973a80d753c6fa028b0b548462fbbef122d Mon Sep 17 00:00:00 2001 From: LihanChen2004 <74599182+LihanChen2004@users.noreply.github.com> Date: Mon, 30 Dec 2024 07:51:58 +0800 Subject: [PATCH 53/98] Redundant .c_str() usage in rclcpp_components triggers ament_clang_tidy warning (#2718) * fix: Simplify string assignment for class name in node_main.cpp.in Signed-off-by: LihanChen2004 <757003373@qq.com> * Remove redundant local variable `name` Co-authored-by: Tomoya Fujita Signed-off-by: LihanChen2004 <74599182+LihanChen2004@users.noreply.github.com> --------- Signed-off-by: LihanChen2004 <757003373@qq.com> Signed-off-by: LihanChen2004 <74599182+LihanChen2004@users.noreply.github.com> Co-authored-by: Tomoya Fujita --- rclcpp_components/src/node_main.cpp.in | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/rclcpp_components/src/node_main.cpp.in b/rclcpp_components/src/node_main.cpp.in index 0ca5eb8c61..1d39c855e4 100644 --- a/rclcpp_components/src/node_main.cpp.in +++ b/rclcpp_components/src/node_main.cpp.in @@ -40,8 +40,7 @@ int main(int argc, char * argv[]) auto loader = new class_loader::ClassLoader(library_name); auto classes = loader->getAvailableClasses(); for (const auto & clazz : classes) { - std::string name = clazz.c_str(); - if (name.compare(class_name) == 0) { + if (clazz.compare(class_name) == 0) { RCLCPP_DEBUG(logger, "Instantiate class %s", clazz.c_str()); std::shared_ptr node_factory = nullptr; try { From d3e6254ff149f96a83e35135d5787445ddae2431 Mon Sep 17 00:00:00 2001 From: "mergify[bot]" <37929162+mergify[bot]@users.noreply.github.com> Date: Fri, 14 Mar 2025 17:25:09 -0700 Subject: [PATCH 54/98] doc: Added warning to not instantiate Clock directly with RCL_ROS_TIME (#2768) (#2770) Signed-off-by: Janosch Machowinski Signed-off-by: Janosch Machowinski Co-authored-by: Janosch Machowinski Co-authored-by: Tomoya Fujita (cherry picked from commit 30e61c955d42aa4620e166e22745558825801e34) Co-authored-by: Janosch Machowinski --- rclcpp/include/rclcpp/clock.hpp | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/rclcpp/include/rclcpp/clock.hpp b/rclcpp/include/rclcpp/clock.hpp index c41f17312b..d781aa4493 100644 --- a/rclcpp/include/rclcpp/clock.hpp +++ b/rclcpp/include/rclcpp/clock.hpp @@ -60,6 +60,13 @@ class Clock /** * Initializes the clock instance with the given clock_type. * + * WARNING Don't instantiate a clock using RCL_ROS_TIME directly, + * unless you really know what you are doing. By default no TimeSource + * is attached to a new clock. This will lead to the unexpected behavior, + * that your RCL_ROS_TIME will run always on system time. If you want + * a RCL_ROS_TIME use Node::get_clock(), or make sure to attach a + * TimeSource yourself. + * * \param clock_type type of the clock. * \throws anything rclcpp::exceptions::throw_from_rcl_error can throw. */ From 6084057f89dc311858a6fda0a82546ac63b54f16 Mon Sep 17 00:00:00 2001 From: Audrow Nash Date: Tue, 25 Mar 2025 08:19:47 -0500 Subject: [PATCH 55/98] 16.0.12 Signed-off-by: Audrow Nash --- rclcpp/CHANGELOG.rst | 7 +++++++ rclcpp/package.xml | 2 +- rclcpp_action/CHANGELOG.rst | 3 +++ rclcpp_action/package.xml | 2 +- rclcpp_components/CHANGELOG.rst | 5 +++++ rclcpp_components/package.xml | 2 +- rclcpp_lifecycle/CHANGELOG.rst | 3 +++ rclcpp_lifecycle/package.xml | 2 +- 8 files changed, 22 insertions(+), 4 deletions(-) diff --git a/rclcpp/CHANGELOG.rst b/rclcpp/CHANGELOG.rst index 31334a86fe..e7c6eb25db 100644 --- a/rclcpp/CHANGELOG.rst +++ b/rclcpp/CHANGELOG.rst @@ -2,6 +2,13 @@ Changelog for package rclcpp ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +16.0.12 (2025-03-25) +-------------------- +* doc: Added warning to not instantiate Clock directly with RCL_ROS_TIME (`#2768 `_) (`#2770 `_) +* apply actual QoS from rmw to the IPC publisher. (backport `#2707 `_) (`#2711 `_) +* Adding in topic name to logging on IPC issues (`#2706 `_) (`#2709 `_) +* Contributors: mergify[bot] + 16.0.11 (2024-11-25) -------------------- * Fix subscription.is_serialized() for callbacks with message info (`#1950 `_) (`#2622 `_) diff --git a/rclcpp/package.xml b/rclcpp/package.xml index 3dd8dbf296..b144d4ccf5 100644 --- a/rclcpp/package.xml +++ b/rclcpp/package.xml @@ -2,7 +2,7 @@ rclcpp - 16.0.11 + 16.0.12 The ROS client library in C++. Ivan Paunovic Jacob Perron diff --git a/rclcpp_action/CHANGELOG.rst b/rclcpp_action/CHANGELOG.rst index 72fb4c86ca..4c775268ce 100644 --- a/rclcpp_action/CHANGELOG.rst +++ b/rclcpp_action/CHANGELOG.rst @@ -3,6 +3,9 @@ Changelog for package rclcpp_action ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +16.0.12 (2025-03-25) +-------------------- + 16.0.11 (2024-11-25) -------------------- * fix: Fixed race condition in action server between is_ready and take. Backport from iron `#2531 `_ (`#2635 `_) diff --git a/rclcpp_action/package.xml b/rclcpp_action/package.xml index 8bf9020501..83472a6500 100644 --- a/rclcpp_action/package.xml +++ b/rclcpp_action/package.xml @@ -2,7 +2,7 @@ rclcpp_action - 16.0.11 + 16.0.12 Adds action APIs for C++. Ivan Paunovic Jacob Perron diff --git a/rclcpp_components/CHANGELOG.rst b/rclcpp_components/CHANGELOG.rst index fd3c1033c5..91f16aa5b7 100644 --- a/rclcpp_components/CHANGELOG.rst +++ b/rclcpp_components/CHANGELOG.rst @@ -2,6 +2,11 @@ Changelog for package rclcpp_components ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +16.0.12 (2025-03-25) +-------------------- +* Redundant .c_str() usage in rclcpp_components triggers ament_clang_tidy warning (`#2718 `_) +* Contributors: LihanChen2004 + 16.0.11 (2024-11-25) -------------------- diff --git a/rclcpp_components/package.xml b/rclcpp_components/package.xml index b963006b32..160a33d47f 100644 --- a/rclcpp_components/package.xml +++ b/rclcpp_components/package.xml @@ -2,7 +2,7 @@ rclcpp_components - 16.0.11 + 16.0.12 Package containing tools for dynamically loadable components Ivan Paunovic Jacob Perron diff --git a/rclcpp_lifecycle/CHANGELOG.rst b/rclcpp_lifecycle/CHANGELOG.rst index 1cf408511c..058d717a05 100644 --- a/rclcpp_lifecycle/CHANGELOG.rst +++ b/rclcpp_lifecycle/CHANGELOG.rst @@ -3,6 +3,9 @@ Changelog for package rclcpp_lifecycle ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +16.0.12 (2025-03-25) +-------------------- + 16.0.11 (2024-11-25) -------------------- diff --git a/rclcpp_lifecycle/package.xml b/rclcpp_lifecycle/package.xml index fca0807b25..c4859d02f2 100644 --- a/rclcpp_lifecycle/package.xml +++ b/rclcpp_lifecycle/package.xml @@ -2,7 +2,7 @@ rclcpp_lifecycle - 16.0.11 + 16.0.12 Package containing a prototype for lifecycle implementation Ivan Paunovic Jacob Perron From 493fe2b5d5fffe4c7c4ec2d4197a3fc955a42048 Mon Sep 17 00:00:00 2001 From: "mergify[bot]" <37929162+mergify[bot]@users.noreply.github.com> Date: Sun, 30 Mar 2025 11:33:00 -0700 Subject: [PATCH 56/98] Harden rclcpp_action::convert(). (backport #2786) (#2788) * Harden rclcpp_action::convert(). (#2786) * Harden rclcpp_action::convert(). Signed-off-by: Tomoya Fujita * update docstring. Signed-off-by: Tomoya Fujita --------- Signed-off-by: Tomoya Fujita (cherry picked from commit ce86ef7e621d96ce50d6ec1b49e9e1cd4f0a828b) # Conflicts: # rclcpp_action/src/types.cpp * resolve conflicts. Signed-off-by: Tomoya Fujita --------- Signed-off-by: Tomoya Fujita Co-authored-by: Tomoya Fujita --- rclcpp_action/include/rclcpp_action/types.hpp | 14 ++++++++++++-- rclcpp_action/src/types.cpp | 6 ++++++ rclcpp_action/test/test_types.cpp | 2 ++ 3 files changed, 20 insertions(+), 2 deletions(-) diff --git a/rclcpp_action/include/rclcpp_action/types.hpp b/rclcpp_action/include/rclcpp_action/types.hpp index 4f8548ff76..fbbcb9b82a 100644 --- a/rclcpp_action/include/rclcpp_action/types.hpp +++ b/rclcpp_action/include/rclcpp_action/types.hpp @@ -39,12 +39,22 @@ RCLCPP_ACTION_PUBLIC std::string to_string(const GoalUUID & goal_id); -// Convert C++ GoalID to rcl_action_goal_info_t +/// Convert C++ GoalID to rcl_action_goal_info_t +/** + * \param[in] goal_id C++ GoalUUID reference to be converted. + * \param[inout] info rcl_action_goal_info_t structure to be filled. + * \throws std::runtime_error if info is null. + */ RCLCPP_ACTION_PUBLIC void convert(const GoalUUID & goal_id, rcl_action_goal_info_t * info); -// Convert rcl_action_goal_info_t to C++ GoalID +/// Convert rcl_action_goal_info_t to C++ GoalID +/** + * \param[in] info rcl_action_goal_info_t reference to be converted. + * \param[inout] goal_id C++ GoalUUID structure to be filled. + * \throws std::runtime_error if goal_id is null. + */ RCLCPP_ACTION_PUBLIC void convert(const rcl_action_goal_info_t & info, GoalUUID * goal_id); diff --git a/rclcpp_action/src/types.cpp b/rclcpp_action/src/types.cpp index 773702789e..b392ec7fac 100644 --- a/rclcpp_action/src/types.cpp +++ b/rclcpp_action/src/types.cpp @@ -33,6 +33,9 @@ to_string(const GoalUUID & goal_id) void convert(const GoalUUID & goal_id, rcl_action_goal_info_t * info) { + if (info == nullptr) { + throw std::invalid_argument("info is nullptr"); + } for (size_t i = 0; i < 16; ++i) { info->goal_id.uuid[i] = goal_id[i]; } @@ -41,6 +44,9 @@ convert(const GoalUUID & goal_id, rcl_action_goal_info_t * info) void convert(const rcl_action_goal_info_t & info, GoalUUID * goal_id) { + if (goal_id == nullptr) { + throw std::invalid_argument("goal_id is nullptr"); + } for (size_t i = 0; i < 16; ++i) { (*goal_id)[i] = info.goal_id.uuid[i]; } diff --git a/rclcpp_action/test/test_types.cpp b/rclcpp_action/test/test_types.cpp index 7c652aaad6..3cb97c62f7 100644 --- a/rclcpp_action/test/test_types.cpp +++ b/rclcpp_action/test/test_types.cpp @@ -40,6 +40,7 @@ TEST(TestActionTypes, goal_uuid_to_rcl_action_goal_info) { for (uint8_t i = 0; i < UUID_SIZE; ++i) { goal_id[i] = i; } + ASSERT_THROW(rclcpp_action::convert(goal_id, nullptr), std::invalid_argument); rcl_action_goal_info_t goal_info = rcl_action_get_zero_initialized_goal_info(); rclcpp_action::convert(goal_id, &goal_info); for (uint8_t i = 0; i < UUID_SIZE; ++i) { @@ -53,6 +54,7 @@ TEST(TestActionTypes, rcl_action_goal_info_to_goal_uuid) { goal_info.goal_id.uuid[i] = i; } + ASSERT_THROW(rclcpp_action::convert(goal_info, nullptr), std::invalid_argument); rclcpp_action::GoalUUID goal_id; rclcpp_action::convert(goal_id, &goal_info); for (uint8_t i = 0; i < UUID_SIZE; ++i) { From c751dfb76ba9bd50c5933dedf472b29ab063416b Mon Sep 17 00:00:00 2001 From: "mergify[bot]" <37929162+mergify[bot]@users.noreply.github.com> Date: Mon, 31 Mar 2025 10:10:56 +0200 Subject: [PATCH 57/98] should pull valid transition before trying to change the state. (backport #2774) (#2785) * should pull valid transition before trying to change the state. (#2774) Signed-off-by: Tomoya Fujita (cherry picked from commit 7b6ee8a2e7a13d73f9f69368970390a9e0930448) --- .../src/lifecycle_node_interface_impl.hpp | 15 +- rclcpp_lifecycle/test/test_lifecycle_node.cpp | 135 ++++++++++++++++++ 2 files changed, 146 insertions(+), 4 deletions(-) diff --git a/rclcpp_lifecycle/src/lifecycle_node_interface_impl.hpp b/rclcpp_lifecycle/src/lifecycle_node_interface_impl.hpp index 13110d29c7..c292622175 100644 --- a/rclcpp_lifecycle/src/lifecycle_node_interface_impl.hpp +++ b/rclcpp_lifecycle/src/lifecycle_node_interface_impl.hpp @@ -535,15 +535,22 @@ class LifecycleNode::LifecycleNodeInterfaceImpl trigger_transition(uint8_t transition_id) { LifecycleNodeInterface::CallbackReturn error; - change_state(transition_id, error); - (void) error; - return get_current_state(); + return trigger_transition(transition_id, error); } const State & trigger_transition(uint8_t transition_id, LifecycleNodeInterface::CallbackReturn & cb_return_code) { - change_state(transition_id, cb_return_code); + const rcl_lifecycle_transition_t * transition; + { + std::lock_guard lock(state_machine_mutex_); + + transition = + rcl_lifecycle_get_transition_by_id(state_machine_.current_state, transition_id); + } + if (transition) { + change_state(static_cast(transition->id), cb_return_code); + } return get_current_state(); } diff --git a/rclcpp_lifecycle/test/test_lifecycle_node.cpp b/rclcpp_lifecycle/test/test_lifecycle_node.cpp index 5a3054781b..fc3ac74baf 100644 --- a/rclcpp_lifecycle/test/test_lifecycle_node.cpp +++ b/rclcpp_lifecycle/test/test_lifecycle_node.cpp @@ -264,11 +264,146 @@ TEST_F(TestDefaultStateMachine, trigger_transition) { ASSERT_EQ( State::PRIMARY_STATE_UNCONFIGURED, test_node->trigger_transition( rclcpp_lifecycle::Transition(Transition::TRANSITION_CLEANUP)).id()); + // supposed to fail because primary state is NOT active + ASSERT_EQ( + State::PRIMARY_STATE_UNCONFIGURED, test_node->trigger_transition( + rclcpp_lifecycle::Transition(Transition::TRANSITION_ACTIVE_SHUTDOWN)).id()); + // supposed to fail because primary state is NOT inactive + ASSERT_EQ( + State::PRIMARY_STATE_UNCONFIGURED, test_node->trigger_transition( + rclcpp_lifecycle::Transition(Transition::TRANSITION_INACTIVE_SHUTDOWN)).id()); ASSERT_EQ( State::PRIMARY_STATE_FINALIZED, test_node->trigger_transition( rclcpp_lifecycle::Transition(Transition::TRANSITION_UNCONFIGURED_SHUTDOWN)).id()); } +TEST_F(TestDefaultStateMachine, trigger_transition_shutdown_id) { + // test Transition::TRANSITION_ACTIVE_SHUTDOWN + { + auto test_node = std::make_shared("testnode"); + + EXPECT_EQ(State::PRIMARY_STATE_UNCONFIGURED, test_node->get_current_state().id()); + ASSERT_EQ( + State::PRIMARY_STATE_INACTIVE, test_node->trigger_transition( + rclcpp_lifecycle::Transition(Transition::TRANSITION_CONFIGURE)).id()); + ASSERT_EQ( + State::PRIMARY_STATE_ACTIVE, test_node->trigger_transition( + rclcpp_lifecycle::Transition(Transition::TRANSITION_ACTIVATE)).id()); + ASSERT_EQ( + State::PRIMARY_STATE_FINALIZED, test_node->trigger_transition( + rclcpp_lifecycle::Transition(Transition::TRANSITION_ACTIVE_SHUTDOWN)).id()); + } + + // test Transition::TRANSITION_INACTIVE_SHUTDOWN + { + auto test_node = std::make_shared("testnode"); + + EXPECT_EQ(State::PRIMARY_STATE_UNCONFIGURED, test_node->get_current_state().id()); + ASSERT_EQ( + State::PRIMARY_STATE_INACTIVE, test_node->trigger_transition( + rclcpp_lifecycle::Transition(Transition::TRANSITION_CONFIGURE)).id()); + ASSERT_EQ( + State::PRIMARY_STATE_ACTIVE, test_node->trigger_transition( + rclcpp_lifecycle::Transition(Transition::TRANSITION_ACTIVATE)).id()); + ASSERT_EQ( + State::PRIMARY_STATE_INACTIVE, test_node->trigger_transition( + rclcpp_lifecycle::Transition(Transition::TRANSITION_DEACTIVATE)).id()); + // supposed to fail because primary state is NOT active + ASSERT_EQ( + State::PRIMARY_STATE_INACTIVE, test_node->trigger_transition( + rclcpp_lifecycle::Transition(Transition::TRANSITION_ACTIVE_SHUTDOWN)).id()); + ASSERT_EQ( + State::PRIMARY_STATE_FINALIZED, test_node->trigger_transition( + rclcpp_lifecycle::Transition(Transition::TRANSITION_INACTIVE_SHUTDOWN)).id()); + } + + // test Transition::TRANSITION_UNCONFIGURED_SHUTDOWN + { + auto test_node = std::make_shared("testnode"); + + EXPECT_EQ(State::PRIMARY_STATE_UNCONFIGURED, test_node->get_current_state().id()); + ASSERT_EQ( + State::PRIMARY_STATE_INACTIVE, test_node->trigger_transition( + rclcpp_lifecycle::Transition(Transition::TRANSITION_CONFIGURE)).id()); + ASSERT_EQ( + State::PRIMARY_STATE_ACTIVE, test_node->trigger_transition( + rclcpp_lifecycle::Transition(Transition::TRANSITION_ACTIVATE)).id()); + ASSERT_EQ( + State::PRIMARY_STATE_INACTIVE, test_node->trigger_transition( + rclcpp_lifecycle::Transition(Transition::TRANSITION_DEACTIVATE)).id()); + ASSERT_EQ( + State::PRIMARY_STATE_UNCONFIGURED, test_node->trigger_transition( + rclcpp_lifecycle::Transition(Transition::TRANSITION_CLEANUP)).id()); + // supposed to fail because primary state is NOT active + ASSERT_EQ( + State::PRIMARY_STATE_UNCONFIGURED, test_node->trigger_transition( + rclcpp_lifecycle::Transition(Transition::TRANSITION_ACTIVE_SHUTDOWN)).id()); + // supposed to fail because primary state is NOT inactive + ASSERT_EQ( + State::PRIMARY_STATE_UNCONFIGURED, test_node->trigger_transition( + rclcpp_lifecycle::Transition(Transition::TRANSITION_INACTIVE_SHUTDOWN)).id()); + ASSERT_EQ( + State::PRIMARY_STATE_FINALIZED, test_node->trigger_transition( + rclcpp_lifecycle::Transition(Transition::TRANSITION_UNCONFIGURED_SHUTDOWN)).id()); + } +} + +TEST_F(TestDefaultStateMachine, trigger_transition_shutdown_label) { + // test Transition::TRANSITION_ACTIVE_SHUTDOWN + { + auto test_node = std::make_shared("testnode"); + + EXPECT_EQ(State::PRIMARY_STATE_UNCONFIGURED, test_node->get_current_state().id()); + ASSERT_EQ( + State::PRIMARY_STATE_INACTIVE, test_node->trigger_transition( + rclcpp_lifecycle::Transition(Transition::TRANSITION_CONFIGURE)).id()); + ASSERT_EQ( + State::PRIMARY_STATE_ACTIVE, test_node->trigger_transition( + rclcpp_lifecycle::Transition(Transition::TRANSITION_ACTIVATE)).id()); + ASSERT_EQ( + State::PRIMARY_STATE_FINALIZED, test_node->shutdown().id()); + } + + // test Transition::TRANSITION_INACTIVE_SHUTDOWN + { + auto test_node = std::make_shared("testnode"); + + EXPECT_EQ(State::PRIMARY_STATE_UNCONFIGURED, test_node->get_current_state().id()); + ASSERT_EQ( + State::PRIMARY_STATE_INACTIVE, test_node->trigger_transition( + rclcpp_lifecycle::Transition(Transition::TRANSITION_CONFIGURE)).id()); + ASSERT_EQ( + State::PRIMARY_STATE_ACTIVE, test_node->trigger_transition( + rclcpp_lifecycle::Transition(Transition::TRANSITION_ACTIVATE)).id()); + ASSERT_EQ( + State::PRIMARY_STATE_INACTIVE, test_node->trigger_transition( + rclcpp_lifecycle::Transition(Transition::TRANSITION_DEACTIVATE)).id()); + ASSERT_EQ( + State::PRIMARY_STATE_FINALIZED, test_node->shutdown().id()); + } + + // test Transition::TRANSITION_UNCONFIGURED_SHUTDOWN + { + auto test_node = std::make_shared("testnode"); + + EXPECT_EQ(State::PRIMARY_STATE_UNCONFIGURED, test_node->get_current_state().id()); + ASSERT_EQ( + State::PRIMARY_STATE_INACTIVE, test_node->trigger_transition( + rclcpp_lifecycle::Transition(Transition::TRANSITION_CONFIGURE)).id()); + ASSERT_EQ( + State::PRIMARY_STATE_ACTIVE, test_node->trigger_transition( + rclcpp_lifecycle::Transition(Transition::TRANSITION_ACTIVATE)).id()); + ASSERT_EQ( + State::PRIMARY_STATE_INACTIVE, test_node->trigger_transition( + rclcpp_lifecycle::Transition(Transition::TRANSITION_DEACTIVATE)).id()); + ASSERT_EQ( + State::PRIMARY_STATE_UNCONFIGURED, test_node->trigger_transition( + rclcpp_lifecycle::Transition(Transition::TRANSITION_CLEANUP)).id()); + ASSERT_EQ( + State::PRIMARY_STATE_FINALIZED, test_node->shutdown().id()); + } +} + TEST_F(TestDefaultStateMachine, trigger_transition_rcl_errors) { auto test_node = std::make_shared("testnode"); From 1b040e7df84b61f4738f5dea5716cdf57b565a4d Mon Sep 17 00:00:00 2001 From: "mergify[bot]" <37929162+mergify[bot]@users.noreply.github.com> Date: Tue, 15 Apr 2025 09:40:57 +0200 Subject: [PATCH 58/98] remove redundant typesupport check in serialization module (#2808) (#2816) Signed-off-by: Tanishq Chaudhary (cherry picked from commit f78ed952b27acc63ef8022d78cb816c309a9ca3d) Co-authored-by: Tanishq Chaudhary --- rclcpp/src/rclcpp/serialization.cpp | 2 -- 1 file changed, 2 deletions(-) diff --git a/rclcpp/src/rclcpp/serialization.cpp b/rclcpp/src/rclcpp/serialization.cpp index bca341185f..890b3e4f50 100644 --- a/rclcpp/src/rclcpp/serialization.cpp +++ b/rclcpp/src/rclcpp/serialization.cpp @@ -36,7 +36,6 @@ SerializationBase::SerializationBase(const rosidl_message_type_support_t * type_ void SerializationBase::serialize_message( const void * ros_message, SerializedMessage * serialized_message) const { - rcpputils::check_true(nullptr != type_support_, "Typesupport is nullpointer."); rcpputils::check_true(nullptr != ros_message, "ROS message is nullpointer."); rcpputils::check_true(nullptr != serialized_message, "Serialized message is nullpointer."); @@ -52,7 +51,6 @@ void SerializationBase::serialize_message( void SerializationBase::deserialize_message( const SerializedMessage * serialized_message, void * ros_message) const { - rcpputils::check_true(nullptr != type_support_, "Typesupport is nullpointer."); rcpputils::check_true(nullptr != serialized_message, "Serialized message is nullpointer."); rcpputils::check_true( 0u != serialized_message->capacity(), From b6cd8393db6865c0a9ef92911f9aa7b3e98aad8a Mon Sep 17 00:00:00 2001 From: "mergify[bot]" <37929162+mergify[bot]@users.noreply.github.com> Date: Thu, 1 May 2025 11:15:09 -0700 Subject: [PATCH 59/98] throws std::invalid_argument if ParameterEvent is NULL. (#2814) (#2824) Signed-off-by: Tomoya Fujita (cherry picked from commit 8b9691f42d5f20dbd737fc712c95f4154dde214e) Co-authored-by: Tomoya Fujita --- rclcpp/include/rclcpp/parameter_events_filter.hpp | 1 + rclcpp/src/rclcpp/parameter_events_filter.cpp | 3 +++ rclcpp/test/rclcpp/test_parameter_events_filter.cpp | 7 +++++++ 3 files changed, 11 insertions(+) diff --git a/rclcpp/include/rclcpp/parameter_events_filter.hpp b/rclcpp/include/rclcpp/parameter_events_filter.hpp index 3aa70d8a85..98a2027276 100644 --- a/rclcpp/include/rclcpp/parameter_events_filter.hpp +++ b/rclcpp/include/rclcpp/parameter_events_filter.hpp @@ -45,6 +45,7 @@ class ParameterEventsFilter * \param[in] names A list of parameter names of interest. * \param[in] types A list of the types of parameter events of iterest. * EventType NEW, DELETED, or CHANGED + * \throws std::invalid_argument if event is NULL. * * Example Usage: * diff --git a/rclcpp/src/rclcpp/parameter_events_filter.cpp b/rclcpp/src/rclcpp/parameter_events_filter.cpp index be9882c85b..44e7f57d55 100644 --- a/rclcpp/src/rclcpp/parameter_events_filter.cpp +++ b/rclcpp/src/rclcpp/parameter_events_filter.cpp @@ -28,6 +28,9 @@ ParameterEventsFilter::ParameterEventsFilter( const std::vector & types) : event_(event) { + if (!event) { + throw std::invalid_argument("event cannot be null"); + } if (std::find(types.begin(), types.end(), EventType::NEW) != types.end()) { for (auto & new_parameter : event->new_parameters) { if (std::find(names.begin(), names.end(), new_parameter.name) != names.end()) { diff --git a/rclcpp/test/rclcpp/test_parameter_events_filter.cpp b/rclcpp/test/rclcpp/test_parameter_events_filter.cpp index a497a28c93..1c4d7c06c7 100644 --- a/rclcpp/test/rclcpp/test_parameter_events_filter.cpp +++ b/rclcpp/test/rclcpp/test_parameter_events_filter.cpp @@ -72,6 +72,13 @@ class TestParameterEventFilter : public ::testing::Test /* Testing filters. */ +TEST_F(TestParameterEventFilter, invalide_arguments) { + EXPECT_THROW( + rclcpp::ParameterEventsFilter(nullptr, {"new"}, {nt}), + std::invalid_argument + ); +} + TEST_F(TestParameterEventFilter, full_by_type) { auto res = rclcpp::ParameterEventsFilter( full, From 53eed447713377d0859fd8f736496c76ffd4fa20 Mon Sep 17 00:00:00 2001 From: "mergify[bot]" <37929162+mergify[bot]@users.noreply.github.com> Date: Mon, 19 May 2025 16:02:59 +0200 Subject: [PATCH 60/98] =?UTF-8?q?QoSInitialization::from=5Frmw=20does=20no?= =?UTF-8?q?t=20validate=20invalid=20history=20policy=20=E2=80=A6=20(backpo?= =?UTF-8?q?rt=20#2841)=20(#2844)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Alejandro Hernandez Cordero Co-authored-by: Alejandro Hernández Cordero --- rclcpp/src/rclcpp/qos.cpp | 4 +++- rclcpp/test/rclcpp/test_qos.cpp | 12 ++++++++++++ 2 files changed, 15 insertions(+), 1 deletion(-) diff --git a/rclcpp/src/rclcpp/qos.cpp b/rclcpp/src/rclcpp/qos.cpp index 8b912de07f..ee37bdb575 100644 --- a/rclcpp/src/rclcpp/qos.cpp +++ b/rclcpp/src/rclcpp/qos.cpp @@ -56,8 +56,10 @@ QoSInitialization::from_rmw(const rmw_qos_profile_t & rmw_qos) case RMW_QOS_POLICY_HISTORY_KEEP_LAST: case RMW_QOS_POLICY_HISTORY_SYSTEM_DEFAULT: case RMW_QOS_POLICY_HISTORY_UNKNOWN: - default: return KeepLast(rmw_qos.depth); + default: + throw std::invalid_argument( + "Invalid history policy enum value passed to QoSInitialization::from_rmw"); } } diff --git a/rclcpp/test/rclcpp/test_qos.cpp b/rclcpp/test/rclcpp/test_qos.cpp index 47d1d10b07..017f189781 100644 --- a/rclcpp/test/rclcpp/test_qos.cpp +++ b/rclcpp/test/rclcpp/test_qos.cpp @@ -250,3 +250,15 @@ TEST(TestQoS, qos_check_compatible) EXPECT_FALSE(ret.reason.empty()); } } + +TEST(TestQoS, from_rmw_validity) +{ + rmw_qos_profile_t invalid_qos; + memset(&invalid_qos, 0, sizeof(invalid_qos)); + reinterpret_cast(invalid_qos.history) = 999; + + EXPECT_THROW( + { + rclcpp::QoSInitialization::from_rmw(invalid_qos); + }, std::invalid_argument); +} From f43d4edc6bdb59786c9fcb07929e02444c90778d Mon Sep 17 00:00:00 2001 From: "mergify[bot]" <37929162+mergify[bot]@users.noreply.github.com> Date: Mon, 26 May 2025 13:29:17 +0200 Subject: [PATCH 61/98] Added missing chrono includes (backport #2854) (#2857) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Alejandro Hernandez Cordero Co-authored-by: Alejandro Hernández Cordero --- rclcpp/test/benchmark/benchmark_executor.cpp | 1 + rclcpp/test/rclcpp/test_generic_pubsub.cpp | 1 + rclcpp/test/rclcpp/test_parameter_service.cpp | 1 + rclcpp/test/rclcpp/test_service.cpp | 1 + rclcpp/test/rclcpp/test_wait_for_message.cpp | 1 + rclcpp_action/test/test_client.cpp | 2 +- rclcpp_components/test/test_component_manager_api.cpp | 1 + rclcpp_lifecycle/test/benchmark/benchmark_lifecycle_client.cpp | 1 + rclcpp_lifecycle/test/test_lifecycle_node.cpp | 1 + 9 files changed, 9 insertions(+), 1 deletion(-) diff --git a/rclcpp/test/benchmark/benchmark_executor.cpp b/rclcpp/test/benchmark/benchmark_executor.cpp index 652007b589..21fbb34962 100644 --- a/rclcpp/test/benchmark/benchmark_executor.cpp +++ b/rclcpp/test/benchmark/benchmark_executor.cpp @@ -12,6 +12,7 @@ // See the License for the specific language governing permissions and // limitations under the License. +#include #include #include #include diff --git a/rclcpp/test/rclcpp/test_generic_pubsub.cpp b/rclcpp/test/rclcpp/test_generic_pubsub.cpp index 0d2bec588a..c678c9d6de 100644 --- a/rclcpp/test/rclcpp/test_generic_pubsub.cpp +++ b/rclcpp/test/rclcpp/test_generic_pubsub.cpp @@ -15,6 +15,7 @@ #include +#include #include #include #include diff --git a/rclcpp/test/rclcpp/test_parameter_service.cpp b/rclcpp/test/rclcpp/test_parameter_service.cpp index 7d63d0866d..6b0838b0db 100644 --- a/rclcpp/test/rclcpp/test_parameter_service.cpp +++ b/rclcpp/test/rclcpp/test_parameter_service.cpp @@ -14,6 +14,7 @@ #include +#include #include #include #include diff --git a/rclcpp/test/rclcpp/test_service.cpp b/rclcpp/test/rclcpp/test_service.cpp index 1dbb8ca9e4..4f0437620f 100644 --- a/rclcpp/test/rclcpp/test_service.cpp +++ b/rclcpp/test/rclcpp/test_service.cpp @@ -14,6 +14,7 @@ #include +#include #include #include #include diff --git a/rclcpp/test/rclcpp/test_wait_for_message.cpp b/rclcpp/test/rclcpp/test_wait_for_message.cpp index dadc06f2da..60dd6e4c05 100644 --- a/rclcpp/test/rclcpp/test_wait_for_message.cpp +++ b/rclcpp/test/rclcpp/test_wait_for_message.cpp @@ -14,6 +14,7 @@ #include +#include #include #include #include diff --git a/rclcpp_action/test/test_client.cpp b/rclcpp_action/test/test_client.cpp index b94a82d500..3c2b3e3a4f 100644 --- a/rclcpp_action/test/test_client.cpp +++ b/rclcpp_action/test/test_client.cpp @@ -12,13 +12,13 @@ // See the License for the specific language governing permissions and // limitations under the License. +#include #include #include #include #include #include #include -#include #include "gtest/gtest.h" diff --git a/rclcpp_components/test/test_component_manager_api.cpp b/rclcpp_components/test/test_component_manager_api.cpp index dfb9db76a2..5282e1fb8c 100644 --- a/rclcpp_components/test/test_component_manager_api.cpp +++ b/rclcpp_components/test/test_component_manager_api.cpp @@ -14,6 +14,7 @@ #include +#include #include #include diff --git a/rclcpp_lifecycle/test/benchmark/benchmark_lifecycle_client.cpp b/rclcpp_lifecycle/test/benchmark/benchmark_lifecycle_client.cpp index c8d166ef66..f60f0a131a 100644 --- a/rclcpp_lifecycle/test/benchmark/benchmark_lifecycle_client.cpp +++ b/rclcpp_lifecycle/test/benchmark/benchmark_lifecycle_client.cpp @@ -14,6 +14,7 @@ #include +#include #include #include #include diff --git a/rclcpp_lifecycle/test/test_lifecycle_node.cpp b/rclcpp_lifecycle/test/test_lifecycle_node.cpp index fc3ac74baf..1aa1f17c3c 100644 --- a/rclcpp_lifecycle/test/test_lifecycle_node.cpp +++ b/rclcpp_lifecycle/test/test_lifecycle_node.cpp @@ -14,6 +14,7 @@ #include +#include #include #include #include From c4e82ddabb7b753ff33b7c79ae5c298173dda0c7 Mon Sep 17 00:00:00 2001 From: "mergify[bot]" <37929162+mergify[bot]@users.noreply.github.com> Date: Fri, 30 May 2025 10:43:20 +0200 Subject: [PATCH 62/98] Fix for memory leaks in rclcpp::SerializedMessage (#2861) (#2865) (cherry picked from commit 8d44b95d8bbcd95424267e97954297656e9047a8) Signed-off-by: Michael Orlov Signed-off-by: Michael Orlov Co-authored-by: Michael Orlov Co-authored-by: kylemarcey --- rclcpp/src/rclcpp/serialized_message.cpp | 27 ++++++++++++++++++++---- 1 file changed, 23 insertions(+), 4 deletions(-) diff --git a/rclcpp/src/rclcpp/serialized_message.cpp b/rclcpp/src/rclcpp/serialized_message.cpp index 2caaf90edb..2e78f5c767 100644 --- a/rclcpp/src/rclcpp/serialized_message.cpp +++ b/rclcpp/src/rclcpp/serialized_message.cpp @@ -26,8 +26,13 @@ namespace rclcpp inline void copy_rcl_message(const rcl_serialized_message_t & from, rcl_serialized_message_t & to) { - const auto ret = rmw_serialized_message_init( - &to, from.buffer_capacity, &from.allocator); + auto ret = RCL_RET_ERROR; + if (nullptr == to.buffer) { + ret = rmw_serialized_message_init(&to, from.buffer_capacity, &from.allocator); + } else { + ret = rmw_serialized_message_resize(&to, from.buffer_capacity); + } + if (RCL_RET_OK != ret) { rclcpp::exceptions::throw_from_rcl_error(ret); } @@ -78,7 +83,6 @@ SerializedMessage::SerializedMessage(rcl_serialized_message_t && other) SerializedMessage & SerializedMessage::operator=(const SerializedMessage & other) { if (this != &other) { - serialized_message_ = rmw_get_zero_initialized_serialized_message(); copy_rcl_message(other.serialized_message_, serialized_message_); } @@ -88,7 +92,6 @@ SerializedMessage & SerializedMessage::operator=(const SerializedMessage & other SerializedMessage & SerializedMessage::operator=(const rcl_serialized_message_t & other) { if (&serialized_message_ != &other) { - serialized_message_ = rmw_get_zero_initialized_serialized_message(); copy_rcl_message(other, serialized_message_); } @@ -98,6 +101,14 @@ SerializedMessage & SerializedMessage::operator=(const rcl_serialized_message_t SerializedMessage & SerializedMessage::operator=(SerializedMessage && other) { if (this != &other) { + if (nullptr != serialized_message_.buffer) { + const auto fini_ret = rmw_serialized_message_fini(&serialized_message_); + if (RCL_RET_OK != fini_ret) { + RCLCPP_ERROR( + get_logger("rclcpp"), + "Failed to destroy serialized message: %s", rcl_get_error_string().str); + } + } serialized_message_ = std::exchange(other.serialized_message_, rmw_get_zero_initialized_serialized_message()); } @@ -108,6 +119,14 @@ SerializedMessage & SerializedMessage::operator=(SerializedMessage && other) SerializedMessage & SerializedMessage::operator=(rcl_serialized_message_t && other) { if (&serialized_message_ != &other) { + if (nullptr != serialized_message_.buffer) { + const auto fini_ret = rmw_serialized_message_fini(&serialized_message_); + if (RCL_RET_OK != fini_ret) { + RCLCPP_ERROR( + get_logger("rclcpp"), + "Failed to destroy serialized message: %s", rcl_get_error_string().str); + } + } serialized_message_ = std::exchange(other, rmw_get_zero_initialized_serialized_message()); } From a0e2240ca3a25c560f24b792cc22846528b9f726 Mon Sep 17 00:00:00 2001 From: keeponoiro Date: Sat, 31 May 2025 05:01:54 +0900 Subject: [PATCH 63/98] Replace std::default_random_engine with std::mt19937 (humble) (#2847) Signed-off-by: keeponoiro --- rclcpp_action/src/client.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/rclcpp_action/src/client.cpp b/rclcpp_action/src/client.cpp index a3be59b2ee..9c644f85bb 100644 --- a/rclcpp_action/src/client.cpp +++ b/rclcpp_action/src/client.cpp @@ -183,7 +183,7 @@ class ClientBaseImpl std::mutex cancel_requests_mutex; std::independent_bits_engine< - std::default_random_engine, 8, unsigned int> random_bytes_generator; + std::mt19937, 8, unsigned int> random_bytes_generator; }; ClientBase::ClientBase( From 62123557756251d11b515f2749a926770d3606f9 Mon Sep 17 00:00:00 2001 From: Alejandro Hernandez Cordero Date: Mon, 23 Jun 2025 16:57:29 +0200 Subject: [PATCH 64/98] Changelog Signed-off-by: Alejandro Hernandez Cordero --- rclcpp/CHANGELOG.rst | 9 +++++++++ rclcpp_action/CHANGELOG.rst | 7 +++++++ rclcpp_components/CHANGELOG.rst | 5 +++++ rclcpp_lifecycle/CHANGELOG.rst | 6 ++++++ 4 files changed, 27 insertions(+) diff --git a/rclcpp/CHANGELOG.rst b/rclcpp/CHANGELOG.rst index e7c6eb25db..6942f01c83 100644 --- a/rclcpp/CHANGELOG.rst +++ b/rclcpp/CHANGELOG.rst @@ -2,6 +2,15 @@ Changelog for package rclcpp ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +16.0.13 (2025-06-23) +-------------------- +* Fix for memory leaks in rclcpp::SerializedMessage (`#2861 `_) (`#2865 `_) +* Added missing chrono includes (backport `#2854 `_) (`#2857 `_) +* QoSInitialization::from_rmw does not validate invalid history policy … (backport `#2841 `_) (`#2844 `_) +* throws std::invalid_argument if ParameterEvent is NULL. (`#2814 `_) (`#2824 `_) +* remove redundant typesupport check in serialization module (`#2808 `_) (`#2816 `_) +* Contributors: mergify[bot] + 16.0.12 (2025-03-25) -------------------- * doc: Added warning to not instantiate Clock directly with RCL_ROS_TIME (`#2768 `_) (`#2770 `_) diff --git a/rclcpp_action/CHANGELOG.rst b/rclcpp_action/CHANGELOG.rst index 4c775268ce..5c25074ebc 100644 --- a/rclcpp_action/CHANGELOG.rst +++ b/rclcpp_action/CHANGELOG.rst @@ -3,6 +3,13 @@ Changelog for package rclcpp_action ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +16.0.13 (2025-06-23) +-------------------- +* Replace std::default_random_engine with std::mt19937 (humble) (`#2847 `_) +* Added missing chrono includes (backport `#2854 `_) (`#2857 `_) +* Harden rclcpp_action::convert(). (backport `#2786 `_) (`#2788 `_) +* Contributors: keeponoiro, mergify[bot] + 16.0.12 (2025-03-25) -------------------- diff --git a/rclcpp_components/CHANGELOG.rst b/rclcpp_components/CHANGELOG.rst index 91f16aa5b7..1081f16def 100644 --- a/rclcpp_components/CHANGELOG.rst +++ b/rclcpp_components/CHANGELOG.rst @@ -2,6 +2,11 @@ Changelog for package rclcpp_components ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +16.0.13 (2025-06-23) +-------------------- +* Added missing chrono includes (backport `#2854 `_) (`#2857 `_) +* Contributors: mergify[bot] + 16.0.12 (2025-03-25) -------------------- * Redundant .c_str() usage in rclcpp_components triggers ament_clang_tidy warning (`#2718 `_) diff --git a/rclcpp_lifecycle/CHANGELOG.rst b/rclcpp_lifecycle/CHANGELOG.rst index 058d717a05..17eb9e3e2e 100644 --- a/rclcpp_lifecycle/CHANGELOG.rst +++ b/rclcpp_lifecycle/CHANGELOG.rst @@ -3,6 +3,12 @@ Changelog for package rclcpp_lifecycle ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +16.0.13 (2025-06-23) +-------------------- +* Added missing chrono includes (backport `#2854 `_) (`#2857 `_) +* should pull valid transition before trying to change the state. (backport `#2774 `_) (`#2785 `_) +* Contributors: mergify[bot] + 16.0.12 (2025-03-25) -------------------- From 5237763f7da76eb0c6a12e9991d4c1af9ab720d5 Mon Sep 17 00:00:00 2001 From: Alejandro Hernandez Cordero Date: Mon, 23 Jun 2025 16:57:35 +0200 Subject: [PATCH 65/98] 16.0.13 --- rclcpp/package.xml | 2 +- rclcpp_action/package.xml | 2 +- rclcpp_components/package.xml | 2 +- rclcpp_lifecycle/package.xml | 2 +- 4 files changed, 4 insertions(+), 4 deletions(-) diff --git a/rclcpp/package.xml b/rclcpp/package.xml index b144d4ccf5..3c42ee9049 100644 --- a/rclcpp/package.xml +++ b/rclcpp/package.xml @@ -2,7 +2,7 @@ rclcpp - 16.0.12 + 16.0.13 The ROS client library in C++. Ivan Paunovic Jacob Perron diff --git a/rclcpp_action/package.xml b/rclcpp_action/package.xml index 83472a6500..6a065b7ab6 100644 --- a/rclcpp_action/package.xml +++ b/rclcpp_action/package.xml @@ -2,7 +2,7 @@ rclcpp_action - 16.0.12 + 16.0.13 Adds action APIs for C++. Ivan Paunovic Jacob Perron diff --git a/rclcpp_components/package.xml b/rclcpp_components/package.xml index 160a33d47f..6b0f5e4417 100644 --- a/rclcpp_components/package.xml +++ b/rclcpp_components/package.xml @@ -2,7 +2,7 @@ rclcpp_components - 16.0.12 + 16.0.13 Package containing tools for dynamically loadable components Ivan Paunovic Jacob Perron diff --git a/rclcpp_lifecycle/package.xml b/rclcpp_lifecycle/package.xml index c4859d02f2..8657f2f75d 100644 --- a/rclcpp_lifecycle/package.xml +++ b/rclcpp_lifecycle/package.xml @@ -2,7 +2,7 @@ rclcpp_lifecycle - 16.0.12 + 16.0.13 Package containing a prototype for lifecycle implementation Ivan Paunovic Jacob Perron From 4a5bbfa42f6008dc44cdbcd5095b9609c976061c Mon Sep 17 00:00:00 2001 From: "mergify[bot]" <37929162+mergify[bot]@users.noreply.github.com> Date: Thu, 10 Jul 2025 13:27:40 -0700 Subject: [PATCH 66/98] fix test_publisher_with_system_default_qos. (backport #2881) (#2882) * fix test_publisher_with_system_default_qos. (#2881) Signed-off-by: Tomoya Fujita (cherry picked from commit e6577c6792f76a74e303cc0c061e89abeb8cb1a6) * intraprocess communication allowed only with volatile durability. Signed-off-by: Tomoya Fujita --------- Signed-off-by: Tomoya Fujita Co-authored-by: Tomoya Fujita --- rclcpp/test/rclcpp/test_publisher.cpp | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/rclcpp/test/rclcpp/test_publisher.cpp b/rclcpp/test/rclcpp/test_publisher.cpp index 31a518f8aa..4a1b77fc9d 100644 --- a/rclcpp/test/rclcpp/test_publisher.cpp +++ b/rclcpp/test/rclcpp/test_publisher.cpp @@ -184,10 +184,12 @@ TEST_F(TestPublisher, test_publisher_with_system_default_qos) { // explicitly enable intra-process comm with publisher option auto options = rclcpp::PublisherOptions(); options.use_intra_process_comm = rclcpp::IntraProcessSetting::Enable; + // intraprocess communication allowed only with volatile durability + rclcpp::QoS qos = rclcpp::QoS(10).durability_volatile(); using test_msgs::msg::Empty; ASSERT_NO_THROW( { - auto publisher = node->create_publisher("topic", rclcpp::SystemDefaultsQoS()); + auto publisher = node->create_publisher("topic", qos, options); }); } From 5a0c24c0ddef92c23bee28b286be4ee6b76cf54d Mon Sep 17 00:00:00 2001 From: Christophe Bedard Date: Wed, 16 Jul 2025 11:33:05 -0700 Subject: [PATCH 67/98] Update changelogs Signed-off-by: Christophe Bedard --- rclcpp/CHANGELOG.rst | 5 +++++ rclcpp_action/CHANGELOG.rst | 3 +++ rclcpp_components/CHANGELOG.rst | 3 +++ rclcpp_lifecycle/CHANGELOG.rst | 3 +++ 4 files changed, 14 insertions(+) diff --git a/rclcpp/CHANGELOG.rst b/rclcpp/CHANGELOG.rst index 6942f01c83..0baf026e6d 100644 --- a/rclcpp/CHANGELOG.rst +++ b/rclcpp/CHANGELOG.rst @@ -2,6 +2,11 @@ Changelog for package rclcpp ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +Forthcoming +----------- +* fix test_publisher_with_system_default_qos. (backport `#2881 `_) (`#2882 `_) +* Contributors: mergify[bot] + 16.0.13 (2025-06-23) -------------------- * Fix for memory leaks in rclcpp::SerializedMessage (`#2861 `_) (`#2865 `_) diff --git a/rclcpp_action/CHANGELOG.rst b/rclcpp_action/CHANGELOG.rst index 5c25074ebc..57510c4aea 100644 --- a/rclcpp_action/CHANGELOG.rst +++ b/rclcpp_action/CHANGELOG.rst @@ -3,6 +3,9 @@ Changelog for package rclcpp_action ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +Forthcoming +----------- + 16.0.13 (2025-06-23) -------------------- * Replace std::default_random_engine with std::mt19937 (humble) (`#2847 `_) diff --git a/rclcpp_components/CHANGELOG.rst b/rclcpp_components/CHANGELOG.rst index 1081f16def..9ae98cafb6 100644 --- a/rclcpp_components/CHANGELOG.rst +++ b/rclcpp_components/CHANGELOG.rst @@ -2,6 +2,9 @@ Changelog for package rclcpp_components ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +Forthcoming +----------- + 16.0.13 (2025-06-23) -------------------- * Added missing chrono includes (backport `#2854 `_) (`#2857 `_) diff --git a/rclcpp_lifecycle/CHANGELOG.rst b/rclcpp_lifecycle/CHANGELOG.rst index 17eb9e3e2e..92619953b0 100644 --- a/rclcpp_lifecycle/CHANGELOG.rst +++ b/rclcpp_lifecycle/CHANGELOG.rst @@ -3,6 +3,9 @@ Changelog for package rclcpp_lifecycle ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +Forthcoming +----------- + 16.0.13 (2025-06-23) -------------------- * Added missing chrono includes (backport `#2854 `_) (`#2857 `_) From 75b8684b86ac7a9f5276dbe02cbbf914b631a645 Mon Sep 17 00:00:00 2001 From: Christophe Bedard Date: Wed, 16 Jul 2025 11:33:20 -0700 Subject: [PATCH 68/98] 16.0.14 Signed-off-by: Christophe Bedard --- rclcpp/CHANGELOG.rst | 4 ++-- rclcpp/package.xml | 2 +- rclcpp_action/CHANGELOG.rst | 4 ++-- rclcpp_action/package.xml | 2 +- rclcpp_components/CHANGELOG.rst | 4 ++-- rclcpp_components/package.xml | 2 +- rclcpp_lifecycle/CHANGELOG.rst | 4 ++-- rclcpp_lifecycle/package.xml | 2 +- 8 files changed, 12 insertions(+), 12 deletions(-) diff --git a/rclcpp/CHANGELOG.rst b/rclcpp/CHANGELOG.rst index 0baf026e6d..6a6dfec7f7 100644 --- a/rclcpp/CHANGELOG.rst +++ b/rclcpp/CHANGELOG.rst @@ -2,8 +2,8 @@ Changelog for package rclcpp ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -Forthcoming ------------ +16.0.14 (2025-07-16) +-------------------- * fix test_publisher_with_system_default_qos. (backport `#2881 `_) (`#2882 `_) * Contributors: mergify[bot] diff --git a/rclcpp/package.xml b/rclcpp/package.xml index 3c42ee9049..3461e5365f 100644 --- a/rclcpp/package.xml +++ b/rclcpp/package.xml @@ -2,7 +2,7 @@ rclcpp - 16.0.13 + 16.0.14 The ROS client library in C++. Ivan Paunovic Jacob Perron diff --git a/rclcpp_action/CHANGELOG.rst b/rclcpp_action/CHANGELOG.rst index 57510c4aea..7ae4982857 100644 --- a/rclcpp_action/CHANGELOG.rst +++ b/rclcpp_action/CHANGELOG.rst @@ -3,8 +3,8 @@ Changelog for package rclcpp_action ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -Forthcoming ------------ +16.0.14 (2025-07-16) +-------------------- 16.0.13 (2025-06-23) -------------------- diff --git a/rclcpp_action/package.xml b/rclcpp_action/package.xml index 6a065b7ab6..1635bfd7b5 100644 --- a/rclcpp_action/package.xml +++ b/rclcpp_action/package.xml @@ -2,7 +2,7 @@ rclcpp_action - 16.0.13 + 16.0.14 Adds action APIs for C++. Ivan Paunovic Jacob Perron diff --git a/rclcpp_components/CHANGELOG.rst b/rclcpp_components/CHANGELOG.rst index 9ae98cafb6..0c06ba7d50 100644 --- a/rclcpp_components/CHANGELOG.rst +++ b/rclcpp_components/CHANGELOG.rst @@ -2,8 +2,8 @@ Changelog for package rclcpp_components ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -Forthcoming ------------ +16.0.14 (2025-07-16) +-------------------- 16.0.13 (2025-06-23) -------------------- diff --git a/rclcpp_components/package.xml b/rclcpp_components/package.xml index 6b0f5e4417..99012acf17 100644 --- a/rclcpp_components/package.xml +++ b/rclcpp_components/package.xml @@ -2,7 +2,7 @@ rclcpp_components - 16.0.13 + 16.0.14 Package containing tools for dynamically loadable components Ivan Paunovic Jacob Perron diff --git a/rclcpp_lifecycle/CHANGELOG.rst b/rclcpp_lifecycle/CHANGELOG.rst index 92619953b0..993c68704b 100644 --- a/rclcpp_lifecycle/CHANGELOG.rst +++ b/rclcpp_lifecycle/CHANGELOG.rst @@ -3,8 +3,8 @@ Changelog for package rclcpp_lifecycle ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -Forthcoming ------------ +16.0.14 (2025-07-16) +-------------------- 16.0.13 (2025-06-23) -------------------- diff --git a/rclcpp_lifecycle/package.xml b/rclcpp_lifecycle/package.xml index 8657f2f75d..cbe441c26a 100644 --- a/rclcpp_lifecycle/package.xml +++ b/rclcpp_lifecycle/package.xml @@ -2,7 +2,7 @@ rclcpp_lifecycle - 16.0.13 + 16.0.14 Package containing a prototype for lifecycle implementation Ivan Paunovic Jacob Perron From 8e19cbaa1458037f43b52376ecab1f2f1ab46f20 Mon Sep 17 00:00:00 2001 From: "mergify[bot]" <37929162+mergify[bot]@users.noreply.github.com> Date: Wed, 16 Jul 2025 21:53:27 -0700 Subject: [PATCH 69/98] Add qos parameter for wait_for_message function (#2903) (#2907) (cherry picked from commit 2fcef70ea78c2c3a45391e59aebb265c05113050) Signed-off-by: Sriharsha Ghanta Signed-off-by: Alejandro Hernandez Cordero Co-authored-by: Sriharsha Ghanta Co-authored-by: Alejandro Hernandez Cordero --- rclcpp/include/rclcpp/wait_for_message.hpp | 10 +++++--- rclcpp/test/rclcpp/test_wait_for_message.cpp | 26 ++++++++++++++++++++ 2 files changed, 33 insertions(+), 3 deletions(-) diff --git a/rclcpp/include/rclcpp/wait_for_message.hpp b/rclcpp/include/rclcpp/wait_for_message.hpp index 25c45ad782..fab2e6ccfc 100644 --- a/rclcpp/include/rclcpp/wait_for_message.hpp +++ b/rclcpp/include/rclcpp/wait_for_message.hpp @@ -15,6 +15,7 @@ #ifndef RCLCPP__WAIT_FOR_MESSAGE_HPP_ #define RCLCPP__WAIT_FOR_MESSAGE_HPP_ +#include #include #include @@ -23,6 +24,7 @@ #include "rclcpp/node.hpp" #include "rclcpp/visibility_control.hpp" #include "rclcpp/wait_set.hpp" +#include "rclcpp/qos.hpp" namespace rclcpp { @@ -79,10 +81,11 @@ bool wait_for_message( /** * Wait for the next incoming message to arrive on a specified topic before the specified timeout. * - * \param[out] out is the message to be filled when a new message is arriving. + * \param[out] out is the message to be filled when a new message is arriving * \param[in] node the node pointer to initialize the subscription on. * \param[in] topic the topic to wait for messages. * \param[in] time_to_wait parameter specifying the timeout before returning. + * \param[in] qos parameter specifying QoS settings for the subscription. * \return true if a message was successfully received, false if message could not * be obtained or shutdown was triggered asynchronously on the context. */ @@ -91,9 +94,10 @@ bool wait_for_message( MsgT & out, rclcpp::Node::SharedPtr node, const std::string & topic, - std::chrono::duration time_to_wait = std::chrono::duration(-1)) + std::chrono::duration time_to_wait = std::chrono::duration(-1), + const rclcpp::QoS & qos = rclcpp::SystemDefaultsQoS()) { - auto sub = node->create_subscription(topic, 1, [](const std::shared_ptr) {}); + auto sub = node->create_subscription(topic, qos, [](const std::shared_ptr) {}); return wait_for_message( out, sub, node->get_node_options().context(), time_to_wait); } diff --git a/rclcpp/test/rclcpp/test_wait_for_message.cpp b/rclcpp/test/rclcpp/test_wait_for_message.cpp index 60dd6e4c05..9f49fb141c 100644 --- a/rclcpp/test/rclcpp/test_wait_for_message.cpp +++ b/rclcpp/test/rclcpp/test_wait_for_message.cpp @@ -108,3 +108,29 @@ TEST(TestUtilities, wait_for_message_twice_one_sub) { rclcpp::shutdown(); } + +TEST(TestUtilities, wait_for_last_message) { + rclcpp::init(0, nullptr); + + auto node = std::make_shared("wait_for_last_message_node"); + auto qos = rclcpp::QoS(1).reliable().transient_local(); + + using MsgT = test_msgs::msg::Strings; + auto pub = node->create_publisher("wait_for_last_message_topic", qos); + pub->publish(*get_messages_strings()[0]); + + MsgT out; + auto received = false; + auto wait = std::async( + [&]() { + auto ret = rclcpp::wait_for_message(out, node, "wait_for_last_message_topic", 5s, qos); + EXPECT_TRUE(ret); + received = true; + }); + + ASSERT_NO_THROW(wait.get()); + ASSERT_TRUE(received); + EXPECT_EQ(out, *get_messages_strings()[0]); + + rclcpp::shutdown(); +} From 76cdd45da31c7da87a9c2cbefff8e7437b47dae9 Mon Sep 17 00:00:00 2001 From: "mergify[bot]" <37929162+mergify[bot]@users.noreply.github.com> Date: Thu, 7 Aug 2025 11:35:53 -0500 Subject: [PATCH 70/98] Removed warning test_qos (#2859) (#2925) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit (cherry picked from commit df3a303a1788a010358572b93b30df9166923390) Signed-off-by: Alejandro Hernandez Cordero Signed-off-by: Crola1702 Co-authored-by: Alejandro Hernández Cordero --- rclcpp/test/rclcpp/test_qos.cpp | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/rclcpp/test/rclcpp/test_qos.cpp b/rclcpp/test/rclcpp/test_qos.cpp index 017f189781..d293db0f13 100644 --- a/rclcpp/test/rclcpp/test_qos.cpp +++ b/rclcpp/test/rclcpp/test_qos.cpp @@ -255,7 +255,8 @@ TEST(TestQoS, from_rmw_validity) { rmw_qos_profile_t invalid_qos; memset(&invalid_qos, 0, sizeof(invalid_qos)); - reinterpret_cast(invalid_qos.history) = 999; + unsigned int n = 999; + memcpy(&invalid_qos.history, &n, sizeof(n)); EXPECT_THROW( { From 0036533e9482860df29ae04b59f099778b95be57 Mon Sep 17 00:00:00 2001 From: "mergify[bot]" <37929162+mergify[bot]@users.noreply.github.com> Date: Mon, 18 Aug 2025 20:04:17 +0200 Subject: [PATCH 71/98] Fix: improve exception context for parameter_value_from (backport #2917) (#2921) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Michiel Leegwater Signed-off-by: Alejandro Hernández Cordero Signed-off-by: Alejandro Hernandez Cordero Co-authored-by: Michiel Leegwater Co-authored-by: Tomoya Fujita Co-authored-by: Alejandro Hernández Cordero --- rclcpp/src/rclcpp/parameter_map.cpp | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/rclcpp/src/rclcpp/parameter_map.cpp b/rclcpp/src/rclcpp/parameter_map.cpp index 6365f55478..61c1e08f4e 100644 --- a/rclcpp/src/rclcpp/parameter_map.cpp +++ b/rclcpp/src/rclcpp/parameter_map.cpp @@ -81,7 +81,15 @@ rclcpp::parameter_map_from(const rcl_params_t * const c_params, const char * nod throw InvalidParametersException(message); } const rcl_variant_t * const c_param_value = &(c_params_node->parameter_values[p]); - params_node.emplace_back(c_param_name, parameter_value_from(c_param_value)); + ParameterValue value; + try { + value = parameter_value_from(c_param_value); + } catch (const InvalidParameterValueException & e) { + throw InvalidParameterValueException( + std::string("parameter_value_from failed for parameter '") + + c_param_name + "': " + e.what()); + } + params_node.emplace_back(c_param_name, value); } } From 443b69b6e10fba68c1ec1afa704c3bb0a64d8367 Mon Sep 17 00:00:00 2001 From: "mergify[bot]" <37929162+mergify[bot]@users.noreply.github.com> Date: Tue, 19 Aug 2025 09:33:14 +0200 Subject: [PATCH 72/98] Allow for implicitly convertable loggers as well (#2922) (#2936) (#2938) (cherry picked from commit 8a4cb48b2c5e56f5615befbc6b6ebf91d9bf296e) Signed-off-by: Tim Clephas Co-authored-by: Tim Clephas --- rclcpp/resource/logging.hpp.em | 3 +-- rclcpp/test/rclcpp/test_logging.cpp | 10 ++++++++++ 2 files changed, 11 insertions(+), 2 deletions(-) diff --git a/rclcpp/resource/logging.hpp.em b/rclcpp/resource/logging.hpp.em index 693c5c4f7b..41af49aedb 100644 --- a/rclcpp/resource/logging.hpp.em +++ b/rclcpp/resource/logging.hpp.em @@ -125,8 +125,7 @@ def get_rclcpp_suffix_from_features(features): ) \ do { \ static_assert( \ - ::std::is_same>, \ - typename ::rclcpp::Logger>::value, \ + ::std::is_convertible_v, \ "First argument to logging macros must be an rclcpp::Logger"); \ @[ if 'throttle' in feature_combination]@ \ auto get_time_point = [&c=clock](rcutils_time_point_value_t * time_point) -> rcutils_ret_t { \ diff --git a/rclcpp/test/rclcpp/test_logging.cpp b/rclcpp/test/rclcpp/test_logging.cpp index 8e2214f8e6..6179faf788 100644 --- a/rclcpp/test/rclcpp/test_logging.cpp +++ b/rclcpp/test/rclcpp/test_logging.cpp @@ -253,9 +253,19 @@ bool log_function_const_ref(const rclcpp::Logger & logger) return true; } +class DerivedLogger : public rclcpp::Logger +{ +public: + explicit DerivedLogger(const rclcpp::Logger & logger) + : rclcpp::Logger(logger) {} +}; + TEST_F(TestLoggingMacros, test_log_from_node) { auto logger = rclcpp::get_logger("test_logging_logger"); EXPECT_TRUE(log_function(logger)); EXPECT_TRUE(log_function_const(logger)); EXPECT_TRUE(log_function_const_ref(logger)); + + DerivedLogger derived_logger(logger); + RCLCPP_INFO(derived_logger, "successful log from derived logger"); } From 81b628d4e6beeb1d09c43ecad4ffd7260869568f Mon Sep 17 00:00:00 2001 From: "Peter Mitrano (AR)" Date: Tue, 19 Aug 2025 10:58:16 +0200 Subject: [PATCH 73/98] Add a clearer warning message, the old one lacked information and was misleading (#2924) Signed-off-by: Peter Mitrano (AR) --- .../src/lifecycle_node_interface_impl.hpp | 14 ++++++++++---- 1 file changed, 10 insertions(+), 4 deletions(-) diff --git a/rclcpp_lifecycle/src/lifecycle_node_interface_impl.hpp b/rclcpp_lifecycle/src/lifecycle_node_interface_impl.hpp index c292622175..b7b4b5faa0 100644 --- a/rclcpp_lifecycle/src/lifecycle_node_interface_impl.hpp +++ b/rclcpp_lifecycle/src/lifecycle_node_interface_impl.hpp @@ -401,6 +401,7 @@ class LifecycleNode::LifecycleNodeInterfaceImpl constexpr bool publish_update = true; State initial_state; unsigned int current_state_id; + rcl_lifecycle_transition_t const * original_transition{nullptr}; { std::lock_guard lock(state_machine_mutex_); @@ -414,6 +415,9 @@ class LifecycleNode::LifecycleNodeInterfaceImpl // keep the initial state to pass to a transition callback initial_state = State(state_machine_.current_state); + original_transition = + rcl_lifecycle_get_transition_by_id(state_machine_.current_state, transition_id); + if ( rcl_lifecycle_trigger_transition_by_id( &state_machine_, transition_id, publish_update) != RCL_RET_OK) @@ -458,12 +462,14 @@ class LifecycleNode::LifecycleNodeInterfaceImpl current_state_id = state_machine_.current_state->id; } - // error handling ?! + // error handling // TODO(karsten1987): iterate over possible ret value if (cb_return_code == node_interfaces::LifecycleNodeInterface::CallbackReturn::ERROR) { - RCLCPP_WARN( - node_logging_interface_->get_logger(), - "Error occurred while doing error handling."); + if (original_transition) { + RCLCPP_WARN( + node_logging_interface_->get_logger(), + "Callback returned ERROR during the transition: %s", original_transition->label); + } auto error_cb_code = execute_callback(current_state_id, initial_state); auto error_cb_label = get_label_for_return_code(error_cb_code); From e41abc37f3d717e272d8f7b80ca74f9b7ed2f4bd Mon Sep 17 00:00:00 2001 From: Alejandro Hernandez Cordero Date: Thu, 11 Sep 2025 15:21:33 +0200 Subject: [PATCH 74/98] Changelog Signed-off-by: Alejandro Hernandez Cordero --- rclcpp/CHANGELOG.rst | 8 ++++++++ rclcpp_action/CHANGELOG.rst | 3 +++ rclcpp_components/CHANGELOG.rst | 3 +++ rclcpp_lifecycle/CHANGELOG.rst | 5 +++++ 4 files changed, 19 insertions(+) diff --git a/rclcpp/CHANGELOG.rst b/rclcpp/CHANGELOG.rst index 6a6dfec7f7..2f575a815d 100644 --- a/rclcpp/CHANGELOG.rst +++ b/rclcpp/CHANGELOG.rst @@ -2,6 +2,14 @@ Changelog for package rclcpp ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +16.0.15 (2025-09-11) +-------------------- +* Allow for implicitly convertable loggers as well (`#2922 `_) (`#2936 `_) (`#2938 `_) +* Fix: improve exception context for parameter_value_from (backport `#2917 `_) (`#2921 `_) +* Removed warning test_qos (`#2859 `_) (`#2925 `_) +* Add qos parameter for wait_for_message function (`#2903 `_) (`#2907 `_) +* Contributors: mergify[bot] + 16.0.14 (2025-07-16) -------------------- * fix test_publisher_with_system_default_qos. (backport `#2881 `_) (`#2882 `_) diff --git a/rclcpp_action/CHANGELOG.rst b/rclcpp_action/CHANGELOG.rst index 7ae4982857..135851dd14 100644 --- a/rclcpp_action/CHANGELOG.rst +++ b/rclcpp_action/CHANGELOG.rst @@ -3,6 +3,9 @@ Changelog for package rclcpp_action ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +16.0.15 (2025-09-11) +-------------------- + 16.0.14 (2025-07-16) -------------------- diff --git a/rclcpp_components/CHANGELOG.rst b/rclcpp_components/CHANGELOG.rst index 0c06ba7d50..aa08667880 100644 --- a/rclcpp_components/CHANGELOG.rst +++ b/rclcpp_components/CHANGELOG.rst @@ -2,6 +2,9 @@ Changelog for package rclcpp_components ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +16.0.15 (2025-09-11) +-------------------- + 16.0.14 (2025-07-16) -------------------- diff --git a/rclcpp_lifecycle/CHANGELOG.rst b/rclcpp_lifecycle/CHANGELOG.rst index 993c68704b..a9999640f3 100644 --- a/rclcpp_lifecycle/CHANGELOG.rst +++ b/rclcpp_lifecycle/CHANGELOG.rst @@ -3,6 +3,11 @@ Changelog for package rclcpp_lifecycle ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +16.0.15 (2025-09-11) +-------------------- +* Add a clearer warning message, the old one lacked information and was misleading (`#2924 `_) +* Contributors: Peter Mitrano (AR) + 16.0.14 (2025-07-16) -------------------- From 632a41e6fa0850d7c0d56e92016d47fb3bb6d7cf Mon Sep 17 00:00:00 2001 From: Alejandro Hernandez Cordero Date: Thu, 11 Sep 2025 15:21:37 +0200 Subject: [PATCH 75/98] 16.0.15 --- rclcpp/package.xml | 2 +- rclcpp_action/package.xml | 2 +- rclcpp_components/package.xml | 2 +- rclcpp_lifecycle/package.xml | 2 +- 4 files changed, 4 insertions(+), 4 deletions(-) diff --git a/rclcpp/package.xml b/rclcpp/package.xml index 3461e5365f..071a2a8b76 100644 --- a/rclcpp/package.xml +++ b/rclcpp/package.xml @@ -2,7 +2,7 @@ rclcpp - 16.0.14 + 16.0.15 The ROS client library in C++. Ivan Paunovic Jacob Perron diff --git a/rclcpp_action/package.xml b/rclcpp_action/package.xml index 1635bfd7b5..6335f6cc2c 100644 --- a/rclcpp_action/package.xml +++ b/rclcpp_action/package.xml @@ -2,7 +2,7 @@ rclcpp_action - 16.0.14 + 16.0.15 Adds action APIs for C++. Ivan Paunovic Jacob Perron diff --git a/rclcpp_components/package.xml b/rclcpp_components/package.xml index 99012acf17..660cadfb4e 100644 --- a/rclcpp_components/package.xml +++ b/rclcpp_components/package.xml @@ -2,7 +2,7 @@ rclcpp_components - 16.0.14 + 16.0.15 Package containing tools for dynamically loadable components Ivan Paunovic Jacob Perron diff --git a/rclcpp_lifecycle/package.xml b/rclcpp_lifecycle/package.xml index cbe441c26a..fbf2c1b063 100644 --- a/rclcpp_lifecycle/package.xml +++ b/rclcpp_lifecycle/package.xml @@ -2,7 +2,7 @@ rclcpp_lifecycle - 16.0.14 + 16.0.15 Package containing a prototype for lifecycle implementation Ivan Paunovic Jacob Perron From e69382c357014d0776d30a4ad8bc97784cd404b2 Mon Sep 17 00:00:00 2001 From: "mergify[bot]" <37929162+mergify[bot]@users.noreply.github.com> Date: Mon, 10 Nov 2025 08:55:37 +0100 Subject: [PATCH 76/98] Add get_parameter_or overload returning value or alternative (#2973) (#2978) (cherry picked from commit eb49444c3229f03255083d7992564b83ec2d9e2c) Signed-off-by: Zheng Qu Co-authored-by: Zheng Qu --- .../rclcpp_lifecycle/lifecycle_node.hpp | 10 ++++++++++ .../rclcpp_lifecycle/lifecycle_node_impl.hpp | 11 ++++++++++ rclcpp_lifecycle/test/test_lifecycle_node.cpp | 20 +++++++++++++++++++ 3 files changed, 41 insertions(+) diff --git a/rclcpp_lifecycle/include/rclcpp_lifecycle/lifecycle_node.hpp b/rclcpp_lifecycle/include/rclcpp_lifecycle/lifecycle_node.hpp index 6048d0d691..99278d60a2 100644 --- a/rclcpp_lifecycle/include/rclcpp_lifecycle/lifecycle_node.hpp +++ b/rclcpp_lifecycle/include/rclcpp_lifecycle/lifecycle_node.hpp @@ -458,6 +458,16 @@ class LifecycleNode : public node_interfaces::LifecycleNodeInterface, ParameterT & value, const ParameterT & alternative_value) const; + /// Return the parameter value, or the "alternative_value" if not set. + /** + * \sa rclcpp::Node::get_parameter_or + */ + template + ParameterT + get_parameter_or( + const std::string & name, + const ParameterT & alternative_value) const; + /// Return the parameters by the given parameter names. /** * \sa rclcpp::Node::get_parameters diff --git a/rclcpp_lifecycle/include/rclcpp_lifecycle/lifecycle_node_impl.hpp b/rclcpp_lifecycle/include/rclcpp_lifecycle/lifecycle_node_impl.hpp index 22fd7f9c08..0f2ee55177 100644 --- a/rclcpp_lifecycle/include/rclcpp_lifecycle/lifecycle_node_impl.hpp +++ b/rclcpp_lifecycle/include/rclcpp_lifecycle/lifecycle_node_impl.hpp @@ -294,5 +294,16 @@ LifecycleNode::get_parameter_or( return got_parameter; } +template +ParameterT +LifecycleNode::get_parameter_or( + const std::string & name, + const ParameterT & alternative_value) const +{ + ParameterT parameter; + get_parameter_or(name, parameter, alternative_value); + return parameter; +} + } // namespace rclcpp_lifecycle #endif // RCLCPP_LIFECYCLE__LIFECYCLE_NODE_IMPL_HPP_ diff --git a/rclcpp_lifecycle/test/test_lifecycle_node.cpp b/rclcpp_lifecycle/test/test_lifecycle_node.cpp index 1aa1f17c3c..a1235ad97e 100644 --- a/rclcpp_lifecycle/test/test_lifecycle_node.cpp +++ b/rclcpp_lifecycle/test/test_lifecycle_node.cpp @@ -771,6 +771,26 @@ TEST_F(TestDefaultStateMachine, check_parameters) { EXPECT_TRUE(parameter.as_bool()); } +TEST_F(TestDefaultStateMachine, test_get_parameter_or) { + auto test_node = std::make_shared("testnode"); + + const std::string param_name = "test_param"; + int param_int = -999; + + // Parameter does not exist, should return "or" value + EXPECT_FALSE(test_node->get_parameter_or(param_name, param_int, 123)); + EXPECT_EQ(param_int, 123); + EXPECT_EQ(test_node->get_parameter_or(param_name, 456), 456); + + // Declare param_int + test_node->declare_parameter(param_name, rclcpp::ParameterValue(789)); + + // Parameter exists, should return existing value + EXPECT_TRUE(test_node->get_parameter_or(param_name, param_int, 123)); + EXPECT_EQ(param_int, 789); + EXPECT_EQ(test_node->get_parameter_or(param_name, 456), 789); +} + TEST_F(TestDefaultStateMachine, test_getters) { auto test_node = std::make_shared("testnode"); auto options = test_node->get_node_options(); From a9d1abe402a168253dc0812b4e675441a1035275 Mon Sep 17 00:00:00 2001 From: "mergify[bot]" <37929162+mergify[bot]@users.noreply.github.com> Date: Mon, 17 Nov 2025 10:11:16 +0100 Subject: [PATCH 77/98] Fix REP url locations (backport #2987) (#2991) * Fix REP url locations (#2987) This popped up in my global replace Signed-off-by: Tim Clephas (cherry picked from commit ad019b9827d6c696fd9569ec33c88e2a99c9011f) Signed-off-by: Alejandro Hernandez Cordero Co-authored-by: Tim Clephas Co-authored-by: Alejandro Hernandez Cordero --- rclcpp/QUALITY_DECLARATION.md | 10 +++++----- rclcpp_action/QUALITY_DECLARATION.md | 10 +++++----- rclcpp_components/QUALITY_DECLARATION.md | 10 +++++----- rclcpp_lifecycle/QUALITY_DECLARATION.md | 10 +++++----- 4 files changed, 20 insertions(+), 20 deletions(-) diff --git a/rclcpp/QUALITY_DECLARATION.md b/rclcpp/QUALITY_DECLARATION.md index e0913167c2..b377134b0a 100644 --- a/rclcpp/QUALITY_DECLARATION.md +++ b/rclcpp/QUALITY_DECLARATION.md @@ -1,10 +1,10 @@ -This document is a declaration of software quality for the `rclcpp` package, based on the guidelines in [REP-2004](https://www.ros.org/reps/rep-2004.html). +This document is a declaration of software quality for the `rclcpp` package, based on the guidelines in [REP-2004](https://reps.openrobotics.org/rep-2004/). # rclcpp Quality Declaration The package `rclcpp` claims to be in the **Quality Level 1** category when it is used with a **Quality Level 1** middleware. -Below are the rationales, notes, and caveats for this claim, organized by each requirement listed in the [Package Quality Categories in REP-2004](https://www.ros.org/reps/rep-2004.html) of the ROS2 developer guide. +Below are the rationales, notes, and caveats for this claim, organized by each requirement listed in the [Package Quality Categories in REP-2004](https://reps.openrobotics.org/rep-2004/) of the ROS2 developer guide. ## Version Policy [1] @@ -55,7 +55,7 @@ All pull requests will be peer-reviewed, check [ROS 2 Developer Guide](https://d ### Continuous Integration [2.iv] -All pull requests must pass CI on all [tier 1 platforms](https://www.ros.org/reps/rep-2000.html#support-tiers) +All pull requests must pass CI on all [tier 1 platforms](https://reps.openrobotics.org/rep-2000/#support-tiers) Currently nightly results can be seen here: @@ -213,7 +213,7 @@ It is **Quality Level 1**, see its [Quality Declaration document](https://gitlab ## Platform Support [6] -`rclcpp` supports all of the tier 1 platforms as described in [REP-2000](https://www.ros.org/reps/rep-2000.html#support-tiers), and tests each change against all of them. +`rclcpp` supports all of the tier 1 platforms as described in [REP-2000](https://reps.openrobotics.org/rep-2000/#support-tiers), and tests each change against all of them. Currently nightly build status can be seen here: * [linux-aarch64_release](https://ci.ros2.org/view/nightly/job/nightly_linux-aarch64_release/lastBuild/rclcpp/) @@ -225,4 +225,4 @@ Currently nightly build status can be seen here: ### Vulnerability Disclosure Policy [7.i] -This package conforms to the Vulnerability Disclosure Policy in [REP-2006](https://www.ros.org/reps/rep-2006.html). +This package conforms to the Vulnerability Disclosure Policy in [REP-2006](https://reps.openrobotics.org/rep-2006/). diff --git a/rclcpp_action/QUALITY_DECLARATION.md b/rclcpp_action/QUALITY_DECLARATION.md index 7512ad3312..2e5f342d61 100644 --- a/rclcpp_action/QUALITY_DECLARATION.md +++ b/rclcpp_action/QUALITY_DECLARATION.md @@ -1,10 +1,10 @@ -This document is a declaration of software quality for the `rclcpp_action` package, based on the guidelines in [REP-2004](https://www.ros.org/reps/rep-2004.html). +This document is a declaration of software quality for the `rclcpp_action` package, based on the guidelines in [REP-2004](https://reps.openrobotics.org/rep-2004/). # rclcpp_action Quality Declaration The package `rclcpp_action` claims to be in the **Quality Level 1** category when it is used with a **Quality Level 1** middleware. -Below are the rationales, notes, and caveats for this claim, organized by each requirement listed in the [Package Quality Categories in REP-2004](https://www.ros.org/reps/rep-2004.html) of the ROS2 developer guide. +Below are the rationales, notes, and caveats for this claim, organized by each requirement listed in the [Package Quality Categories in REP-2004](https://reps.openrobotics.org/rep-2004/) of the ROS2 developer guide. ## Version Policy [1] @@ -53,7 +53,7 @@ All pull requests will be peer-reviewed, check [ROS 2 Developer Guide](https://d ### Continuous Integration [2.iv] -All pull requests must pass CI on all [tier 1 platforms](https://www.ros.org/reps/rep-2000.html#support-tiers) +All pull requests must pass CI on all [tier 1 platforms](https://reps.openrobotics.org/rep-2000/#support-tiers) Currently nightly results can be seen here: @@ -179,7 +179,7 @@ It is **Quality Level 1**, see its [Quality Declaration document](https://github ## Platform Support [6] -`rclcpp_action` supports all of the tier 1 platforms as described in [REP-2000](https://www.ros.org/reps/rep-2000.html#support-tiers), and tests each change against all of them. +`rclcpp_action` supports all of the tier 1 platforms as described in [REP-2000](https://reps.openrobotics.org/rep-2000/#support-tiers), and tests each change against all of them. Currently nightly build status can be seen here: * [linux-aarch64_release](https://ci.ros2.org/view/nightly/job/nightly_linux-aarch64_release/lastBuild/rclcpp_action/) @@ -191,4 +191,4 @@ Currently nightly build status can be seen here: ### Vulnerability Disclosure Policy [7.i] -This package conforms to the Vulnerability Disclosure Policy in [REP-2006](https://www.ros.org/reps/rep-2006.html). +This package conforms to the Vulnerability Disclosure Policy in [REP-2006](https://reps.openrobotics.org/rep-2006/). diff --git a/rclcpp_components/QUALITY_DECLARATION.md b/rclcpp_components/QUALITY_DECLARATION.md index 5b8efa934d..e98ac85be9 100644 --- a/rclcpp_components/QUALITY_DECLARATION.md +++ b/rclcpp_components/QUALITY_DECLARATION.md @@ -1,10 +1,10 @@ -This document is a declaration of software quality for the `rclcpp_components` package, based on the guidelines in [REP-2004](https://www.ros.org/reps/rep-2004.html). +This document is a declaration of software quality for the `rclcpp_components` package, based on the guidelines in [REP-2004](https://reps.openrobotics.org/rep-2004/). # rclcpp_components Quality Declaration The package `rclcpp_components` claims to be in the **Quality Level 1** category. -Below are the rationales, notes, and caveats for this claim, organized by each requirement listed in the [Package Quality Categories in REP-2004](https://www.ros.org/reps/rep-2004.html) of the ROS2 developer guide. +Below are the rationales, notes, and caveats for this claim, organized by each requirement listed in the [Package Quality Categories in REP-2004](https://reps.openrobotics.org/rep-2004/) of the ROS2 developer guide. ## Version Policy [1] @@ -53,7 +53,7 @@ All pull requests will be peer-reviewed, check [ROS 2 Developer Guide](https://d ### Continuous Integration [2.iv] -All pull requests must pass CI on all [tier 1 platforms](https://www.ros.org/reps/rep-2000.html#support-tiers) +All pull requests must pass CI on all [tier 1 platforms](https://reps.openrobotics.org/rep-2000/#support-tiers) Currently nightly results can be seen here: @@ -191,7 +191,7 @@ It is **Quality Level 1**, see its [Quality Declaration document](https://github ## Platform Support [6] -`rclcpp_components` supports all of the tier 1 platforms as described in [REP-2000](https://www.ros.org/reps/rep-2000.html#support-tiers), and tests each change against all of them. +`rclcpp_components` supports all of the tier 1 platforms as described in [REP-2000](https://reps.openrobotics.org/rep-2000/#support-tiers), and tests each change against all of them. Currently nightly build status can be seen here: * [linux-aarch64_release](https://ci.ros2.org/view/nightly/job/nightly_linux-aarch64_release/lastBuild/rclcpp_components/) @@ -203,4 +203,4 @@ Currently nightly build status can be seen here: ### Vulnerability Disclosure Policy [7.i] -This package conforms to the Vulnerability Disclosure Policy in [REP-2006](https://www.ros.org/reps/rep-2006.html). +This package conforms to the Vulnerability Disclosure Policy in [REP-2006](https://reps.openrobotics.org/rep-2006/). diff --git a/rclcpp_lifecycle/QUALITY_DECLARATION.md b/rclcpp_lifecycle/QUALITY_DECLARATION.md index 5a37ad395b..fd988a23ed 100644 --- a/rclcpp_lifecycle/QUALITY_DECLARATION.md +++ b/rclcpp_lifecycle/QUALITY_DECLARATION.md @@ -1,10 +1,10 @@ -This document is a declaration of software quality for the `rclcpp_lifecycle` package, based on the guidelines in [REP-2004](https://www.ros.org/reps/rep-2004.html). +This document is a declaration of software quality for the `rclcpp_lifecycle` package, based on the guidelines in [REP-2004](https://reps.openrobotics.org/rep-2004/). # rclcpp_lifecycle Quality Declaration The package `rclcpp_lifecycle` claims to be in the **Quality Level 1** category when used with a **Quality Level 1** middleware. -Below are the rationales, notes, and caveats for this claim, organized by each requirement listed in the [Package Quality Categories in REP-2004](https://www.ros.org/reps/rep-2004.html) of the ROS2 developer guide. +Below are the rationales, notes, and caveats for this claim, organized by each requirement listed in the [Package Quality Categories in REP-2004](https://reps.openrobotics.org/rep-2004/) of the ROS2 developer guide. ## Version Policy [1] @@ -53,7 +53,7 @@ All pull requests will be peer-reviewed, check [ROS 2 Developer Guide](https://d ### Continuous Integration [2.iv] -All pull requests must pass CI on all [tier 1 platforms](https://www.ros.org/reps/rep-2000.html#support-tiers) +All pull requests must pass CI on all [tier 1 platforms](https://reps.openrobotics.org/rep-2000/#support-tiers) Currently nightly results can be seen here: @@ -191,7 +191,7 @@ It is **Quality Level 1**, see its [Quality Declaration document](https://github ## Platform Support [6] -`rclcpp_lifecycle` supports all of the tier 1 platforms as described in [REP-2000](https://www.ros.org/reps/rep-2000.html#support-tiers), and tests each change against all of them. +`rclcpp_lifecycle` supports all of the tier 1 platforms as described in [REP-2000](https://reps.openrobotics.org/rep-2000/#support-tiers), and tests each change against all of them. Currently nightly build status can be seen here: * [linux-aarch64_release](https://ci.ros2.org/view/nightly/job/nightly_linux-aarch64_release/lastBuild/rclcpp_lifecycle/) @@ -203,4 +203,4 @@ Currently nightly build status can be seen here: ### Vulnerability Disclosure Policy [7.i] -This package conforms to the Vulnerability Disclosure Policy in [REP-2006](https://www.ros.org/reps/rep-2006.html). +This package conforms to the Vulnerability Disclosure Policy in [REP-2006](https://reps.openrobotics.org/rep-2006/). From 3c5631b4fe6c17334dd1b00a546f414a1269bbe9 Mon Sep 17 00:00:00 2001 From: Alejandro Hernandez Cordero Date: Tue, 18 Nov 2025 15:10:02 +0100 Subject: [PATCH 78/98] Changelog Signed-off-by: Alejandro Hernandez Cordero --- rclcpp/CHANGELOG.rst | 5 +++++ rclcpp_action/CHANGELOG.rst | 5 +++++ rclcpp_components/CHANGELOG.rst | 5 +++++ rclcpp_lifecycle/CHANGELOG.rst | 6 ++++++ 4 files changed, 21 insertions(+) diff --git a/rclcpp/CHANGELOG.rst b/rclcpp/CHANGELOG.rst index 2f575a815d..a58d1e657e 100644 --- a/rclcpp/CHANGELOG.rst +++ b/rclcpp/CHANGELOG.rst @@ -2,6 +2,11 @@ Changelog for package rclcpp ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +16.0.16 (2025-11-18) +-------------------- +* Fix REP url locations (backport `#2987 `_) (`#2991 `_) +* Contributors: mergify[bot] + 16.0.15 (2025-09-11) -------------------- * Allow for implicitly convertable loggers as well (`#2922 `_) (`#2936 `_) (`#2938 `_) diff --git a/rclcpp_action/CHANGELOG.rst b/rclcpp_action/CHANGELOG.rst index 135851dd14..bcc91ad6f2 100644 --- a/rclcpp_action/CHANGELOG.rst +++ b/rclcpp_action/CHANGELOG.rst @@ -3,6 +3,11 @@ Changelog for package rclcpp_action ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +16.0.16 (2025-11-18) +-------------------- +* Fix REP url locations (backport `#2987 `_) (`#2991 `_) +* Contributors: mergify[bot] + 16.0.15 (2025-09-11) -------------------- diff --git a/rclcpp_components/CHANGELOG.rst b/rclcpp_components/CHANGELOG.rst index aa08667880..10a63a5ea7 100644 --- a/rclcpp_components/CHANGELOG.rst +++ b/rclcpp_components/CHANGELOG.rst @@ -2,6 +2,11 @@ Changelog for package rclcpp_components ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +16.0.16 (2025-11-18) +-------------------- +* Fix REP url locations (backport `#2987 `_) (`#2991 `_) +* Contributors: mergify[bot] + 16.0.15 (2025-09-11) -------------------- diff --git a/rclcpp_lifecycle/CHANGELOG.rst b/rclcpp_lifecycle/CHANGELOG.rst index a9999640f3..4347fc0b48 100644 --- a/rclcpp_lifecycle/CHANGELOG.rst +++ b/rclcpp_lifecycle/CHANGELOG.rst @@ -3,6 +3,12 @@ Changelog for package rclcpp_lifecycle ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +16.0.16 (2025-11-18) +-------------------- +* Fix REP url locations (backport `#2987 `_) (`#2991 `_) +* Add get_parameter_or overload returning value or alternative (`#2973 `_) (`#2978 `_) +* Contributors: mergify[bot] + 16.0.15 (2025-09-11) -------------------- * Add a clearer warning message, the old one lacked information and was misleading (`#2924 `_) From 36d0aee198abf966556564dd9fad035547cbe9fb Mon Sep 17 00:00:00 2001 From: Alejandro Hernandez Cordero Date: Tue, 18 Nov 2025 15:10:08 +0100 Subject: [PATCH 79/98] 16.0.16 --- rclcpp/package.xml | 2 +- rclcpp_action/package.xml | 2 +- rclcpp_components/package.xml | 2 +- rclcpp_lifecycle/package.xml | 2 +- 4 files changed, 4 insertions(+), 4 deletions(-) diff --git a/rclcpp/package.xml b/rclcpp/package.xml index 071a2a8b76..7ec1f60d1b 100644 --- a/rclcpp/package.xml +++ b/rclcpp/package.xml @@ -2,7 +2,7 @@ rclcpp - 16.0.15 + 16.0.16 The ROS client library in C++. Ivan Paunovic Jacob Perron diff --git a/rclcpp_action/package.xml b/rclcpp_action/package.xml index 6335f6cc2c..7b06189ab4 100644 --- a/rclcpp_action/package.xml +++ b/rclcpp_action/package.xml @@ -2,7 +2,7 @@ rclcpp_action - 16.0.15 + 16.0.16 Adds action APIs for C++. Ivan Paunovic Jacob Perron diff --git a/rclcpp_components/package.xml b/rclcpp_components/package.xml index 660cadfb4e..17a5910bad 100644 --- a/rclcpp_components/package.xml +++ b/rclcpp_components/package.xml @@ -2,7 +2,7 @@ rclcpp_components - 16.0.15 + 16.0.16 Package containing tools for dynamically loadable components Ivan Paunovic Jacob Perron diff --git a/rclcpp_lifecycle/package.xml b/rclcpp_lifecycle/package.xml index fbf2c1b063..de64ad33c4 100644 --- a/rclcpp_lifecycle/package.xml +++ b/rclcpp_lifecycle/package.xml @@ -2,7 +2,7 @@ rclcpp_lifecycle - 16.0.15 + 16.0.16 Package containing a prototype for lifecycle implementation Ivan Paunovic Jacob Perron From b6238def5824b3bdc846ce96ec3eb6526e0e81ec Mon Sep 17 00:00:00 2001 From: "mergify[bot]" <37929162+mergify[bot]@users.noreply.github.com> Date: Tue, 18 Nov 2025 17:28:33 +0100 Subject: [PATCH 80/98] correct test function descriptions (backport #2970) (#2994) * correct test function descriptions (#2970) Signed-off-by: Yuchen Liu (cherry picked from commit 354413c0606dd66739054806cafc49dcd08db0e9) Signed-off-by: Alejandro Hernandez Cordero Co-authored-by: Yuchen966 <70603129+Yuchen966@users.noreply.github.com> Co-authored-by: Alejandro Hernandez Cordero --- rclcpp/test/rclcpp/test_intra_process_manager.cpp | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/rclcpp/test/rclcpp/test_intra_process_manager.cpp b/rclcpp/test/rclcpp/test_intra_process_manager.cpp index 45d916b004..17dc8a043e 100644 --- a/rclcpp/test/rclcpp/test_intra_process_manager.cpp +++ b/rclcpp/test/rclcpp/test_intra_process_manager.cpp @@ -430,7 +430,7 @@ TEST(TestIntraProcessManager, add_pub_sub) { - Remove the first subscription from ipm and add a new one. - Publishes a unique_ptr message with a subscription not requesting ownership. - The received message is expected to be the same, the first subscription do not receive it. - - Publishes a shared_ptr message with a subscription not requesting ownership. + - Publishes a unique_ptr message with a subscription not requesting ownership. - The received message is expected to be the same. */ TEST(TestIntraProcessManager, single_subscription) { @@ -482,9 +482,9 @@ TEST(TestIntraProcessManager, single_subscription) { - One is expected to receive the published message, while the other will receive a copy. - Publishes a unique_ptr message with 2 subscriptions not requesting ownership. - Both received messages are expected to be the same as the published one. - - Publishes a shared_ptr message with 2 subscriptions requesting ownership. + - Publishes a unique_ptr message with 2 subscriptions requesting ownership. - Both received messages are expected to be a copy of the published one. - - Publishes a shared_ptr message with 2 subscriptions not requesting ownership. + - Publishes a unique_ptr message with 2 subscriptions not requesting ownership. - Both received messages are expected to be the same as the published one. */ TEST(TestIntraProcessManager, multiple_subscriptions_same_type) { @@ -589,9 +589,9 @@ TEST(TestIntraProcessManager, multiple_subscriptions_same_type) { - The 2 subscriptions not requesting ownership are expected to both receive the same copy of the message, one of the subscription requesting ownership is expected to receive a different copy, while the last is expected to receive the published message. - - Publishes a shared_ptr message with 1 subscription requesting ownership and 1 not. - - The subscription requesting ownership is expected to receive a copy of the message, while - the other is expected to receive the published message + - Publishes a unique_ptr message with 1 subscription requesting ownership and 1 not. + - The subscription requesting ownership is expected to receive the published message, while + the other is expected to receive a copy of the published message */ TEST(TestIntraProcessManager, multiple_subscriptions_different_type) { using IntraProcessManagerT = rclcpp::experimental::IntraProcessManager; From 2ba0b01d09e565f94c3ffcb02879fb4f5f6ebbc8 Mon Sep 17 00:00:00 2001 From: "mergify[bot]" <37929162+mergify[bot]@users.noreply.github.com> Date: Mon, 8 Dec 2025 10:57:51 +0900 Subject: [PATCH 81/98] remove I/O from signal handler. (#3000) (#3005) (cherry picked from commit 3ce946f6e9e981f9b5c6029ab944063ad06c0a15) Signed-off-by: Tomoya Fujita Co-authored-by: Tomoya Fujita --- rclcpp/src/rclcpp/signal_handler.cpp | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/rclcpp/src/rclcpp/signal_handler.cpp b/rclcpp/src/rclcpp/signal_handler.cpp index cf26d06df4..c0c6b70fea 100644 --- a/rclcpp/src/rclcpp/signal_handler.cpp +++ b/rclcpp/src/rclcpp/signal_handler.cpp @@ -68,7 +68,6 @@ void SignalHandler::signal_handler( int signum, siginfo_t * siginfo, void * context) { - RCLCPP_INFO(SignalHandler::get_logger(), "signal_handler(signum=%d)", signum); auto & instance = SignalHandler::get_global_signal_handler(); auto old_signal_handler = instance.get_old_signal_handler(signum); @@ -91,7 +90,6 @@ SignalHandler::signal_handler( void SignalHandler::signal_handler(int signum) { - RCLCPP_INFO(SignalHandler::get_logger(), "signal_handler(signum=%d)", signum); auto & instance = SignalHandler::get_global_signal_handler(); auto old_signal_handler = instance.get_old_signal_handler(signum); if ( @@ -249,9 +247,6 @@ SignalHandler::signal_handler_common() { auto & instance = SignalHandler::get_global_signal_handler(); instance.signal_received_.store(true); - RCLCPP_DEBUG( - get_logger(), - "signal_handler(): notifying deferred signal handler"); instance.notify_signal_handler(); } @@ -260,6 +255,7 @@ SignalHandler::deferred_signal_handler() { while (true) { if (signal_received_.exchange(false)) { + RCLCPP_INFO(SignalHandler::get_logger(), "signal_handler(SIGINT/SIGTERM)"); RCLCPP_DEBUG(get_logger(), "deferred_signal_handler(): shutting down"); for (auto context_ptr : rclcpp::get_contexts()) { if (context_ptr->get_init_options().shutdown_on_signal) { From 4e834faf91039788acb0fe3eeb28ec989dd23e01 Mon Sep 17 00:00:00 2001 From: fabianhirmann <117293434+fabianhirmann@users.noreply.github.com> Date: Thu, 18 Dec 2025 10:11:02 +0100 Subject: [PATCH 82/98] [Humble] Implement Unified Node Interface (NodeInterfaces class) (backport #2041) (#3002) Signed-off-by: Fabian Hirmann Co-authored-by: methylDragon Co-authored-by: William Woodall --- .../rclcpp/detail/template_contains.hpp | 47 ++++ .../include/rclcpp/detail/template_unique.hpp | 49 ++++ .../detail/node_interfaces_helpers.hpp | 204 +++++++++++++++ .../node_interfaces/node_base_interface.hpp | 3 + .../node_interfaces/node_clock_interface.hpp | 3 + .../node_interfaces/node_graph_interface.hpp | 3 + .../node_interfaces/node_interfaces.hpp | 171 ++++++++++++ .../node_logging_interface.hpp | 3 + .../node_parameters_interface.hpp | 3 + .../node_services_interface.hpp | 3 + .../node_time_source_interface.hpp | 3 + .../node_interfaces/node_timers_interface.hpp | 3 + .../node_interfaces/node_topics_interface.hpp | 3 + .../node_waitables_interface.hpp | 3 + rclcpp/test/rclcpp/CMakeLists.txt | 10 + .../detail/test_template_utils.cpp | 56 ++++ .../node_interfaces/test_node_interfaces.cpp | 245 ++++++++++++++++++ .../lifecycle_node_interface.hpp | 5 + 18 files changed, 817 insertions(+) create mode 100644 rclcpp/include/rclcpp/detail/template_contains.hpp create mode 100644 rclcpp/include/rclcpp/detail/template_unique.hpp create mode 100644 rclcpp/include/rclcpp/node_interfaces/detail/node_interfaces_helpers.hpp create mode 100644 rclcpp/include/rclcpp/node_interfaces/node_interfaces.hpp create mode 100644 rclcpp/test/rclcpp/node_interfaces/detail/test_template_utils.cpp create mode 100644 rclcpp/test/rclcpp/node_interfaces/test_node_interfaces.cpp diff --git a/rclcpp/include/rclcpp/detail/template_contains.hpp b/rclcpp/include/rclcpp/detail/template_contains.hpp new file mode 100644 index 0000000000..b60a75f36d --- /dev/null +++ b/rclcpp/include/rclcpp/detail/template_contains.hpp @@ -0,0 +1,47 @@ +// 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__DETAIL__TEMPLATE_CONTAINS_HPP_ +#define RCLCPP__DETAIL__TEMPLATE_CONTAINS_HPP_ + +#include + +namespace rclcpp +{ +namespace detail +{ + +/// Template meta-function that checks if a given T is contained in the list Us. +template +struct template_contains; + +template +inline constexpr bool template_contains_v = template_contains::value; + +template +struct template_contains +{ + enum { value = (std::is_same_v|| template_contains_v)}; +}; + +template +struct template_contains +{ + enum { value = false }; +}; + +} // namespace detail +} // namespace rclcpp + +#endif // RCLCPP__DETAIL__TEMPLATE_CONTAINS_HPP_ diff --git a/rclcpp/include/rclcpp/detail/template_unique.hpp b/rclcpp/include/rclcpp/detail/template_unique.hpp new file mode 100644 index 0000000000..4986102d78 --- /dev/null +++ b/rclcpp/include/rclcpp/detail/template_unique.hpp @@ -0,0 +1,49 @@ +// 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__DETAIL__TEMPLATE_UNIQUE_HPP_ +#define RCLCPP__DETAIL__TEMPLATE_UNIQUE_HPP_ + +#include + +#include "rclcpp/detail/template_contains.hpp" + +namespace rclcpp +{ +namespace detail +{ + +/// Template meta-function that checks if a given list Ts contains unique types. +template +struct template_unique; + +template +inline constexpr bool template_unique_v = template_unique::value; + +template +struct template_unique +{ + enum { value = !template_contains_v&& template_unique_v}; +}; + +template +struct template_unique +{ + enum { value = true }; +}; + +} // namespace detail +} // namespace rclcpp + +#endif // RCLCPP__DETAIL__TEMPLATE_UNIQUE_HPP_ diff --git a/rclcpp/include/rclcpp/node_interfaces/detail/node_interfaces_helpers.hpp b/rclcpp/include/rclcpp/node_interfaces/detail/node_interfaces_helpers.hpp new file mode 100644 index 0000000000..8f7b452f5f --- /dev/null +++ b/rclcpp/include/rclcpp/node_interfaces/detail/node_interfaces_helpers.hpp @@ -0,0 +1,204 @@ +// 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__NODE_INTERFACES__DETAIL__NODE_INTERFACES_HELPERS_HPP_ +#define RCLCPP__NODE_INTERFACES__DETAIL__NODE_INTERFACES_HELPERS_HPP_ + +#include +#include +#include +#include + +#include "rclcpp/visibility_control.hpp" + +namespace rclcpp +{ +namespace node_interfaces +{ +namespace detail +{ + +// Support and Helper template classes for the NodeInterfaces class. + +template +std::tuple...> +init_tuple(NodeT & n); + +/// Stores the interfaces in a tuple, provides constructors, and getters. +template +struct NodeInterfacesStorage +{ + template + NodeInterfacesStorage(NodeT & node) // NOLINT(runtime/explicit) + : interfaces_(init_tuple(node)) + {} + + explicit NodeInterfacesStorage(std::shared_ptr... args) + : interfaces_(args ...) + {} + + /// Individual Node Interface non-const getter. + template + std::shared_ptr + get() + { + static_assert( + (std::is_same_v|| ...), + "NodeInterfaces class does not contain given NodeInterfaceT"); + return std::get>(interfaces_); + } + + /// Individual Node Interface const getter. + template + std::shared_ptr + get() const + { + static_assert( + (std::is_same_v|| ...), + "NodeInterfaces class does not contain given NodeInterfaceT"); + return std::get>(interfaces_); + } + +protected: + std::tuple...> interfaces_; +}; + +/// Prototype of NodeInterfacesSupports. +/** + * Should read NodeInterfacesSupports<..., T, ...> as "NodeInterfaces supports T", and + * if NodeInterfacesSupport is specialized for T, the is_supported should be + * set to std::true_type, but by default it is std::false_type, which will + * lead to a compiler error when trying to use T with NodeInterfaces. + */ +template +struct NodeInterfacesSupports; + +/// Prototype of NodeInterfacesSupportCheck template meta-function. +/** + * This meta-function checks that all the types given are supported, + * throwing a more human-readable error if an unsupported type is used. + */ +template +struct NodeInterfacesSupportCheck; + +/// Iterating specialization that ensures classes are supported and inherited. +template +struct NodeInterfacesSupportCheck + : public NodeInterfacesSupportCheck +{ + static_assert( + NodeInterfacesSupports::is_supported::value, + "given NodeInterfaceT is not supported by rclcpp::node_interfaces::NodeInterfaces"); +}; + +/// Terminating case when there are no more "RemainingInterfaceTs". +template +struct NodeInterfacesSupportCheck +{}; + +/// Default specialization, needs to be specialized for each supported interface. +template +struct NodeInterfacesSupports +{ + // Specializations need to set this to std::true_type in addition to other interfaces. + using is_supported = std::false_type; +}; + +/// Terminating specialization of NodeInterfacesSupports. +template +struct NodeInterfacesSupports + : public StorageClassT +{ + /// Perfect forwarding constructor to get arguments down to StorageClassT. + template + explicit NodeInterfacesSupports(ArgsT && ... args) + : StorageClassT(std::forward(args) ...) + {} +}; + +// Helper functions to initialize the tuple in NodeInterfaces. + +template +void +init_element(TupleT & t, NodeT & n) +{ + std::get>(t) = + NodeInterfacesSupports::get_from_node_like(n); +} + +template +std::tuple...> +init_tuple(NodeT & n) +{ + using StorageClassT = NodeInterfacesStorage; + std::tuple...> t; + (init_element(t, n), ...); + return t; +} + +/// Macro for creating specializations with less boilerplate. +/** + * You can use this macro to add support for your interface class if: + * + * - The standard getter is get_node_{NodeInterfaceName}_interface(), and + * - the getter returns a non-const shared_ptr<{NodeInterfaceType}> + * + * Examples of using this can be seen in the standard node interface headers + * in rclcpp, e.g. rclcpp/node_interfaces/node_base_interface.hpp has: + * + * RCLCPP_NODE_INTERFACE_HELPERS_SUPPORT(rclcpp::node_interfaces::NodeBaseInterface, base) + * + * If your interface has a non-standard getter, or you want to instrument it or + * something like that, then you'll need to create your own specialization of + * the NodeInterfacesSupports struct without this macro. + */ +#define RCLCPP_NODE_INTERFACE_HELPERS_SUPPORT(NodeInterfaceType, NodeInterfaceName) \ + namespace rclcpp::node_interfaces::detail { \ + template \ + struct NodeInterfacesSupports< \ + StorageClassT, \ + NodeInterfaceType, \ + RemainingInterfaceTs ...> \ + : public NodeInterfacesSupports \ + { \ + using is_supported = std::true_type; \ + \ + template \ + static \ + std::shared_ptr \ + get_from_node_like(NodeT & node_like) \ + { \ + return node_like.get_node_ ## NodeInterfaceName ## _interface(); \ + } \ + \ + /* Perfect forwarding constructor to get arguments down to StorageClassT (eventually). */ \ + template \ + explicit NodeInterfacesSupports(ArgsT && ... args) \ + : NodeInterfacesSupports( \ + std::forward(args) ...) \ + {} \ + \ + std::shared_ptr \ + get_node_ ## NodeInterfaceName ## _interface() \ + { \ + return StorageClassT::template get(); \ + } \ + }; \ + } // namespace rclcpp::node_interfaces::detail + +} // namespace detail +} // namespace node_interfaces +} // namespace rclcpp + +#endif // RCLCPP__NODE_INTERFACES__DETAIL__NODE_INTERFACES_HELPERS_HPP_ diff --git a/rclcpp/include/rclcpp/node_interfaces/node_base_interface.hpp b/rclcpp/include/rclcpp/node_interfaces/node_base_interface.hpp index b0f8fbd65e..79a156461b 100644 --- a/rclcpp/include/rclcpp/node_interfaces/node_base_interface.hpp +++ b/rclcpp/include/rclcpp/node_interfaces/node_base_interface.hpp @@ -26,6 +26,7 @@ #include "rclcpp/context.hpp" #include "rclcpp/guard_condition.hpp" #include "rclcpp/macros.hpp" +#include "rclcpp/node_interfaces/detail/node_interfaces_helpers.hpp" #include "rclcpp/visibility_control.hpp" namespace rclcpp @@ -177,4 +178,6 @@ class NodeBaseInterface } // namespace node_interfaces } // namespace rclcpp +RCLCPP_NODE_INTERFACE_HELPERS_SUPPORT(rclcpp::node_interfaces::NodeBaseInterface, base) + #endif // RCLCPP__NODE_INTERFACES__NODE_BASE_INTERFACE_HPP_ diff --git a/rclcpp/include/rclcpp/node_interfaces/node_clock_interface.hpp b/rclcpp/include/rclcpp/node_interfaces/node_clock_interface.hpp index 8965a371d8..744eef4ce6 100644 --- a/rclcpp/include/rclcpp/node_interfaces/node_clock_interface.hpp +++ b/rclcpp/include/rclcpp/node_interfaces/node_clock_interface.hpp @@ -17,6 +17,7 @@ #include "rclcpp/clock.hpp" #include "rclcpp/macros.hpp" +#include "rclcpp/node_interfaces/detail/node_interfaces_helpers.hpp" #include "rclcpp/visibility_control.hpp" namespace rclcpp @@ -50,4 +51,6 @@ class NodeClockInterface } // namespace node_interfaces } // namespace rclcpp +RCLCPP_NODE_INTERFACE_HELPERS_SUPPORT(rclcpp::node_interfaces::NodeClockInterface, clock) + #endif // RCLCPP__NODE_INTERFACES__NODE_CLOCK_INTERFACE_HPP_ diff --git a/rclcpp/include/rclcpp/node_interfaces/node_graph_interface.hpp b/rclcpp/include/rclcpp/node_interfaces/node_graph_interface.hpp index 3958f5b2d9..fefda0da69 100644 --- a/rclcpp/include/rclcpp/node_interfaces/node_graph_interface.hpp +++ b/rclcpp/include/rclcpp/node_interfaces/node_graph_interface.hpp @@ -29,6 +29,7 @@ #include "rclcpp/event.hpp" #include "rclcpp/macros.hpp" +#include "rclcpp/node_interfaces/detail/node_interfaces_helpers.hpp" #include "rclcpp/qos.hpp" #include "rclcpp/visibility_control.hpp" @@ -382,4 +383,6 @@ class NodeGraphInterface } // namespace node_interfaces } // namespace rclcpp +RCLCPP_NODE_INTERFACE_HELPERS_SUPPORT(rclcpp::node_interfaces::NodeGraphInterface, graph) + #endif // RCLCPP__NODE_INTERFACES__NODE_GRAPH_INTERFACE_HPP_ diff --git a/rclcpp/include/rclcpp/node_interfaces/node_interfaces.hpp b/rclcpp/include/rclcpp/node_interfaces/node_interfaces.hpp new file mode 100644 index 0000000000..1845e84dcb --- /dev/null +++ b/rclcpp/include/rclcpp/node_interfaces/node_interfaces.hpp @@ -0,0 +1,171 @@ +// 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__NODE_INTERFACES__NODE_INTERFACES_HPP_ +#define RCLCPP__NODE_INTERFACES__NODE_INTERFACES_HPP_ + +#include + +#include "rclcpp/detail/template_unique.hpp" +#include "rclcpp/node_interfaces/detail/node_interfaces_helpers.hpp" + +#define ALL_RCLCPP_NODE_INTERFACES \ + rclcpp::node_interfaces::NodeBaseInterface, \ + rclcpp::node_interfaces::NodeClockInterface, \ + rclcpp::node_interfaces::NodeGraphInterface, \ + rclcpp::node_interfaces::NodeLoggingInterface, \ + rclcpp::node_interfaces::NodeParametersInterface, \ + rclcpp::node_interfaces::NodeServicesInterface, \ + rclcpp::node_interfaces::NodeTimeSourceInterface, \ + rclcpp::node_interfaces::NodeTimersInterface, \ + rclcpp::node_interfaces::NodeTopicsInterface, \ + rclcpp::node_interfaces::NodeWaitablesInterface + + +namespace rclcpp +{ +namespace node_interfaces +{ + + +/// A helper class for aggregating node interfaces. +template +class NodeInterfaces + : public detail::NodeInterfacesSupportCheck< + detail::NodeInterfacesStorage, + InterfaceTs ... + >, + public detail::NodeInterfacesSupports< + detail::NodeInterfacesStorage, + InterfaceTs ... + > +{ + static_assert( + 0 != sizeof ...(InterfaceTs), + "must provide at least one interface as a template argument"); + static_assert( + rclcpp::detail::template_unique_v, + "must provide unique template parameters"); + + using NodeInterfacesSupportsT = detail::NodeInterfacesSupports< + detail::NodeInterfacesStorage, + InterfaceTs ... + >; + +public: + /// Create a new NodeInterfaces object using the given node-like object's interfaces. + /** + * Specify which interfaces you need by passing them as template parameters. + * + * This allows you to aggregate interfaces from different sources together to pass as a single + * aggregate object to any functions that take node interfaces or node-likes, without needing to + * templatize that function. + * + * You may also use this constructor to create a NodeInterfaces that contains a subset of + * another NodeInterfaces' interfaces. + * + * Finally, this class supports implicit conversion from node-like objects, allowing you to + * directly pass a node-like to a function that takes a NodeInterfaces object. + * + * Usage examples: + * ```cpp + * // Suppose we have some function: + * void fn(NodeInterfaces interfaces); + * + * // Then we can, explicitly: + * rclcpp::Node node("some_node"); + * auto ni = NodeInterfaces(node); + * fn(ni); + * + * // But also: + * fn(node); + * + * // Subsetting a NodeInterfaces object also works! + * auto ni_base = NodeInterfaces(ni); + * + * // Or aggregate them (you could aggregate interfaces from disparate node-likes) + * auto ni_aggregated = NodeInterfaces( + * node->get_node_base_interface(), + * node->get_node_clock_interface() + * ) + * + * // And then to access the interfaces: + * // Get with get<> + * auto base = ni.get(); + * + * // Or the appropriate getter + * auto clock = ni.get_clock_interface(); + * ``` + * + * You may use any of the standard node interfaces that come with rclcpp: + * - rclcpp::node_interfaces::NodeBaseInterface + * - rclcpp::node_interfaces::NodeClockInterface + * - rclcpp::node_interfaces::NodeGraphInterface + * - rclcpp::node_interfaces::NodeLoggingInterface + * - rclcpp::node_interfaces::NodeParametersInterface + * - rclcpp::node_interfaces::NodeServicesInterface + * - rclcpp::node_interfaces::NodeTimeSourceInterface + * - rclcpp::node_interfaces::NodeTimersInterface + * - rclcpp::node_interfaces::NodeTopicsInterface + * - rclcpp::node_interfaces::NodeWaitablesInterface + * + * Or you use custom interfaces as long as you make a template specialization + * of the rclcpp::node_interfaces::detail::NodeInterfacesSupport struct using + * the RCLCPP_NODE_INTERFACE_HELPERS_SUPPORT macro. + * + * Usage example: + * ```RCLCPP_NODE_INTERFACE_HELPERS_SUPPORT(rclcpp::node_interfaces::NodeBaseInterface, base)``` + * + * If you choose not to use the helper macro, then you can specialize the + * template yourself, but you must: + * + * - Provide a template specialization of the get_from_node_like method that gets the interface + * from any node-like that stores the interface, using the node-like's getter + * - Designate the is_supported type as std::true_type using a using directive + * - Provide any number of getter methods to be used to obtain the interface with the + * NodeInterface object, noting that the getters of the storage class will apply to all + * supported interfaces. + * - The getter method names should not clash in name with any other interface getter + * specializations if those other interfaces are meant to be aggregated in the same + * NodeInterfaces object. + * + * \param[in] node Node-like object from which to get the node interfaces + */ + template + NodeInterfaces(NodeT & node) // NOLINT(runtime/explicit) + : NodeInterfacesSupportsT(node) + {} + + /// NodeT::SharedPtr Constructor + template + NodeInterfaces(std::shared_ptr node) // NOLINT(runtime/explicit) + : NodeInterfaces( + [&]() -> NodeT & { + if (!node) { + throw std::invalid_argument("given node pointer is nullptr"); + } + return *node; + }()) + {} + + explicit NodeInterfaces(std::shared_ptr... args) + : NodeInterfacesSupportsT(args ...) + {} +}; + + +} // namespace node_interfaces +} // namespace rclcpp + +#endif // RCLCPP__NODE_INTERFACES__NODE_INTERFACES_HPP_ diff --git a/rclcpp/include/rclcpp/node_interfaces/node_logging_interface.hpp b/rclcpp/include/rclcpp/node_interfaces/node_logging_interface.hpp index 669da55ee6..681f5c28e8 100644 --- a/rclcpp/include/rclcpp/node_interfaces/node_logging_interface.hpp +++ b/rclcpp/include/rclcpp/node_interfaces/node_logging_interface.hpp @@ -19,6 +19,7 @@ #include "rclcpp/logger.hpp" #include "rclcpp/macros.hpp" +#include "rclcpp/node_interfaces/detail/node_interfaces_helpers.hpp" #include "rclcpp/visibility_control.hpp" namespace rclcpp @@ -54,4 +55,6 @@ class NodeLoggingInterface } // namespace node_interfaces } // namespace rclcpp +RCLCPP_NODE_INTERFACE_HELPERS_SUPPORT(rclcpp::node_interfaces::NodeLoggingInterface, logging) + #endif // RCLCPP__NODE_INTERFACES__NODE_LOGGING_INTERFACE_HPP_ diff --git a/rclcpp/include/rclcpp/node_interfaces/node_parameters_interface.hpp b/rclcpp/include/rclcpp/node_interfaces/node_parameters_interface.hpp index 743c1b8d4f..729a006b26 100644 --- a/rclcpp/include/rclcpp/node_interfaces/node_parameters_interface.hpp +++ b/rclcpp/include/rclcpp/node_interfaces/node_parameters_interface.hpp @@ -25,6 +25,7 @@ #include "rcl_interfaces/msg/set_parameters_result.hpp" #include "rclcpp/macros.hpp" +#include "rclcpp/node_interfaces/detail/node_interfaces_helpers.hpp" #include "rclcpp/parameter.hpp" #include "rclcpp/visibility_control.hpp" @@ -215,4 +216,6 @@ class NodeParametersInterface } // namespace node_interfaces } // namespace rclcpp +RCLCPP_NODE_INTERFACE_HELPERS_SUPPORT(rclcpp::node_interfaces::NodeParametersInterface, parameters) + #endif // RCLCPP__NODE_INTERFACES__NODE_PARAMETERS_INTERFACE_HPP_ diff --git a/rclcpp/include/rclcpp/node_interfaces/node_services_interface.hpp b/rclcpp/include/rclcpp/node_interfaces/node_services_interface.hpp index ab6cdab42e..ff58ef36b6 100644 --- a/rclcpp/include/rclcpp/node_interfaces/node_services_interface.hpp +++ b/rclcpp/include/rclcpp/node_interfaces/node_services_interface.hpp @@ -20,6 +20,7 @@ #include "rclcpp/callback_group.hpp" #include "rclcpp/client.hpp" #include "rclcpp/macros.hpp" +#include "rclcpp/node_interfaces/detail/node_interfaces_helpers.hpp" #include "rclcpp/service.hpp" #include "rclcpp/visibility_control.hpp" @@ -62,4 +63,6 @@ class NodeServicesInterface } // namespace node_interfaces } // namespace rclcpp +RCLCPP_NODE_INTERFACE_HELPERS_SUPPORT(rclcpp::node_interfaces::NodeServicesInterface, services) + #endif // RCLCPP__NODE_INTERFACES__NODE_SERVICES_INTERFACE_HPP_ diff --git a/rclcpp/include/rclcpp/node_interfaces/node_time_source_interface.hpp b/rclcpp/include/rclcpp/node_interfaces/node_time_source_interface.hpp index 5b7065193e..3783e5d83a 100644 --- a/rclcpp/include/rclcpp/node_interfaces/node_time_source_interface.hpp +++ b/rclcpp/include/rclcpp/node_interfaces/node_time_source_interface.hpp @@ -16,6 +16,7 @@ #define RCLCPP__NODE_INTERFACES__NODE_TIME_SOURCE_INTERFACE_HPP_ #include "rclcpp/macros.hpp" +#include "rclcpp/node_interfaces/detail/node_interfaces_helpers.hpp" #include "rclcpp/visibility_control.hpp" namespace rclcpp @@ -37,4 +38,6 @@ class NodeTimeSourceInterface } // namespace node_interfaces } // namespace rclcpp +RCLCPP_NODE_INTERFACE_HELPERS_SUPPORT(rclcpp::node_interfaces::NodeTimeSourceInterface, time_source) + #endif // RCLCPP__NODE_INTERFACES__NODE_TIME_SOURCE_INTERFACE_HPP_ diff --git a/rclcpp/include/rclcpp/node_interfaces/node_timers_interface.hpp b/rclcpp/include/rclcpp/node_interfaces/node_timers_interface.hpp index 4573d3d02b..2f1aaef51e 100644 --- a/rclcpp/include/rclcpp/node_interfaces/node_timers_interface.hpp +++ b/rclcpp/include/rclcpp/node_interfaces/node_timers_interface.hpp @@ -17,6 +17,7 @@ #include "rclcpp/callback_group.hpp" #include "rclcpp/macros.hpp" +#include "rclcpp/node_interfaces/detail/node_interfaces_helpers.hpp" #include "rclcpp/timer.hpp" #include "rclcpp/visibility_control.hpp" @@ -47,4 +48,6 @@ class NodeTimersInterface } // namespace node_interfaces } // namespace rclcpp +RCLCPP_NODE_INTERFACE_HELPERS_SUPPORT(rclcpp::node_interfaces::NodeTimersInterface, timers) + #endif // RCLCPP__NODE_INTERFACES__NODE_TIMERS_INTERFACE_HPP_ diff --git a/rclcpp/include/rclcpp/node_interfaces/node_topics_interface.hpp b/rclcpp/include/rclcpp/node_interfaces/node_topics_interface.hpp index ca69e86e73..392989a9b8 100644 --- a/rclcpp/include/rclcpp/node_interfaces/node_topics_interface.hpp +++ b/rclcpp/include/rclcpp/node_interfaces/node_topics_interface.hpp @@ -24,6 +24,7 @@ #include "rclcpp/callback_group.hpp" #include "rclcpp/macros.hpp" +#include "rclcpp/node_interfaces/detail/node_interfaces_helpers.hpp" #include "rclcpp/node_interfaces/node_base_interface.hpp" #include "rclcpp/node_interfaces/node_timers_interface.hpp" #include "rclcpp/publisher.hpp" @@ -97,4 +98,6 @@ class NodeTopicsInterface } // namespace node_interfaces } // namespace rclcpp +RCLCPP_NODE_INTERFACE_HELPERS_SUPPORT(rclcpp::node_interfaces::NodeTopicsInterface, topics) + #endif // RCLCPP__NODE_INTERFACES__NODE_TOPICS_INTERFACE_HPP_ diff --git a/rclcpp/include/rclcpp/node_interfaces/node_waitables_interface.hpp b/rclcpp/include/rclcpp/node_interfaces/node_waitables_interface.hpp index 0faae1da62..fd0029a89b 100644 --- a/rclcpp/include/rclcpp/node_interfaces/node_waitables_interface.hpp +++ b/rclcpp/include/rclcpp/node_interfaces/node_waitables_interface.hpp @@ -17,6 +17,7 @@ #include "rclcpp/callback_group.hpp" #include "rclcpp/macros.hpp" +#include "rclcpp/node_interfaces/detail/node_interfaces_helpers.hpp" #include "rclcpp/visibility_control.hpp" #include "rclcpp/waitable.hpp" @@ -54,4 +55,6 @@ class NodeWaitablesInterface } // namespace node_interfaces } // namespace rclcpp +RCLCPP_NODE_INTERFACE_HELPERS_SUPPORT(rclcpp::node_interfaces::NodeWaitablesInterface, waitables) + #endif // RCLCPP__NODE_INTERFACES__NODE_WAITABLES_INTERFACE_HPP_ diff --git a/rclcpp/test/rclcpp/CMakeLists.txt b/rclcpp/test/rclcpp/CMakeLists.txt index 0bdb0d931e..23c6cc242d 100644 --- a/rclcpp/test/rclcpp/CMakeLists.txt +++ b/rclcpp/test/rclcpp/CMakeLists.txt @@ -234,6 +234,11 @@ if(TARGET test_node_interfaces__node_graph) "test_msgs") target_link_libraries(test_node_interfaces__node_graph ${PROJECT_NAME} mimick) endif() +ament_add_gtest(test_node_interfaces__node_interfaces + node_interfaces/test_node_interfaces.cpp) +if(TARGET test_node_interfaces__node_interfaces) + target_link_libraries(test_node_interfaces__node_interfaces ${PROJECT_NAME} mimick) +endif() ament_add_gtest(test_node_interfaces__node_parameters node_interfaces/test_node_parameters.cpp) if(TARGET test_node_interfaces__node_parameters) @@ -262,6 +267,11 @@ ament_add_gtest(test_node_interfaces__node_waitables if(TARGET test_node_interfaces__node_waitables) target_link_libraries(test_node_interfaces__node_waitables ${PROJECT_NAME} mimick) endif() +ament_add_gtest(test_node_interfaces__test_template_utils # Compile time test + node_interfaces/detail/test_template_utils.cpp) +if(TARGET test_node_interfaces__test_template_utils) + target_link_libraries(test_node_interfaces__test_template_utils ${PROJECT_NAME}) +endif() # TODO(wjwwood): reenable these build failure tests when I can get Jenkins to ignore their output # rclcpp_add_build_failure_test(build_failure__get_node_topics_interface_const_ref_rclcpp_node diff --git a/rclcpp/test/rclcpp/node_interfaces/detail/test_template_utils.cpp b/rclcpp/test/rclcpp/node_interfaces/detail/test_template_utils.cpp new file mode 100644 index 0000000000..9ae715ebce --- /dev/null +++ b/rclcpp/test/rclcpp/node_interfaces/detail/test_template_utils.cpp @@ -0,0 +1,56 @@ +// 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 + +#include "rclcpp/detail/template_contains.hpp" +#include "rclcpp/detail/template_unique.hpp" + + +TEST(NoOpTests, test_node_interfaces_template_utils) { +} // This is just to let gtest work + +namespace rclcpp +{ +namespace detail +{ + +// This tests template logic at compile time +namespace +{ + +struct test_template_unique +{ + static_assert(template_unique_v, "failed"); + static_assert(template_unique_v, "failed"); + static_assert(!template_unique_v, "failed"); + static_assert(!template_unique_v, "failed"); + static_assert(!template_unique_v, "failed"); +}; + + +struct test_template_contains +{ + static_assert(template_contains_v, "failed"); + static_assert(template_contains_v, "failed"); + static_assert(template_contains_v, "failed"); + static_assert(!template_contains_v, "failed"); + static_assert(!template_contains_v, "failed"); + static_assert(!template_contains_v, "failed"); +}; + +} // namespace + +} // namespace detail +} // namespace rclcpp diff --git a/rclcpp/test/rclcpp/node_interfaces/test_node_interfaces.cpp b/rclcpp/test/rclcpp/node_interfaces/test_node_interfaces.cpp new file mode 100644 index 0000000000..96596dea37 --- /dev/null +++ b/rclcpp/test/rclcpp/node_interfaces/test_node_interfaces.cpp @@ -0,0 +1,245 @@ +// 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 + +#include "rclcpp/node.hpp" +#include "rclcpp/node_interfaces/node_interfaces.hpp" +#include "rclcpp/node_interfaces/node_base_interface.hpp" +#include "rclcpp/node_interfaces/node_graph_interface.hpp" + +class TestNodeInterfaces : public ::testing::Test +{ +protected: + static void SetUpTestCase() + { + rclcpp::init(0, nullptr); + } + + static void TearDownTestCase() + { + rclcpp::shutdown(); + } +}; + +/* + Testing NodeInterfaces construction from nodes. + */ +TEST_F(TestNodeInterfaces, node_interfaces_nominal) { + auto node = std::make_shared("my_node"); + + // Create a NodeInterfaces for base and graph using a rclcpp::Node. + { + using rclcpp::node_interfaces::NodeInterfaces; + using rclcpp::node_interfaces::NodeBaseInterface; + using rclcpp::node_interfaces::NodeGraphInterface; + auto node_interfaces = NodeInterfaces(node); + } + + // Implicit conversion of rclcpp::Node into function that uses NodeInterfaces of base. + { + using rclcpp::node_interfaces::NodeInterfaces; + using rclcpp::node_interfaces::NodeBaseInterface; + auto some_func = [](NodeInterfaces ni) { + auto base_interface = ni.get(); + }; + + some_func(node); + } + + // Implicit narrowing of NodeInterfaces into a new interface NodeInterfaces with fewer interfaces. + { + using rclcpp::node_interfaces::NodeInterfaces; + using rclcpp::node_interfaces::NodeBaseInterface; + using rclcpp::node_interfaces::NodeGraphInterface; + auto some_func = [](NodeInterfaces ni_with_one) { + auto base_interface = ni_with_one.get(); + }; + + NodeInterfaces ni_with_two(node); + + some_func(ni_with_two); + } + + // Create a NodeInterfaces via aggregation of interfaces in constructor. + { + using rclcpp::node_interfaces::NodeInterfaces; + using rclcpp::node_interfaces::NodeBaseInterface; + using rclcpp::node_interfaces::NodeGraphInterface; + auto loose_node_base = node->get_node_base_interface(); + auto loose_node_graph = node->get_node_graph_interface(); + auto ni = NodeInterfaces( + loose_node_base, + loose_node_graph); + } +} + +/* + Test construction with all standard rclcpp::node_interfaces::Node*Interfaces. + */ +TEST_F(TestNodeInterfaces, node_interfaces_standard_interfaces) { + auto node = std::make_shared("my_node", "/ns"); + + auto ni = rclcpp::node_interfaces::NodeInterfaces< + rclcpp::node_interfaces::NodeBaseInterface, + rclcpp::node_interfaces::NodeClockInterface, + rclcpp::node_interfaces::NodeGraphInterface, + rclcpp::node_interfaces::NodeLoggingInterface, + rclcpp::node_interfaces::NodeTimersInterface, + rclcpp::node_interfaces::NodeTopicsInterface, + rclcpp::node_interfaces::NodeServicesInterface, + rclcpp::node_interfaces::NodeWaitablesInterface, + rclcpp::node_interfaces::NodeParametersInterface, + rclcpp::node_interfaces::NodeTimeSourceInterface + >(node); +} + +/* + Testing getters. + */ +TEST_F(TestNodeInterfaces, ni_init) { + auto node = std::make_shared("my_node", "/ns"); + + using rclcpp::node_interfaces::NodeInterfaces; + using rclcpp::node_interfaces::NodeBaseInterface; + using rclcpp::node_interfaces::NodeClockInterface; + using rclcpp::node_interfaces::NodeGraphInterface; + using rclcpp::node_interfaces::NodeLoggingInterface; + using rclcpp::node_interfaces::NodeTimersInterface; + using rclcpp::node_interfaces::NodeTopicsInterface; + using rclcpp::node_interfaces::NodeServicesInterface; + using rclcpp::node_interfaces::NodeWaitablesInterface; + using rclcpp::node_interfaces::NodeParametersInterface; + using rclcpp::node_interfaces::NodeTimeSourceInterface; + + auto ni = NodeInterfaces< + NodeBaseInterface, + NodeClockInterface, + NodeGraphInterface, + NodeLoggingInterface, + NodeTimersInterface, + NodeTopicsInterface, + NodeServicesInterface, + NodeWaitablesInterface, + NodeParametersInterface, + NodeTimeSourceInterface + >(node); + + { + auto base = ni.get(); + base = ni.get_node_base_interface(); + EXPECT_STREQ(base->get_name(), "my_node"); // Test for functionality + } + { + auto clock = ni.get(); + clock = ni.get_node_clock_interface(); + clock->get_clock(); + } + { + auto graph = ni.get(); + graph = ni.get_node_graph_interface(); + } + { + auto logging = ni.get(); + logging = ni.get_node_logging_interface(); + } + { + auto timers = ni.get(); + timers = ni.get_node_timers_interface(); + } + { + auto topics = ni.get(); + topics = ni.get_node_topics_interface(); + } + { + auto services = ni.get(); + services = ni.get_node_services_interface(); + } + { + auto waitables = ni.get(); + waitables = ni.get_node_waitables_interface(); + } + { + auto parameters = ni.get(); + parameters = ni.get_node_parameters_interface(); + } + { + auto time_source = ni.get(); + time_source = ni.get_node_time_source_interface(); + } +} + +/* + Testing macro'ed getters. + */ +TEST_F(TestNodeInterfaces, ni_all_init) { + auto node = std::make_shared("my_node", "/ns"); + + using rclcpp::node_interfaces::NodeInterfaces; + using rclcpp::node_interfaces::NodeBaseInterface; + using rclcpp::node_interfaces::NodeClockInterface; + using rclcpp::node_interfaces::NodeGraphInterface; + using rclcpp::node_interfaces::NodeLoggingInterface; + using rclcpp::node_interfaces::NodeTimersInterface; + using rclcpp::node_interfaces::NodeTopicsInterface; + using rclcpp::node_interfaces::NodeServicesInterface; + using rclcpp::node_interfaces::NodeWaitablesInterface; + using rclcpp::node_interfaces::NodeParametersInterface; + using rclcpp::node_interfaces::NodeTimeSourceInterface; + + auto ni = rclcpp::node_interfaces::NodeInterfaces(node); + + { + auto base = ni.get(); + base = ni.get_node_base_interface(); + EXPECT_STREQ(base->get_name(), "my_node"); // Test for functionality + } + { + auto clock = ni.get(); + clock = ni.get_node_clock_interface(); + clock->get_clock(); + } + { + auto graph = ni.get(); + graph = ni.get_node_graph_interface(); + } + { + auto logging = ni.get(); + logging = ni.get_node_logging_interface(); + } + { + auto timers = ni.get(); + timers = ni.get_node_timers_interface(); + } + { + auto topics = ni.get(); + topics = ni.get_node_topics_interface(); + } + { + auto services = ni.get(); + services = ni.get_node_services_interface(); + } + { + auto waitables = ni.get(); + waitables = ni.get_node_waitables_interface(); + } + { + auto parameters = ni.get(); + parameters = ni.get_node_parameters_interface(); + } + { + auto time_source = ni.get(); + time_source = ni.get_node_time_source_interface(); + } +} diff --git a/rclcpp_lifecycle/include/rclcpp_lifecycle/node_interfaces/lifecycle_node_interface.hpp b/rclcpp_lifecycle/include/rclcpp_lifecycle/node_interfaces/lifecycle_node_interface.hpp index 9f2459e296..9f39f49d78 100644 --- a/rclcpp_lifecycle/include/rclcpp_lifecycle/node_interfaces/lifecycle_node_interface.hpp +++ b/rclcpp_lifecycle/include/rclcpp_lifecycle/node_interfaces/lifecycle_node_interface.hpp @@ -21,6 +21,7 @@ #include "rclcpp_lifecycle/state.hpp" #include "rclcpp_lifecycle/visibility_control.h" +#include "rclcpp/node_interfaces/detail/node_interfaces_helpers.hpp" namespace rclcpp_lifecycle { @@ -108,4 +109,8 @@ class LifecycleNodeInterface } // namespace node_interfaces } // namespace rclcpp_lifecycle + +RCLCPP_NODE_INTERFACE_HELPERS_SUPPORT( + rclcpp_lifecycle::node_interfaces::LifecycleNodeInterface, lifecycle_node) + #endif // RCLCPP_LIFECYCLE__NODE_INTERFACES__LIFECYCLE_NODE_INTERFACE_HPP_ From c42bb23a524059208304ada51055c0c88b838b46 Mon Sep 17 00:00:00 2001 From: "mergify[bot]" <37929162+mergify[bot]@users.noreply.github.com> Date: Thu, 18 Dec 2025 22:23:15 +0100 Subject: [PATCH 83/98] Unified Node Interfaces: Add const version of get_node_x_interface() (#3006) (#3010) (cherry picked from commit 825b4e465080fb44f7c6119a77f05233266635be) Signed-off-by: Fabian Hirmann Co-authored-by: fabianhirmann <117293434+fabianhirmann@users.noreply.github.com> --- .../detail/node_interfaces_helpers.hpp | 6 ++ .../node_interfaces/test_node_interfaces.cpp | 63 ++++++++++++++++++- 2 files changed, 68 insertions(+), 1 deletion(-) diff --git a/rclcpp/include/rclcpp/node_interfaces/detail/node_interfaces_helpers.hpp b/rclcpp/include/rclcpp/node_interfaces/detail/node_interfaces_helpers.hpp index 8f7b452f5f..0a95a548c2 100644 --- a/rclcpp/include/rclcpp/node_interfaces/detail/node_interfaces_helpers.hpp +++ b/rclcpp/include/rclcpp/node_interfaces/detail/node_interfaces_helpers.hpp @@ -194,6 +194,12 @@ init_tuple(NodeT & n) { \ return StorageClassT::template get(); \ } \ + \ + std::shared_ptr \ + get_node_ ## NodeInterfaceName ## _interface() const \ + { \ + return StorageClassT::template get(); \ + } \ }; \ } // namespace rclcpp::node_interfaces::detail diff --git a/rclcpp/test/rclcpp/node_interfaces/test_node_interfaces.cpp b/rclcpp/test/rclcpp/node_interfaces/test_node_interfaces.cpp index 96596dea37..a01be4ff19 100644 --- a/rclcpp/test/rclcpp/node_interfaces/test_node_interfaces.cpp +++ b/rclcpp/test/rclcpp/node_interfaces/test_node_interfaces.cpp @@ -183,7 +183,7 @@ TEST_F(TestNodeInterfaces, ni_init) { /* Testing macro'ed getters. */ -TEST_F(TestNodeInterfaces, ni_all_init) { +TEST_F(TestNodeInterfaces, ni_all_init_non_const) { auto node = std::make_shared("my_node", "/ns"); using rclcpp::node_interfaces::NodeInterfaces; @@ -243,3 +243,64 @@ TEST_F(TestNodeInterfaces, ni_all_init) { time_source = ni.get_node_time_source_interface(); } } + +TEST_F(TestNodeInterfaces, ni_all_init_const) { + auto node = std::make_shared("my_node", "/ns"); + + using rclcpp::node_interfaces::NodeInterfaces; + using rclcpp::node_interfaces::NodeBaseInterface; + using rclcpp::node_interfaces::NodeClockInterface; + using rclcpp::node_interfaces::NodeGraphInterface; + using rclcpp::node_interfaces::NodeLoggingInterface; + using rclcpp::node_interfaces::NodeTimersInterface; + using rclcpp::node_interfaces::NodeTopicsInterface; + using rclcpp::node_interfaces::NodeServicesInterface; + using rclcpp::node_interfaces::NodeWaitablesInterface; + using rclcpp::node_interfaces::NodeParametersInterface; + using rclcpp::node_interfaces::NodeTimeSourceInterface; + + const auto ni = rclcpp::node_interfaces::NodeInterfaces(*node); + + { + auto base = ni.get(); + base = ni.get_node_base_interface(); + EXPECT_STREQ(base->get_name(), "my_node"); // Test for functionality + } + { + auto clock = ni.get(); + clock = ni.get_node_clock_interface(); + clock->get_clock(); + } + { + auto graph = ni.get(); + graph = ni.get_node_graph_interface(); + } + { + auto logging = ni.get(); + logging = ni.get_node_logging_interface(); + } + { + auto timers = ni.get(); + timers = ni.get_node_timers_interface(); + } + { + auto topics = ni.get(); + topics = ni.get_node_topics_interface(); + } + { + auto services = ni.get(); + services = ni.get_node_services_interface(); + } + { + auto waitables = ni.get(); + waitables = ni.get_node_waitables_interface(); + } + { + auto parameters = ni.get(); + parameters = ni.get_node_parameters_interface(); + } + { + auto time_source = ni.get(); + time_source = ni.get_node_time_source_interface(); + } +} From bc14074e3b0c378b4f8ef93455c7a6ba2b6d79be Mon Sep 17 00:00:00 2001 From: Alejandro Hernandez Cordero Date: Tue, 23 Dec 2025 11:55:06 +0100 Subject: [PATCH 84/98] Changelog Signed-off-by: Alejandro Hernandez Cordero --- rclcpp/CHANGELOG.rst | 8 ++++++++ rclcpp_action/CHANGELOG.rst | 3 +++ rclcpp_components/CHANGELOG.rst | 3 +++ rclcpp_lifecycle/CHANGELOG.rst | 5 +++++ 4 files changed, 19 insertions(+) diff --git a/rclcpp/CHANGELOG.rst b/rclcpp/CHANGELOG.rst index a58d1e657e..3c2c2f95f7 100644 --- a/rclcpp/CHANGELOG.rst +++ b/rclcpp/CHANGELOG.rst @@ -2,6 +2,14 @@ Changelog for package rclcpp ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +16.0.17 (2025-12-23) +-------------------- +* Unified Node Interfaces: Add const version of get_node_x_interface() (`#3006 `_) (`#3010 `_) +* [Humble] Implement Unified Node Interface (NodeInterfaces class) (backport `#2041 `_) (`#3002 `_) +* remove I/O from signal handler. (`#3000 `_) (`#3005 `_) +* correct test function descriptions (backport `#2970 `_) (`#2994 `_) +* Contributors: fabianhirmann, mergify[bot] + 16.0.16 (2025-11-18) -------------------- * Fix REP url locations (backport `#2987 `_) (`#2991 `_) diff --git a/rclcpp_action/CHANGELOG.rst b/rclcpp_action/CHANGELOG.rst index bcc91ad6f2..724778ad73 100644 --- a/rclcpp_action/CHANGELOG.rst +++ b/rclcpp_action/CHANGELOG.rst @@ -3,6 +3,9 @@ Changelog for package rclcpp_action ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +16.0.17 (2025-12-23) +-------------------- + 16.0.16 (2025-11-18) -------------------- * Fix REP url locations (backport `#2987 `_) (`#2991 `_) diff --git a/rclcpp_components/CHANGELOG.rst b/rclcpp_components/CHANGELOG.rst index 10a63a5ea7..e9ff04830d 100644 --- a/rclcpp_components/CHANGELOG.rst +++ b/rclcpp_components/CHANGELOG.rst @@ -2,6 +2,9 @@ Changelog for package rclcpp_components ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +16.0.17 (2025-12-23) +-------------------- + 16.0.16 (2025-11-18) -------------------- * Fix REP url locations (backport `#2987 `_) (`#2991 `_) diff --git a/rclcpp_lifecycle/CHANGELOG.rst b/rclcpp_lifecycle/CHANGELOG.rst index 4347fc0b48..59deb32903 100644 --- a/rclcpp_lifecycle/CHANGELOG.rst +++ b/rclcpp_lifecycle/CHANGELOG.rst @@ -3,6 +3,11 @@ Changelog for package rclcpp_lifecycle ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +16.0.17 (2025-12-23) +-------------------- +* [Humble] Implement Unified Node Interface (NodeInterfaces class) (backport `#2041 `_) (`#3002 `_) +* Contributors: fabianhirmann + 16.0.16 (2025-11-18) -------------------- * Fix REP url locations (backport `#2987 `_) (`#2991 `_) From 41182a972055bdcfd9c19888c2f302ef8882b0ca Mon Sep 17 00:00:00 2001 From: Alejandro Hernandez Cordero Date: Tue, 23 Dec 2025 11:55:13 +0100 Subject: [PATCH 85/98] 16.0.17 --- rclcpp/package.xml | 2 +- rclcpp_action/package.xml | 2 +- rclcpp_components/package.xml | 2 +- rclcpp_lifecycle/package.xml | 2 +- 4 files changed, 4 insertions(+), 4 deletions(-) diff --git a/rclcpp/package.xml b/rclcpp/package.xml index 7ec1f60d1b..8eac113678 100644 --- a/rclcpp/package.xml +++ b/rclcpp/package.xml @@ -2,7 +2,7 @@ rclcpp - 16.0.16 + 16.0.17 The ROS client library in C++. Ivan Paunovic Jacob Perron diff --git a/rclcpp_action/package.xml b/rclcpp_action/package.xml index 7b06189ab4..13f3acd186 100644 --- a/rclcpp_action/package.xml +++ b/rclcpp_action/package.xml @@ -2,7 +2,7 @@ rclcpp_action - 16.0.16 + 16.0.17 Adds action APIs for C++. Ivan Paunovic Jacob Perron diff --git a/rclcpp_components/package.xml b/rclcpp_components/package.xml index 17a5910bad..ef0b9707f3 100644 --- a/rclcpp_components/package.xml +++ b/rclcpp_components/package.xml @@ -2,7 +2,7 @@ rclcpp_components - 16.0.16 + 16.0.17 Package containing tools for dynamically loadable components Ivan Paunovic Jacob Perron diff --git a/rclcpp_lifecycle/package.xml b/rclcpp_lifecycle/package.xml index de64ad33c4..83c863f33d 100644 --- a/rclcpp_lifecycle/package.xml +++ b/rclcpp_lifecycle/package.xml @@ -2,7 +2,7 @@ rclcpp_lifecycle - 16.0.16 + 16.0.17 Package containing a prototype for lifecycle implementation Ivan Paunovic Jacob Perron From 66dbc789bc7a5abe40c7d4815379a638bcd81471 Mon Sep 17 00:00:00 2001 From: "mergify[bot]" <37929162+mergify[bot]@users.noreply.github.com> Date: Tue, 13 Jan 2026 12:34:19 +0100 Subject: [PATCH 86/98] Improve the robustness of the TopicEndpointInfo constructor (backport #3013) (#3016) Signed-off-by: Alejandro Hernandez Cordero Co-authored-by: Barry Xu Co-authored-by: Alejandro Hernandez Cordero --- .../rclcpp/node_interfaces/node_graph_interface.hpp | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/rclcpp/include/rclcpp/node_interfaces/node_graph_interface.hpp b/rclcpp/include/rclcpp/node_interfaces/node_graph_interface.hpp index fefda0da69..1eba5779d7 100644 --- a/rclcpp/include/rclcpp/node_interfaces/node_graph_interface.hpp +++ b/rclcpp/include/rclcpp/node_interfaces/node_graph_interface.hpp @@ -53,12 +53,16 @@ class TopicEndpointInfo /// Construct a TopicEndpointInfo from a rcl_topic_endpoint_info_t. RCLCPP_PUBLIC explicit TopicEndpointInfo(const rcl_topic_endpoint_info_t & info) - : node_name_(info.node_name), - node_namespace_(info.node_namespace), - topic_type_(info.topic_type), - endpoint_type_(static_cast(info.endpoint_type)), + : endpoint_type_(static_cast(info.endpoint_type)), qos_profile_({info.qos_profile.history, info.qos_profile.depth}, info.qos_profile) { + if (!info.node_name || !info.node_namespace || !info.topic_type) { + throw std::invalid_argument("Constructor TopicEndpointInfo with invalid topic endpoint info"); + } + node_name_ = info.node_name; + node_namespace_ = info.node_namespace; + topic_type_ = info.topic_type; + std::copy(info.endpoint_gid, info.endpoint_gid + RMW_GID_STORAGE_SIZE, endpoint_gid_.begin()); } From 6040d745e73c208890e97ddc7e11f3b620a957a3 Mon Sep 17 00:00:00 2001 From: "mergify[bot]" <37929162+mergify[bot]@users.noreply.github.com> Date: Wed, 21 Jan 2026 10:22:30 +0100 Subject: [PATCH 87/98] Update exception documentation for goal cancellation in ServerGoalHandle (#3019) (#3024) * Update exception documentation for goal cancellation The documentation for the canceled function is misleading. Previously, the description said: 1. "Only call this if the goal is canceling." and 2. "\throws rclcpp::exceptions::RCLError If the goal is in any state besides executing." This is a contradiction. Experimentally verified that if the goal is executing and this method is called, an error is thrown. This makes the second statement wrong => correct the statement in the documentation. (cherry picked from commit 6397047d4795f594cf65dd360d70f5c9c3618700) Signed-off-by: Andrei Costinescu Co-authored-by: Andrei Costinescu --- rclcpp_action/include/rclcpp_action/server_goal_handle.hpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/rclcpp_action/include/rclcpp_action/server_goal_handle.hpp b/rclcpp_action/include/rclcpp_action/server_goal_handle.hpp index ac9dd49492..1b97dcf371 100644 --- a/rclcpp_action/include/rclcpp_action/server_goal_handle.hpp +++ b/rclcpp_action/include/rclcpp_action/server_goal_handle.hpp @@ -200,7 +200,7 @@ class ServerGoalHandle : public ServerGoalHandleBase * This is a terminal state, no more methods should be called on a goal handle after this is * called. * - * \throws rclcpp::exceptions::RCLError If the goal is in any state besides executing. + * \throws rclcpp::exceptions::RCLError If a cancel request for this goal has not been received. * * \param[in] result_msg the final result to send to clients. */ From 78bc4734df7e0b92598648a7fd356202eb47fcd0 Mon Sep 17 00:00:00 2001 From: "mergify[bot]" <37929162+mergify[bot]@users.noreply.github.com> Date: Fri, 23 Jan 2026 09:44:31 +0100 Subject: [PATCH 88/98] fix context in wait for message wait set (#3030) (#3033) (cherry picked from commit fcc505f4532346434f1dca8304285043d071ff90) Signed-off-by: Rahat Dhande Co-authored-by: Rahat Dhande --- rclcpp/include/rclcpp/wait_for_message.hpp | 2 +- rclcpp/test/rclcpp/test_wait_for_message.cpp | 29 ++++++++++++++++++++ 2 files changed, 30 insertions(+), 1 deletion(-) diff --git a/rclcpp/include/rclcpp/wait_for_message.hpp b/rclcpp/include/rclcpp/wait_for_message.hpp index fab2e6ccfc..58665ec921 100644 --- a/rclcpp/include/rclcpp/wait_for_message.hpp +++ b/rclcpp/include/rclcpp/wait_for_message.hpp @@ -56,7 +56,7 @@ bool wait_for_message( } }); - rclcpp::WaitSet wait_set; + rclcpp::WaitSet wait_set({}, {}, {}, {}, {}, {}, context); wait_set.add_subscription(subscription); RCPPUTILS_SCOPE_EXIT(wait_set.remove_subscription(subscription); ); wait_set.add_guard_condition(gc); diff --git a/rclcpp/test/rclcpp/test_wait_for_message.cpp b/rclcpp/test/rclcpp/test_wait_for_message.cpp index 9f49fb141c..81179ca6fc 100644 --- a/rclcpp/test/rclcpp/test_wait_for_message.cpp +++ b/rclcpp/test/rclcpp/test_wait_for_message.cpp @@ -134,3 +134,32 @@ TEST(TestUtilities, wait_for_last_message) { rclcpp::shutdown(); } + +TEST(TestUtilities, wait_for_message_custom_context) { + auto context = std::make_shared(); + context->init(0, nullptr); + + auto node_opt = rclcpp::NodeOptions().context(context); + auto node = std::make_shared("wait_for_message_custom_context_node", node_opt); + + using MsgT = test_msgs::msg::Strings; + auto pub = node->create_publisher("wait_for_message_topic", 10); + + MsgT out; + auto received = false; + auto wait = std::async( + [&]() { + auto ret = rclcpp::wait_for_message(out, node, "wait_for_message_topic", 5s); + EXPECT_TRUE(ret); + received = true; + }); + + for (auto i = 0u; i < 10 && received == false; ++i) { + pub->publish(*get_messages_strings()[0]); + std::this_thread::sleep_for(1s); + } + ASSERT_TRUE(received); + EXPECT_EQ(out, *get_messages_strings()[0]); + + context->shutdown("test complete"); +} From fea542a1313a5ea9dbcae0fa601c024d2742dec6 Mon Sep 17 00:00:00 2001 From: "mergify[bot]" <37929162+mergify[bot]@users.noreply.github.com> Date: Fri, 30 Jan 2026 15:14:14 +0100 Subject: [PATCH 89/98] print warning message on owner node if the parameter operation fails. (backport #3037) (#3040) * print warning message on owner node if the parameter operation fails. (#3037) Signed-off-by: Tomoya Fujita (cherry picked from commit f8a7ace7a852625fb47169756ec890c4521b3a65) # Conflicts: # rclcpp/src/rclcpp/parameter_service.cpp * resolve conflict. Signed-off-by: Tomoya.Fujita --------- Signed-off-by: Tomoya.Fujita Co-authored-by: Tomoya Fujita --- rclcpp/src/rclcpp/parameter_service.cpp | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/rclcpp/src/rclcpp/parameter_service.cpp b/rclcpp/src/rclcpp/parameter_service.cpp index 5c30917499..ab2aa882d1 100644 --- a/rclcpp/src/rclcpp/parameter_service.cpp +++ b/rclcpp/src/rclcpp/parameter_service.cpp @@ -47,7 +47,7 @@ ParameterService::ParameterService( response->values.push_back(param.get_value_message()); } } catch (const rclcpp::exceptions::ParameterNotDeclaredException & ex) { - RCLCPP_DEBUG(rclcpp::get_logger("rclcpp"), "Failed to get parameters: %s", ex.what()); + RCLCPP_WARN(rclcpp::get_logger("rclcpp"), "Failed to get parameters: %s", ex.what()); } }, qos_profile, nullptr); @@ -68,7 +68,7 @@ ParameterService::ParameterService( return static_cast(type); }); } catch (const rclcpp::exceptions::ParameterNotDeclaredException & ex) { - RCLCPP_DEBUG(rclcpp::get_logger("rclcpp"), "Failed to get parameter types: %s", ex.what()); + RCLCPP_WARN(rclcpp::get_logger("rclcpp"), "Failed to get parameter types: %s", ex.what()); } }, qos_profile, nullptr); @@ -89,7 +89,7 @@ ParameterService::ParameterService( result = node_params->set_parameters_atomically( {rclcpp::Parameter::from_parameter_msg(p)}); } catch (const rclcpp::exceptions::ParameterNotDeclaredException & ex) { - RCLCPP_DEBUG(rclcpp::get_logger("rclcpp"), "Failed to set parameter: %s", ex.what()); + RCLCPP_WARN(rclcpp::get_logger("rclcpp"), "Failed to set parameter: %s", ex.what()); result.successful = false; result.reason = ex.what(); } @@ -117,7 +117,7 @@ ParameterService::ParameterService( auto result = node_params->set_parameters_atomically(pvariants); response->result = result; } catch (const rclcpp::exceptions::ParameterNotDeclaredException & ex) { - RCLCPP_DEBUG( + RCLCPP_WARN( rclcpp::get_logger("rclcpp"), "Failed to set parameters atomically: %s", ex.what()); response->result.successful = false; response->result.reason = "One or more parameters were not declared before setting"; @@ -137,7 +137,7 @@ ParameterService::ParameterService( auto descriptors = node_params->describe_parameters(request->names); response->descriptors = descriptors; } catch (const rclcpp::exceptions::ParameterNotDeclaredException & ex) { - RCLCPP_DEBUG(rclcpp::get_logger("rclcpp"), "Failed to describe parameters: %s", ex.what()); + RCLCPP_WARN(rclcpp::get_logger("rclcpp"), "Failed to describe parameters: %s", ex.what()); } }, qos_profile, nullptr); From a490ca14189b909d9081df944eac844a3984ec65 Mon Sep 17 00:00:00 2001 From: Alejandro Hernandez Cordero Date: Mon, 9 Feb 2026 15:02:35 +0100 Subject: [PATCH 90/98] Changelog Signed-off-by: Alejandro Hernandez Cordero --- rclcpp/CHANGELOG.rst | 7 +++++++ rclcpp_action/CHANGELOG.rst | 5 +++++ rclcpp_components/CHANGELOG.rst | 3 +++ rclcpp_lifecycle/CHANGELOG.rst | 3 +++ 4 files changed, 18 insertions(+) diff --git a/rclcpp/CHANGELOG.rst b/rclcpp/CHANGELOG.rst index 3c2c2f95f7..48a2a4ae4b 100644 --- a/rclcpp/CHANGELOG.rst +++ b/rclcpp/CHANGELOG.rst @@ -2,6 +2,13 @@ Changelog for package rclcpp ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +16.0.18 (2026-02-09) +-------------------- +* print warning message on owner node if the parameter operation fails. (backport `#3037 `_) (`#3040 `_) +* fix context in wait for message wait set (`#3030 `_) (`#3033 `_) +* Improve the robustness of the TopicEndpointInfo constructor (backport `#3013 `_) (`#3016 `_) +* Contributors: mergify[bot] + 16.0.17 (2025-12-23) -------------------- * Unified Node Interfaces: Add const version of get_node_x_interface() (`#3006 `_) (`#3010 `_) diff --git a/rclcpp_action/CHANGELOG.rst b/rclcpp_action/CHANGELOG.rst index 724778ad73..cdf7d5b0d9 100644 --- a/rclcpp_action/CHANGELOG.rst +++ b/rclcpp_action/CHANGELOG.rst @@ -3,6 +3,11 @@ Changelog for package rclcpp_action ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +16.0.18 (2026-02-09) +-------------------- +* Update exception documentation for goal cancellation in ServerGoalHandle (`#3019 `_) (`#3024 `_) +* Contributors: mergify[bot] + 16.0.17 (2025-12-23) -------------------- diff --git a/rclcpp_components/CHANGELOG.rst b/rclcpp_components/CHANGELOG.rst index e9ff04830d..b630d525ae 100644 --- a/rclcpp_components/CHANGELOG.rst +++ b/rclcpp_components/CHANGELOG.rst @@ -2,6 +2,9 @@ Changelog for package rclcpp_components ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +16.0.18 (2026-02-09) +-------------------- + 16.0.17 (2025-12-23) -------------------- diff --git a/rclcpp_lifecycle/CHANGELOG.rst b/rclcpp_lifecycle/CHANGELOG.rst index 59deb32903..e3a4f7d5c8 100644 --- a/rclcpp_lifecycle/CHANGELOG.rst +++ b/rclcpp_lifecycle/CHANGELOG.rst @@ -3,6 +3,9 @@ Changelog for package rclcpp_lifecycle ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +16.0.18 (2026-02-09) +-------------------- + 16.0.17 (2025-12-23) -------------------- * [Humble] Implement Unified Node Interface (NodeInterfaces class) (backport `#2041 `_) (`#3002 `_) From 035777622b495bf48ebc48d797ca885ddd94e8df Mon Sep 17 00:00:00 2001 From: Alejandro Hernandez Cordero Date: Mon, 9 Feb 2026 15:02:40 +0100 Subject: [PATCH 91/98] 16.0.18 --- rclcpp/package.xml | 2 +- rclcpp_action/package.xml | 2 +- rclcpp_components/package.xml | 2 +- rclcpp_lifecycle/package.xml | 2 +- 4 files changed, 4 insertions(+), 4 deletions(-) diff --git a/rclcpp/package.xml b/rclcpp/package.xml index 8eac113678..edcf7cf0c6 100644 --- a/rclcpp/package.xml +++ b/rclcpp/package.xml @@ -2,7 +2,7 @@ rclcpp - 16.0.17 + 16.0.18 The ROS client library in C++. Ivan Paunovic Jacob Perron diff --git a/rclcpp_action/package.xml b/rclcpp_action/package.xml index 13f3acd186..5e9bdbc88f 100644 --- a/rclcpp_action/package.xml +++ b/rclcpp_action/package.xml @@ -2,7 +2,7 @@ rclcpp_action - 16.0.17 + 16.0.18 Adds action APIs for C++. Ivan Paunovic Jacob Perron diff --git a/rclcpp_components/package.xml b/rclcpp_components/package.xml index ef0b9707f3..434b675904 100644 --- a/rclcpp_components/package.xml +++ b/rclcpp_components/package.xml @@ -2,7 +2,7 @@ rclcpp_components - 16.0.17 + 16.0.18 Package containing tools for dynamically loadable components Ivan Paunovic Jacob Perron diff --git a/rclcpp_lifecycle/package.xml b/rclcpp_lifecycle/package.xml index 83c863f33d..fed48db15e 100644 --- a/rclcpp_lifecycle/package.xml +++ b/rclcpp_lifecycle/package.xml @@ -2,7 +2,7 @@ rclcpp_lifecycle - 16.0.17 + 16.0.18 Package containing a prototype for lifecycle implementation Ivan Paunovic Jacob Perron From 1f66148892665335be94ce7acb4a9d78f017f9ec Mon Sep 17 00:00:00 2001 From: "mergify[bot]" <37929162+mergify[bot]@users.noreply.github.com> Date: Fri, 13 Feb 2026 09:48:27 +0100 Subject: [PATCH 92/98] fix: Various data races in test cases (#3057) (#3063) (cherry picked from commit 6ff4d83498e8e3a2db6b9b4c38de098f40ab3ee2) Signed-off-by: Janosch Machowinski Co-authored-by: Janosch Machowinski Co-authored-by: Janosch Machowinski --- rclcpp/test/rclcpp/executors/test_executors.cpp | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/rclcpp/test/rclcpp/executors/test_executors.cpp b/rclcpp/test/rclcpp/executors/test_executors.cpp index 7bdd539614..6b925f86f6 100644 --- a/rclcpp/test/rclcpp/executors/test_executors.cpp +++ b/rclcpp/test/rclcpp/executors/test_executors.cpp @@ -81,7 +81,7 @@ class TestExecutors : public ::testing::Test rclcpp::Node::SharedPtr node; rclcpp::Publisher::SharedPtr publisher; rclcpp::Subscription::SharedPtr subscription; - int callback_count; + std::atomic callback_count; }; // spin_all and spin_some are not implemented correctly in StaticSingleThreadedExecutor, see: @@ -179,7 +179,7 @@ TYPED_TEST(TestExecutors, spinWithTimer) { using ExecutorType = TypeParam; ExecutorType executor; - bool timer_completed = false; + std::atomic timer_completed = false; auto timer = this->node->create_wall_timer(1ms, [&]() {timer_completed = true;}); executor.add_node(this->node); @@ -283,7 +283,7 @@ TYPED_TEST(TestExecutors, testSpinUntilFutureCompleteNoTimeout) { } }); - bool spin_exited = false; + std::atomic spin_exited = false; // Timeout set to negative for no timeout. std::thread spinner([&]() { @@ -319,7 +319,7 @@ TYPED_TEST(TestExecutors, testSpinUntilFutureCompleteWithTimeout) { ExecutorType executor; executor.add_node(this->node); - bool spin_exited = false; + std::atomic spin_exited = false; // Needs to run longer than spin_until_future_complete's timeout. std::future future = std::async( @@ -413,7 +413,7 @@ TYPED_TEST(TestExecutors, spinAll) { // Long timeout, but should not block test if spin_all works as expected as we cancel the // executor. - bool spin_exited = false; + std::atomic spin_exited = false; std::thread spinner([&spin_exited, &executor, this]() { executor.spin_all(1s); executor.remove_node(this->node, true); @@ -519,7 +519,7 @@ TYPED_TEST(TestExecutors, testSpinUntilFutureCompleteInterrupted) { ExecutorType executor; executor.add_node(this->node); - bool spin_exited = false; + std::atomic spin_exited = false; // This needs to block longer than it takes to get to the shutdown call below and for // spin_until_future_complete to return From b21707f2c940543aaecc9ea5323152667d078cf5 Mon Sep 17 00:00:00 2001 From: "mergify[bot]" <37929162+mergify[bot]@users.noreply.github.com> Date: Thu, 19 Feb 2026 10:01:41 +0100 Subject: [PATCH 93/98] fix: Use default rcl allocator if allocator is std::allocator (#3069) (#3072) This fixes a bunch of warnings if using ASAN / valgrind on newer OS versions. It also fixed a real bug, as giving the wrong size on deallocate is undefined behavior according to the C++ standard. This version of the patch keeps the behavior for users that specified an own allocator the same and in therefore back portable. (cherry picked from commit dc4a1dbbca9e907301d4529aa0184877b7058295) Signed-off-by: Janosch Machowinski Co-authored-by: Janosch Machowinski Co-authored-by: Janosch Machowinski --- rclcpp/include/rclcpp/message_memory_strategy.hpp | 15 +++++++++++++-- rclcpp/include/rclcpp/publisher_options.hpp | 4 ++++ .../strategies/allocator_memory_strategy.hpp | 6 +++++- rclcpp/include/rclcpp/subscription_options.hpp | 12 ++++++++---- 4 files changed, 30 insertions(+), 7 deletions(-) diff --git a/rclcpp/include/rclcpp/message_memory_strategy.hpp b/rclcpp/include/rclcpp/message_memory_strategy.hpp index f548d953c2..a51002065f 100644 --- a/rclcpp/include/rclcpp/message_memory_strategy.hpp +++ b/rclcpp/include/rclcpp/message_memory_strategy.hpp @@ -18,6 +18,7 @@ #include #include +#include "rcl/allocator.h" #include "rcl/types.h" #include "rclcpp/allocator/allocator_common.hpp" @@ -61,7 +62,12 @@ class MessageMemoryStrategy message_allocator_ = std::make_shared(); serialized_message_allocator_ = std::make_shared(); buffer_allocator_ = std::make_shared(); - rcutils_allocator_ = allocator::get_rcl_allocator(*buffer_allocator_.get()); + if constexpr (std::is_same_v>) { + rcutils_allocator_ = rcl_get_default_allocator(); + } else { + rcutils_allocator_ = allocator::get_rcl_allocator(*buffer_allocator_.get()); + } } explicit MessageMemoryStrategy(std::shared_ptr allocator) @@ -69,7 +75,12 @@ class MessageMemoryStrategy message_allocator_ = std::make_shared(*allocator.get()); serialized_message_allocator_ = std::make_shared(*allocator.get()); buffer_allocator_ = std::make_shared(*allocator.get()); - rcutils_allocator_ = allocator::get_rcl_allocator(*buffer_allocator_.get()); + if constexpr (std::is_same_v>) { + rcutils_allocator_ = rcl_get_default_allocator(); + } else { + rcutils_allocator_ = allocator::get_rcl_allocator(*buffer_allocator_.get()); + } } virtual ~MessageMemoryStrategy() = default; diff --git a/rclcpp/include/rclcpp/publisher_options.hpp b/rclcpp/include/rclcpp/publisher_options.hpp index 3c88ebccd1..0f198a8874 100644 --- a/rclcpp/include/rclcpp/publisher_options.hpp +++ b/rclcpp/include/rclcpp/publisher_options.hpp @@ -119,6 +119,10 @@ struct PublisherOptionsWithAllocator : public PublisherOptionsBase rcl_allocator_t get_rcl_allocator() const { + if constexpr (std::is_same_v>) { + return rcl_get_default_allocator(); + } + if (!plain_allocator_storage_) { plain_allocator_storage_ = std::make_shared(*this->get_allocator()); diff --git a/rclcpp/include/rclcpp/strategies/allocator_memory_strategy.hpp b/rclcpp/include/rclcpp/strategies/allocator_memory_strategy.hpp index 88698179d4..6d30d1e804 100644 --- a/rclcpp/include/rclcpp/strategies/allocator_memory_strategy.hpp +++ b/rclcpp/include/rclcpp/strategies/allocator_memory_strategy.hpp @@ -424,7 +424,11 @@ class AllocatorMemoryStrategy : public memory_strategy::MemoryStrategy rcl_allocator_t get_allocator() override { - return rclcpp::allocator::get_rcl_allocator(*allocator_.get()); + if constexpr (std::is_same_v>) { + return rcl_get_default_allocator(); + } else { + return rclcpp::allocator::get_rcl_allocator(*allocator_.get()); + } } size_t number_of_ready_subscriptions() const override diff --git a/rclcpp/include/rclcpp/subscription_options.hpp b/rclcpp/include/rclcpp/subscription_options.hpp index 2b819da399..35d3d3c279 100644 --- a/rclcpp/include/rclcpp/subscription_options.hpp +++ b/rclcpp/include/rclcpp/subscription_options.hpp @@ -163,11 +163,15 @@ struct SubscriptionOptionsWithAllocator : public SubscriptionOptionsBase rcl_allocator_t get_rcl_allocator() const { - if (!plain_allocator_storage_) { - plain_allocator_storage_ = - std::make_shared(*this->get_allocator()); + if constexpr (std::is_same_v>) { + return rcl_get_default_allocator(); + } else { + if (!plain_allocator_storage_) { + plain_allocator_storage_ = + std::make_shared(*this->get_allocator()); + } + return rclcpp::allocator::get_rcl_allocator(*plain_allocator_storage_); } - return rclcpp::allocator::get_rcl_allocator(*plain_allocator_storage_); } // This is a temporal workaround, to make sure that get_allocator() From 217e7a705cf53d73cb241935c79238e8fadd3d8b Mon Sep 17 00:00:00 2001 From: "mergify[bot]" <37929162+mergify[bot]@users.noreply.github.com> Date: Mon, 23 Feb 2026 10:19:31 +0100 Subject: [PATCH 94/98] Fix component registering in subdirectories (#3064) (#3076) (cherry picked from commit fc1afcb3bc2c4f9c7113d8ae324f1cbda5f610cd) Signed-off-by: pum1k <55055380+pum1k@users.noreply.github.com> Co-authored-by: pum1k <55055380+pum1k@users.noreply.github.com> --- .../cmake/rclcpp_components_package_hook.cmake | 17 ++++++++++++++--- .../cmake/rclcpp_components_register_node.cmake | 12 ++++++++---- .../rclcpp_components_register_nodes.cmake | 12 ++++++++---- .../rclcpp_components-extras.cmake.in | 2 ++ 4 files changed, 32 insertions(+), 11 deletions(-) diff --git a/rclcpp_components/cmake/rclcpp_components_package_hook.cmake b/rclcpp_components/cmake/rclcpp_components_package_hook.cmake index 268bc5bedb..10a039ee44 100644 --- a/rclcpp_components/cmake/rclcpp_components_package_hook.cmake +++ b/rclcpp_components/cmake/rclcpp_components_package_hook.cmake @@ -13,9 +13,20 @@ # limitations under the License. # register node plugins -list(REMOVE_DUPLICATES _RCLCPP_COMPONENTS_PACKAGE_RESOURCE_INDICES) -foreach(resource_index ${_RCLCPP_COMPONENTS_PACKAGE_RESOURCE_INDICES}) +# The internal data is stored in a project directory scoped properties to allow +# registering the components from nested scopes in CMake, where variables +# would not propagate out. +get_property(_rclcpp_components_package_resource_indices + DIRECTORY "${PROJECT_SOURCE_DIR}" + PROPERTY _RCLCPP_COMPONENTS_PACKAGE_RESOURCE_INDICES +) +list(REMOVE_DUPLICATES _rclcpp_components_package_resource_indices) +foreach(resource_index ${_rclcpp_components_package_resource_indices}) + get_property(_rclcpp_components_nodes + DIRECTORY "${PROJECT_SOURCE_DIR}" + PROPERTY "_RCLCPP_COMPONENTS_${resource_index}__NODES" + ) ament_index_register_resource( - ${resource_index} CONTENT "${_RCLCPP_COMPONENTS_${resource_index}__NODES}") + ${resource_index} CONTENT "${_rclcpp_components_nodes}") endforeach() diff --git a/rclcpp_components/cmake/rclcpp_components_register_node.cmake b/rclcpp_components/cmake/rclcpp_components_register_node.cmake index c5b87af667..7c1e2142e2 100644 --- a/rclcpp_components/cmake/rclcpp_components_register_node.cmake +++ b/rclcpp_components/cmake/rclcpp_components_register_node.cmake @@ -55,15 +55,19 @@ macro(rclcpp_components_register_node target) set(component ${ARGS_PLUGIN}) set(node ${ARGS_EXECUTABLE}) - _rclcpp_components_register_package_hook() set(_path "lib") set(library_name "$") if(WIN32) set(_path "bin") endif() - set(_RCLCPP_COMPONENTS_${resource_index}__NODES - "${_RCLCPP_COMPONENTS_${resource_index}__NODES}${component};${_path}/$\n") - list(APPEND _RCLCPP_COMPONENTS_PACKAGE_RESOURCE_INDICES ${resource_index}) + set_property( + DIRECTORY "${PROJECT_SOURCE_DIR}" + APPEND_STRING PROPERTY _RCLCPP_COMPONENTS_${resource_index}__NODES + "${component};${_path}/$\n") + set_property( + DIRECTORY "${PROJECT_SOURCE_DIR}" + APPEND PROPERTY _RCLCPP_COMPONENTS_PACKAGE_RESOURCE_INDICES + ${resource_index}) configure_file(${rclcpp_components_NODE_TEMPLATE} ${PROJECT_BINARY_DIR}/rclcpp_components/node_main_configured_${node}.cpp.in) diff --git a/rclcpp_components/cmake/rclcpp_components_register_nodes.cmake b/rclcpp_components/cmake/rclcpp_components_register_nodes.cmake index e80550a3ce..c082bc56a4 100644 --- a/rclcpp_components/cmake/rclcpp_components_register_nodes.cmake +++ b/rclcpp_components/cmake/rclcpp_components_register_nodes.cmake @@ -47,7 +47,6 @@ macro(rclcpp_components_register_nodes target) endif() if(${ARGC} GREATER 0) - _rclcpp_components_register_package_hook() set(_unique_names) foreach(_arg ${ARGS_UNPARSED_ARGUMENTS}) if(_arg IN_LIST _unique_names) @@ -63,9 +62,14 @@ macro(rclcpp_components_register_nodes target) else() set(_path "lib") endif() - set(_RCLCPP_COMPONENTS_${resource_index}__NODES - "${_RCLCPP_COMPONENTS_${resource_index}__NODES}${_arg};${_path}/$\n") - list(APPEND _RCLCPP_COMPONENTS_PACKAGE_RESOURCE_INDICES ${resource_index}) + set_property( + DIRECTORY "${PROJECT_SOURCE_DIR}" + APPEND_STRING PROPERTY _RCLCPP_COMPONENTS_${resource_index}__NODES + "${_arg};${_path}/$\n") + set_property( + DIRECTORY "${PROJECT_SOURCE_DIR}" + APPEND PROPERTY _RCLCPP_COMPONENTS_PACKAGE_RESOURCE_INDICES + ${resource_index}) endforeach() endif() endmacro() diff --git a/rclcpp_components/rclcpp_components-extras.cmake.in b/rclcpp_components/rclcpp_components-extras.cmake.in index 45a4e5ac0d..95feb96f04 100644 --- a/rclcpp_components/rclcpp_components-extras.cmake.in +++ b/rclcpp_components/rclcpp_components-extras.cmake.in @@ -25,6 +25,8 @@ macro(_rclcpp_components_register_package_hook) endif() endmacro() +_rclcpp_components_register_package_hook() + get_filename_component(@PROJECT_NAME@_SHARE_DIR "${@PROJECT_NAME@_DIR}" DIRECTORY) set(@PROJECT_NAME@_NODE_TEMPLATE "${@PROJECT_NAME@_SHARE_DIR}/node_main.cpp.in") From 3a053fb46c3c41326f204ed3a2396c84f712f2db Mon Sep 17 00:00:00 2001 From: "mergify[bot]" <37929162+mergify[bot]@users.noreply.github.com> Date: Fri, 13 Mar 2026 12:58:13 +0100 Subject: [PATCH 95/98] Avoid unecessary creation of MultiThreadedExecutor (#3090) (#3096) (cherry picked from commit 8cd4d47ec5c6bfba39002b05a57d026a5455ebd9) Signed-off-by: solonovamax Co-authored-by: solo --- rclcpp_components/src/component_container_mt.cpp | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/rclcpp_components/src/component_container_mt.cpp b/rclcpp_components/src/component_container_mt.cpp index 9dcbade712..8e2a113bec 100644 --- a/rclcpp_components/src/component_container_mt.cpp +++ b/rclcpp_components/src/component_container_mt.cpp @@ -23,16 +23,16 @@ int main(int argc, char * argv[]) /// Component container with a multi-threaded executor. rclcpp::init(argc, argv); - auto exec = std::make_shared(); - auto node = std::make_shared(); + rclcpp::executors::MultiThreadedExecutor::SharedPtr exec = nullptr; + const auto node = std::make_shared(); if (node->has_parameter("thread_num")) { const auto thread_num = node->get_parameter("thread_num").as_int(); exec = std::make_shared( rclcpp::ExecutorOptions{}, thread_num); - node->set_executor(exec); } else { - node->set_executor(exec); + exec = std::make_shared(); } + node->set_executor(exec); exec->add_node(node); exec->spin(); } From dca23fe1117600e114f35cbc20cece5c84162cb9 Mon Sep 17 00:00:00 2001 From: "mergify[bot]" <37929162+mergify[bot]@users.noreply.github.com> Date: Wed, 18 Mar 2026 08:30:29 +0900 Subject: [PATCH 96/98] keep the event alive throught the assertion, preveiting the race. (#3099) (#3101) (cherry picked from commit aadcb3b285b49b986a068725e781395eb2f833c9) Signed-off-by: Tomoya Fujita Co-authored-by: Tomoya Fujita --- rclcpp/test/rclcpp/node_interfaces/test_node_graph.cpp | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/rclcpp/test/rclcpp/node_interfaces/test_node_graph.cpp b/rclcpp/test/rclcpp/node_interfaces/test_node_graph.cpp index c25af15422..e1a0b1e1e7 100644 --- a/rclcpp/test/rclcpp/node_interfaces/test_node_graph.cpp +++ b/rclcpp/test/rclcpp/node_interfaces/test_node_graph.cpp @@ -131,7 +131,8 @@ TEST_F(TestNodeGraph, construct_from_node) EXPECT_NE(nullptr, node_graph()->get_graph_guard_condition()); // get_graph_event is non-const - EXPECT_NE(nullptr, node()->get_node_graph_interface()->get_graph_event()); + auto event = node()->get_node_graph_interface()->get_graph_event(); + EXPECT_NE(nullptr, event); EXPECT_LE(1u, node_graph()->count_graph_users()); } From 97c89afbdf53cd56368e6e57fe648b2e409b3efe Mon Sep 17 00:00:00 2001 From: "mergify[bot]" <37929162+mergify[bot]@users.noreply.github.com> Date: Mon, 23 Mar 2026 13:04:29 +0100 Subject: [PATCH 97/98] Remove duplicate test cases in TestAnySubscriptionCallback::is_serialized_message_callback (backport #3104) (#3108) * remove duplicate test cases in TestAnySubscriptionCallback::is_serialized_message_callback (#3104) Signed-off-by: Ubuntu Co-authored-by: Ubuntu (cherry picked from commit af78e01bd63933aaa255677277581f069dc9f0d6) Signed-off-by: Tomoya.Fujita Co-authored-by: Alexis Tsogias <1114095+Zyrin@users.noreply.github.com> Co-authored-by: Tomoya.Fujita --- rclcpp/test/rclcpp/test_any_subscription_callback.cpp | 9 --------- 1 file changed, 9 deletions(-) diff --git a/rclcpp/test/rclcpp/test_any_subscription_callback.cpp b/rclcpp/test/rclcpp/test_any_subscription_callback.cpp index 45fe091f07..0e29580f9f 100644 --- a/rclcpp/test/rclcpp/test_any_subscription_callback.cpp +++ b/rclcpp/test/rclcpp/test_any_subscription_callback.cpp @@ -112,15 +112,6 @@ TEST_F(TestAnySubscriptionCallback, is_serialized_message_callback) { std::make_shared(), rclcpp::MessageInfo{})); } - { - rclcpp::AnySubscriptionCallback asc; - asc.set([](const rclcpp::SerializedMessage &, const rclcpp::MessageInfo &) {}); - EXPECT_TRUE(asc.is_serialized_message_callback()); - EXPECT_NO_THROW( - asc.dispatch( - std::make_shared(), - rclcpp::MessageInfo{})); - } { rclcpp::AnySubscriptionCallback asc; asc.set([](std::unique_ptr) {}); From 8bf912a700857628e767adbd6a266a7673a600b3 Mon Sep 17 00:00:00 2001 From: ralwing <58466562+ralwing@users.noreply.github.com> Date: Fri, 27 Mar 2026 11:04:30 +0100 Subject: [PATCH 98/98] Sdp 4661 guard condition (#1) * Add error handling for guard conditions in wait set and log warnings - Implemented try-catch block in add_handles_to_wait_set to handle RCLError exceptions. - Added check for finalized guard conditions in add_guard_condition_to_rcl_wait_set to prevent null pointer dereference. - Introduced COLCON_IGNORE files in rclcpp_action, rclcpp_components, and rclcpp_lifecycle directories. * sharedptr * Remove COLCON_IGNORE files from rclcpp_action, rclcpp_components, and rclcpp_lifecycle * regression test --- rclcpp/include/rclcpp/executor.hpp | 6 +- rclcpp/src/rclcpp/executor.cpp | 12 +-- .../test_add_callback_groups_to_executor.cpp | 89 +++++++++++++++++++ 3 files changed, 99 insertions(+), 8 deletions(-) diff --git a/rclcpp/include/rclcpp/executor.hpp b/rclcpp/include/rclcpp/executor.hpp index 65d0a930cb..59f3065860 100644 --- a/rclcpp/include/rclcpp/executor.hpp +++ b/rclcpp/include/rclcpp/executor.hpp @@ -560,9 +560,9 @@ class Executor virtual void spin_once_impl(std::chrono::nanoseconds timeout); - typedef std::map> + typedef std::map< + rclcpp::CallbackGroup::WeakPtr, rclcpp::GuardCondition::SharedPtr, + std::owner_less> WeakCallbackGroupsToGuardConditionsMap; /// maps callback groups to guard conditions diff --git a/rclcpp/src/rclcpp/executor.cpp b/rclcpp/src/rclcpp/executor.cpp index a406cb6e8e..4cae552981 100644 --- a/rclcpp/src/rclcpp/executor.cpp +++ b/rclcpp/src/rclcpp/executor.cpp @@ -107,8 +107,7 @@ Executor::~Executor() 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); + memory_strategy_->remove_guard_condition(pair.second.get()); } weak_groups_to_guard_conditions_.clear(); @@ -218,7 +217,10 @@ Executor::add_callback_group_to_map( 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(); + // Store shared_ptr to keep the guard condition alive while registered with the executor. + // This prevents the guard condition from being finalized (impl set to NULL) while the + // memory strategy still holds a raw pointer to it during wait_for_work(). + weak_groups_to_guard_conditions_[weak_group_ptr] = callback_group_guard_condition; // Add the callback_group's notify condition to the guard condition handles memory_strategy_->add_guard_condition(*callback_group_guard_condition); } @@ -304,7 +306,7 @@ Executor::remove_callback_group_from_map( { 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); + memory_strategy_->remove_guard_condition(iter->second.get()); } weak_groups_to_guard_conditions_.erase(weak_group_ptr); @@ -730,7 +732,7 @@ Executor::wait_for_work(std::chrono::nanoseconds timeout) 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); + memory_strategy_->remove_guard_condition(guard_condition.get()); } weak_groups_to_nodes_.erase(group_ptr); }); diff --git a/rclcpp/test/rclcpp/test_add_callback_groups_to_executor.cpp b/rclcpp/test/rclcpp/test_add_callback_groups_to_executor.cpp index 07ca1e87d8..113d887578 100644 --- a/rclcpp/test/rclcpp/test_add_callback_groups_to_executor.cpp +++ b/rclcpp/test/rclcpp/test_add_callback_groups_to_executor.cpp @@ -14,10 +14,13 @@ #include +#include #include +#include #include #include #include +#include #include #include @@ -340,6 +343,92 @@ TYPED_TEST(TestAddCallbackGroupsToExecutor, subscriber_triggered_to_receive_mess EXPECT_TRUE(received_message_future.get()); } +/* + * Test destroying the last strong callback group reference while the executor is spinning. + * This exercises the callback-group lifetime path from https://github.com/ros2/rclcpp/issues/2163. + */ +TYPED_TEST(TestAddCallbackGroupsToExecutor, callback_group_destroyed_while_spinning) +{ + using ExecutorType = TypeParam; + + ExecutorType executor; + auto node = std::make_shared("callback_group_destroyed_while_spinning", "/ns"); + executor.add_node(node); + + auto count_live_callback_groups = [&executor]() { + size_t count = 0; + for (const auto & weak_group : executor.get_all_callback_groups()) { + if (weak_group.lock()) { + ++count; + } + } + return count; + }; + + auto wait_for_live_callback_groups = + [&count_live_callback_groups](size_t expected_count, std::chrono::milliseconds timeout) { + const auto deadline = std::chrono::steady_clock::now() + timeout; + while (std::chrono::steady_clock::now() < deadline) { + if (count_live_callback_groups() == expected_count) { + return true; + } + std::this_thread::sleep_for(1ms); + } + return count_live_callback_groups() == expected_count; + }; + + const auto initial_callback_group_count = count_live_callback_groups(); + + std::exception_ptr spin_exception; + std::thread spin_thread([&executor, &spin_exception]() { + try { + executor.spin(); + } catch (...) { + spin_exception = std::current_exception(); + } + }); + + auto heartbeat_group = node->create_callback_group( + rclcpp::CallbackGroupType::MutuallyExclusive, true); + auto heartbeat_timer = node->create_wall_timer(1ms, []() {}, heartbeat_group); + + bool callback_groups_tracked = + wait_for_live_callback_groups(initial_callback_group_count + 1u, 2s); + const auto steady_state_callback_group_count = count_live_callback_groups(); + bool callback_groups_cleaned_up = callback_groups_tracked; + + for (size_t attempt = 0; attempt < 50 && callback_groups_cleaned_up; ++attempt) { + auto transient_group = node->create_callback_group( + rclcpp::CallbackGroupType::MutuallyExclusive, true); + auto transient_timer = node->create_wall_timer(1ms, []() {}, transient_group); + + callback_groups_cleaned_up = wait_for_live_callback_groups( + steady_state_callback_group_count + 1u, 2s); + + transient_timer.reset(); + transient_group.reset(); + + callback_groups_cleaned_up = callback_groups_cleaned_up && + wait_for_live_callback_groups(steady_state_callback_group_count, 2s); + } + + executor.cancel(); + spin_thread.join(); + + EXPECT_TRUE(callback_groups_tracked); + EXPECT_TRUE(callback_groups_cleaned_up); + + if (spin_exception) { + try { + std::rethrow_exception(spin_exception); + } catch (const std::exception & exception) { + FAIL() << "executor.spin() threw: " << exception.what(); + } catch (...) { + FAIL() << "executor.spin() threw a non-standard exception"; + } + } +} + /* * Test removing callback group from executor that its not associated with. */