Compare commits

...

18 Commits

Author SHA1 Message Date
Chris Lalancette
ea31f94eb4 23.0.0 2023-09-08 20:47:00 +00:00
Chris Lalancette
f496291e81 Changelog.
Signed-off-by: Chris Lalancette <clalancette@gmail.com>
2023-09-08 20:46:51 +00:00
Chris Lalancette
dd6fad6d42 Fix the return type of Rate::period. (#2301)
In a recent commit (bc435776a2),
we reworked how the Rate class worked so it could be
used with ROS time as well.  Unfortunately, we also
accidentally broke the API of it by changing the return
type of Rate::period to a Duration instead of a
std::chrono::nanoseconds .  Put this back to a std::chrono::nanoseconds;
if we want to change it to a Duration, we'll have to
add a new method and deprecate this one.

Signed-off-by: Chris Lalancette <clalancette@gmail.com>
2023-09-08 16:40:09 -04:00
Christophe Bedard
38734d769a Update API docs links in package READMEs (#2302)
Signed-off-by: Christophe Bedard <christophe.bedard@apex.ai>
2023-09-08 13:59:15 -04:00
jmachowinski
e103b8d37e fix(ClientGoalHandle): Made mutex recursive to prevent deadlocks (#2267)
* fix(ClientGoalHandle): Made mutex recursive to prevent deadlocks

This prevents deadlocks in cases, were e.g. get_status() would
be called on the handle in a callback of the handle.

* test(rclcpp_action): Added test for deadlocks during access of a goal handle

This test checks, if the code deadlocks, if methods on the goal handle are
called from the callbacks.

Signed-off-by: Janosch Machowinski <J.Machowinski@cellumation.com>
Co-authored-by: Tomoya Fujita <Tomoya.Fujita@sony.com>
2023-09-07 17:37:35 -04:00
Chris Lalancette
253d395d4e Cleanup flaky timers_manager tests. (#2299)
* Cleanup flaky timers_manager tests.

The timers_manager tests were relying too heavily on
specific timings; this caused them to be flaky on the
buildfarm, particularly on Windows.

Here, we increase the timeouts, and remove one test which
just relies too heavily on specific timeouts.  This should
make this test much less flaky on the buildfarm.

Signed-off-by: Chris Lalancette <clalancette@gmail.com>
2023-09-07 15:17:16 -04:00
Chris Lalancette
d5e5141b3d 22.2.0 2023-09-07 14:59:44 +00:00
Chris Lalancette
a0148dfd5d Changelog.
Signed-off-by: Chris Lalancette <clalancette@gmail.com>
2023-09-07 14:59:26 +00:00
Chen Lihui
5e152d77d8 Topic correct typeadapter deduction (#2294)
* fix TypeAdapter deduction

Signed-off-by: Chen Lihui <lihui.chen@sony.com>
2023-09-06 10:24:51 -04:00
AiVerisimilitude
fa732b9ee8 Fix C++20 allocator construct deprecation (#2292)
Signed-off-by: Guilherme Rodrigues <guilherme.rodrigues@ait.ac.at>
2023-09-01 17:17:00 -07:00
Alexey Merzlyakov
bc435776a2 Make Rate to select the clock to work with (#2123)
* Make Rate to select the clock to work with
Add ROSRate respective with ROS time

* Make GenericRate class to be deprecated

* Adjust test cases for new rates

is_steady() to be deprecated

Signed-off-by: Alexey Merzlyakov <alexey.merzlyakov@samsung.com>
Co-authored-by: Chris Lalancette <clalancette@gmail.com>
2023-08-31 15:50:40 -04:00
Jiaqi Li
43cf0be15c Correct the position of a comment. (#2290)
Signed-off-by: Jiaqi Li <ljq0831@qq.com>
2023-08-29 13:22:51 -04:00
Chris Lalancette
fd58bddb05 Remove unnecessary lambda captures in the tests. (#2289)
* Remove unnecessary lambda captures in the tests.

This was pointed out by compiling with clang.

Signed-off-by: Chris Lalancette <clalancette@gmail.com>
2023-08-28 14:17:27 -04:00
Tomoya Fujita
e7f06398db add logger level service to lifecycle node. (#2277)
Signed-off-by: Tomoya Fujita <Tomoya.Fujita@sony.com>
2023-08-26 11:38:52 -07:00
Chris Lalancette
ba175922d3 Add rcl_logging_interface as an explicit dependency. (#2284)
It is depended on by rclcpp/src/rclcpp/logger.cpp, but
the dependency was not explicitly declared (it was
being inherited from rcl, I believe).  Fix that here.

Signed-off-by: Chris Lalancette <clalancette@gmail.com>
2023-08-23 19:43:54 -04:00
Chris Lalancette
77db1ed25b Revamp list_parameters to be more efficient and easier to read. (#2282)
1. Use constref for the loop variable.
2. Do more work outside of the loop.
3. Skip doing unnecessary work where we can inside the loop.

With this in place, I measured about a 7% performance
improvement over the previous implementation.

Signed-off-by: Chris Lalancette <clalancette@gmail.com>
2023-08-23 08:15:44 -04:00
Chris Lalancette
403f305b15 Fix a typo in a comment. (#2283)
Signed-off-by: Chris Lalancette <clalancette@gmail.com>
2023-08-22 15:22:01 -07:00
Tomoya Fujita
fd229d72ff doc fix: call canceled only after goal state is in canceling. (#2266)
Signed-off-by: Tomoya Fujita <Tomoya.Fujita@sony.com>
2023-08-21 16:04:49 -04:00
32 changed files with 818 additions and 166 deletions

View File

@@ -2,6 +2,24 @@
Changelog for package rclcpp
^^^^^^^^^^^^^^^^^^^^^^^^^^^^
23.0.0 (2023-09-08)
-------------------
* Fix the return type of Rate::period. (`#2301 <https://github.com/ros2/rclcpp/issues/2301>`_)
* Update API docs links in package READMEs (`#2302 <https://github.com/ros2/rclcpp/issues/2302>`_)
* Cleanup flaky timers_manager tests. (`#2299 <https://github.com/ros2/rclcpp/issues/2299>`_)
* Contributors: Chris Lalancette, Christophe Bedard
22.2.0 (2023-09-07)
-------------------
* Topic correct typeadapter deduction (`#2294 <https://github.com/ros2/rclcpp/issues/2294>`_)
* Fix C++20 allocator construct deprecation (`#2292 <https://github.com/ros2/rclcpp/issues/2292>`_)
* Make Rate to select the clock to work with (`#2123 <https://github.com/ros2/rclcpp/issues/2123>`_)
* Correct the position of a comment. (`#2290 <https://github.com/ros2/rclcpp/issues/2290>`_)
* Remove unnecessary lambda captures in the tests. (`#2289 <https://github.com/ros2/rclcpp/issues/2289>`_)
* Add rcl_logging_interface as an explicit dependency. (`#2284 <https://github.com/ros2/rclcpp/issues/2284>`_)
* Revamp list_parameters to be more efficient and easier to read. (`#2282 <https://github.com/ros2/rclcpp/issues/2282>`_)
* Contributors: AiVerisimilitude, Alexey Merzlyakov, Chen Lihui, Chris Lalancette, Jiaqi Li
22.1.0 (2023-08-21)
-------------------
* Do not crash Executor when send_response fails due to client failure. (`#2276 <https://github.com/ros2/rclcpp/issues/2276>`_)

View File

@@ -10,6 +10,7 @@ find_package(builtin_interfaces REQUIRED)
find_package(libstatistics_collector REQUIRED)
find_package(rcl REQUIRED)
find_package(rcl_interfaces REQUIRED)
find_package(rcl_logging_interface REQUIRED)
find_package(rcl_yaml_param_parser REQUIRED)
find_package(rcpputils REQUIRED)
find_package(rcutils REQUIRED)
@@ -106,6 +107,7 @@ set(${PROJECT_NAME}_SRCS
src/rclcpp/qos.cpp
src/rclcpp/event_handler.cpp
src/rclcpp/qos_overriding_options.cpp
src/rclcpp/rate.cpp
src/rclcpp/serialization.cpp
src/rclcpp/serialized_message.cpp
src/rclcpp/service.cpp
@@ -207,6 +209,7 @@ ament_target_dependencies(${PROJECT_NAME}
"libstatistics_collector"
"rcl"
"rcl_interfaces"
"rcl_logging_interface"
"rcl_yaml_param_parser"
"rcpputils"
"rcutils"

View File

@@ -2,7 +2,7 @@
The ROS client library in C++.
Visit the [rclcpp API documentation](http://docs.ros2.org/latest/api/rclcpp/) for a complete list of its main components and features.
The link to the latest rclcpp API documentation, which includes a complete list of its main components and features, can be found on the rclcpp package info page, at the [ROS Index](https://index.ros.org/p/rclcpp/).
## Quality Declaration

View File

@@ -486,13 +486,13 @@ private:
"subscription use different allocator types, which is not supported");
}
if constexpr (rclcpp::TypeAdapter<MessageT>::is_specialized::value) {
if constexpr (rclcpp::TypeAdapter<MessageT, ROSMessageType>::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<MessageT>::convert_to_ros_message(*message, *ptr);
rclcpp::TypeAdapter<MessageT, ROSMessageType>::convert_to_ros_message(*message, *ptr);
auto ros_msg = std::unique_ptr<ROSMessageType, ROSMessageTypeDeleter>(ptr, deleter);
ros_message_subscription->provide_intra_process_message(std::move(ros_msg));
} else {

View File

@@ -527,7 +527,7 @@ private:
void execute_ready_timers_unsafe();
// Callback to be called when timer is ready
std::function<void(const rclcpp::TimerBase *)> on_ready_callback_ = nullptr;
std::function<void(const rclcpp::TimerBase *)> on_ready_callback_;
// Thread used to run the timers execution task
std::thread timers_thread_;

View File

@@ -19,6 +19,8 @@
#include <memory>
#include <thread>
#include "rclcpp/clock.hpp"
#include "rclcpp/duration.hpp"
#include "rclcpp/macros.hpp"
#include "rclcpp/utilities.hpp"
#include "rclcpp/visibility_control.hpp"
@@ -31,9 +33,20 @@ class RateBase
public:
RCLCPP_SMART_PTR_DEFINITIONS_NOT_COPYABLE(RateBase)
RCLCPP_PUBLIC
virtual ~RateBase() {}
RCLCPP_PUBLIC
virtual bool sleep() = 0;
[[deprecated("use get_type() instead")]]
RCLCPP_PUBLIC
virtual bool is_steady() const = 0;
RCLCPP_PUBLIC
virtual rcl_clock_type_t get_type() const = 0;
RCLCPP_PUBLIC
virtual void reset() = 0;
};
@@ -42,14 +55,13 @@ using std::chrono::duration_cast;
using std::chrono::nanoseconds;
template<class Clock = std::chrono::high_resolution_clock>
class GenericRate : public RateBase
class [[deprecated("use rclcpp::Rate class instead of GenericRate")]] GenericRate : public RateBase
{
public:
RCLCPP_SMART_PTR_DEFINITIONS(GenericRate)
explicit GenericRate(double rate)
: GenericRate<Clock>(
duration_cast<nanoseconds>(duration<double>(1.0 / rate)))
: period_(duration_cast<nanoseconds>(duration<double>(1.0 / rate))), last_interval_(Clock::now())
{}
explicit GenericRate(std::chrono::nanoseconds period)
: period_(period), last_interval_(Clock::now())
@@ -87,12 +99,18 @@ public:
return true;
}
[[deprecated("use get_type() instead")]]
virtual bool
is_steady() const
{
return Clock::is_steady;
}
virtual rcl_clock_type_t get_type() const
{
return Clock::is_steady ? RCL_STEADY_TIME : RCL_SYSTEM_TIME;
}
virtual void
reset()
{
@@ -112,8 +130,69 @@ private:
std::chrono::time_point<Clock, ClockDurationNano> last_interval_;
};
using Rate = GenericRate<std::chrono::system_clock>;
using WallRate = GenericRate<std::chrono::steady_clock>;
class Rate : public RateBase
{
public:
RCLCPP_SMART_PTR_DEFINITIONS(Rate)
RCLCPP_PUBLIC
explicit Rate(
const double rate,
Clock::SharedPtr clock = std::make_shared<Clock>(RCL_SYSTEM_TIME));
RCLCPP_PUBLIC
explicit Rate(
const Duration & period,
Clock::SharedPtr clock = std::make_shared<Clock>(RCL_SYSTEM_TIME));
RCLCPP_PUBLIC
virtual bool
sleep();
[[deprecated("use get_type() instead")]]
RCLCPP_PUBLIC
virtual bool
is_steady() const;
RCLCPP_PUBLIC
virtual rcl_clock_type_t
get_type() const;
RCLCPP_PUBLIC
virtual void
reset();
RCLCPP_PUBLIC
std::chrono::nanoseconds
period() const;
private:
RCLCPP_DISABLE_COPY(Rate)
Clock::SharedPtr clock_;
Duration period_;
Time last_interval_;
};
class WallRate : public Rate
{
public:
RCLCPP_PUBLIC
explicit WallRate(const double rate);
RCLCPP_PUBLIC
explicit WallRate(const Duration & period);
};
class ROSRate : public Rate
{
public:
RCLCPP_PUBLIC
explicit ROSRate(const double rate);
RCLCPP_PUBLIC
explicit ROSRate(const Duration & period);
};
} // namespace rclcpp

View File

@@ -2,7 +2,7 @@
<?xml-model href="http://download.ros.org/schema/package_format2.xsd" schematypens="http://www.w3.org/2001/XMLSchema"?>
<package format="2">
<name>rclcpp</name>
<version>22.1.0</version>
<version>23.0.0</version>
<description>The ROS client library in C++.</description>
<maintainer email="ivanpauno@ekumenlabs.com">Ivan Paunovic</maintainer>
@@ -35,6 +35,7 @@
<depend>libstatistics_collector</depend>
<depend>rcl</depend>
<depend>rcl_logging_interface</depend>
<depend>rcl_yaml_param_parser</depend>
<depend>rcpputils</depend>
<depend>rcutils</depend>

View File

@@ -125,7 +125,6 @@ bool
ClientBase::wait_for_service_nanoseconds(std::chrono::nanoseconds timeout)
{
auto start = std::chrono::steady_clock::now();
// make an event to reuse, rather than create a new one each time
auto node_ptr = node_graph_.lock();
if (!node_ptr) {
throw InvalidNodeError();
@@ -138,6 +137,7 @@ ClientBase::wait_for_service_nanoseconds(std::chrono::nanoseconds timeout)
// check was non-blocking, return immediately
return false;
}
// make an event to reuse, rather than create a new one each time
auto event = node_ptr->get_graph_event();
// update the time even on the first loop to account for time spent in the first call
// to this->server_is_ready()

View File

@@ -28,9 +28,9 @@ using rclcpp::experimental::TimersManager;
TimersManager::TimersManager(
std::shared_ptr<rclcpp::Context> context,
std::function<void(const rclcpp::TimerBase *)> on_ready_callback)
: on_ready_callback_(on_ready_callback),
context_(context)
{
context_ = context;
on_ready_callback_ = on_ready_callback;
}
TimersManager::~TimersManager()

View File

@@ -1038,37 +1038,50 @@ NodeParameters::list_parameters(const std::vector<std::string> & prefixes, uint6
// TODO(mikaelarguedas) define parameter separator different from "/" to avoid ambiguity
// using "." for now
const char * separator = ".";
for (auto & kv : parameters_) {
bool get_all = (prefixes.size() == 0) &&
((depth == rcl_interfaces::srv::ListParameters::Request::DEPTH_RECURSIVE) ||
(static_cast<uint64_t>(std::count(kv.first.begin(), kv.first.end(), *separator)) < depth));
bool prefix_matches = std::any_of(
prefixes.cbegin(), prefixes.cend(),
[&kv, &depth, &separator](const std::string & prefix) {
if (kv.first == prefix) {
return true;
} else if (kv.first.find(prefix + separator) == 0) {
size_t length = prefix.length();
std::string substr = kv.first.substr(length);
// Cast as unsigned integer to avoid warning
return (depth == rcl_interfaces::srv::ListParameters::Request::DEPTH_RECURSIVE) ||
(static_cast<uint64_t>(std::count(substr.begin(), substr.end(), *separator)) < depth);
}
return false;
});
if (get_all || prefix_matches) {
result.names.push_back(kv.first);
size_t last_separator = kv.first.find_last_of(separator);
if (std::string::npos != last_separator) {
std::string prefix = kv.first.substr(0, last_separator);
if (
std::find(result.prefixes.cbegin(), result.prefixes.cend(), prefix) ==
result.prefixes.cend())
{
result.prefixes.push_back(prefix);
auto separators_less_than_depth = [&depth, &separator](const std::string & str) -> bool {
return static_cast<uint64_t>(std::count(str.begin(), str.end(), *separator)) < depth;
};
bool recursive = (prefixes.size() == 0) &&
(depth == rcl_interfaces::srv::ListParameters::Request::DEPTH_RECURSIVE);
for (const std::pair<const std::string, ParameterInfo> & kv : parameters_) {
if (!recursive) {
bool get_all = (prefixes.size() == 0) && separators_less_than_depth(kv.first);
if (!get_all) {
bool prefix_matches = std::any_of(
prefixes.cbegin(), prefixes.cend(),
[&kv, &depth, &separator, &separators_less_than_depth](const std::string & prefix) {
if (kv.first == prefix) {
return true;
} else if (kv.first.find(prefix + separator) == 0) {
if (depth == rcl_interfaces::srv::ListParameters::Request::DEPTH_RECURSIVE) {
return true;
}
std::string substr = kv.first.substr(prefix.length());
return separators_less_than_depth(substr);
}
return false;
});
if (!prefix_matches) {
continue;
}
}
}
result.names.push_back(kv.first);
size_t last_separator = kv.first.find_last_of(separator);
if (std::string::npos != last_separator) {
std::string prefix = kv.first.substr(0, last_separator);
if (
std::find(result.prefixes.cbegin(), result.prefixes.cend(), prefix) ==
result.prefixes.cend())
{
result.prefixes.push_back(prefix);
}
}
}
return result;
}

113
rclcpp/src/rclcpp/rate.cpp Normal file
View File

@@ -0,0 +1,113 @@
// Copyright 2023 Open Source Robotics Foundation, Inc.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
#include "rclcpp/rate.hpp"
#include <chrono>
#include <stdexcept>
namespace rclcpp
{
Rate::Rate(
const double rate, Clock::SharedPtr clock)
: clock_(clock), period_(0, 0), last_interval_(clock_->now())
{
if (rate <= 0.0) {
throw std::invalid_argument{"rate must be greater than 0"};
}
period_ = Duration::from_seconds(1.0 / rate);
}
Rate::Rate(
const Duration & period, Clock::SharedPtr clock)
: clock_(clock), period_(period), last_interval_(clock_->now())
{
if (period <= Duration(0, 0)) {
throw std::invalid_argument{"period must be greater than 0"};
}
}
bool
Rate::sleep()
{
// Time coming into sleep
auto now = clock_->now();
// Time of next interval
auto next_interval = last_interval_ + period_;
// Detect backwards time flow
if (now < last_interval_) {
// Best thing to do is to set the next_interval to now + period
next_interval = now + period_;
}
// Update the interval
last_interval_ += period_;
// If the time_to_sleep is negative or zero, don't sleep
if (next_interval <= now) {
// If an entire cycle was missed then reset next interval.
// This might happen if the loop took more than a cycle.
// Or if time jumps forward.
if (now > next_interval + period_) {
last_interval_ = now + period_;
}
// Either way do not sleep and return false
return false;
}
// Calculate the time to sleep
auto time_to_sleep = next_interval - now;
// Sleep (will get interrupted by ctrl-c, may not sleep full time)
return clock_->sleep_for(time_to_sleep);
}
bool
Rate::is_steady() const
{
return clock_->get_clock_type() == RCL_STEADY_TIME;
}
rcl_clock_type_t
Rate::get_type() const
{
return clock_->get_clock_type();
}
void
Rate::reset()
{
last_interval_ = clock_->now();
}
std::chrono::nanoseconds
Rate::period() const
{
return std::chrono::nanoseconds(period_.nanoseconds());
}
WallRate::WallRate(const double rate)
: Rate(rate, std::make_shared<Clock>(RCL_STEADY_TIME))
{}
WallRate::WallRate(const Duration & period)
: Rate(period, std::make_shared<Clock>(RCL_STEADY_TIME))
{}
ROSRate::ROSRate(const double rate)
: Rate(rate, std::make_shared<Clock>(RCL_ROS_TIME))
{}
ROSRate::ROSRate(const Duration & period)
: Rate(period, std::make_shared<Clock>(RCL_ROS_TIME))
{}
} // namespace rclcpp

View File

@@ -60,7 +60,7 @@ TEST_F(TestEventsExecutor, run_pub_sub)
executor.add_node(node);
bool spin_exited = false;
std::thread spinner([&spin_exited, &executor, this]() {
std::thread spinner([&spin_exited, &executor]() {
executor.spin();
spin_exited = true;
});
@@ -107,7 +107,7 @@ TEST_F(TestEventsExecutor, run_clients_servers)
executor.add_node(node);
bool spin_exited = false;
std::thread spinner([&spin_exited, &executor, this]() {
std::thread spinner([&spin_exited, &executor]() {
executor.spin();
spin_exited = true;
});
@@ -346,7 +346,7 @@ TEST_F(TestEventsExecutor, cancel_while_timers_running)
});
std::thread spinner([&executor, this]() {executor.spin();});
std::thread spinner([&executor]() {executor.spin();});
std::this_thread::sleep_for(10ms);
// Call cancel while t1 callback is still being executed
@@ -373,7 +373,7 @@ TEST_F(TestEventsExecutor, cancel_while_timers_waiting)
executor.add_node(node);
auto start = std::chrono::steady_clock::now();
std::thread spinner([&executor, this]() {executor.spin();});
std::thread spinner([&executor]() {executor.spin();});
std::this_thread::sleep_for(10ms);
executor.cancel();
@@ -395,7 +395,7 @@ TEST_F(TestEventsExecutor, destroy_entities)
2ms, [&]() {publisher->publish(std::make_unique<test_msgs::msg::Empty>());});
EventsExecutor executor_pub;
executor_pub.add_node(node_pub);
std::thread spinner([&executor_pub, this]() {executor_pub.spin();});
std::thread spinner([&executor_pub]() {executor_pub.spin();});
// Create a node with two different subscriptions to the topic
auto node_sub = std::make_shared<rclcpp::Node>("node_sub");

View File

@@ -779,7 +779,7 @@ TYPED_TEST(TestIntraprocessExecutors, testIntraprocessRetrigger) {
// 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;
static constexpr size_t kNumMessages = 100;
using ExecutorType = TypeParam;
ExecutorType executor;
@@ -808,11 +808,9 @@ TYPED_TEST(TestIntraprocessExecutors, testIntraprocessRetrigger) {
// 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]() {
std::chrono::milliseconds(10), [this, &executor, &loops]() {
loops++;
if (kNumMessages == this->callback_count.load() ||
loops == 500)
{
if (kNumMessages == this->callback_count.load() || loops == 500) {
executor.cancel();
}
});

View File

@@ -152,33 +152,54 @@ TEST_F(TestPublisher, conversion_exception_is_passed_up) {
}
}
using UseTakeSharedMethod = bool;
class TestPublisherFixture
: public TestPublisher,
public ::testing::WithParamInterface<UseTakeSharedMethod>
{
};
/*
* 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<std::string, rclcpp::msg::String>;
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<StringTypeAdapter>(topic_name, 10);
auto sub = node->create_subscription<rclcpp::msg::String>(topic_name, 1, callback);
rclcpp::Subscription<rclcpp::msg::String>::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<rclcpp::msg::String>(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<rclcpp::msg::String>(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.
*/

View File

@@ -14,14 +14,19 @@
#include <gtest/gtest.h>
#include <stdexcept>
#include <string>
#include "rclcpp/rate.hpp"
#include "../utils/rclcpp_gtest_macros.hpp"
/*
Basic tests for the Rate and WallRate classes.
Basic tests for the Rate, WallRate and ROSRate classes.
*/
TEST(TestRate, rate_basics) {
rclcpp::init(0, nullptr);
auto period = std::chrono::milliseconds(1000);
auto offset = std::chrono::milliseconds(500);
auto epsilon = std::chrono::milliseconds(100);
@@ -29,8 +34,23 @@ TEST(TestRate, rate_basics) {
auto start = std::chrono::system_clock::now();
rclcpp::Rate r(period);
EXPECT_EQ(period, r.period());
EXPECT_EQ(rclcpp::Duration(period), r.period());
// suppress deprecated warnings
#if !defined(_WIN32)
# pragma GCC diagnostic push
# pragma GCC diagnostic ignored "-Wdeprecated-declarations"
#else // !defined(_WIN32)
# pragma warning(push)
# pragma warning(disable: 4996)
#endif
ASSERT_FALSE(r.is_steady());
// remove warning suppression
#if !defined(_WIN32)
# pragma GCC diagnostic pop
#else // !defined(_WIN32)
# pragma warning(pop)
#endif
ASSERT_EQ(RCL_SYSTEM_TIME, r.get_type());
ASSERT_TRUE(r.sleep());
auto one = std::chrono::system_clock::now();
auto delta = one - start;
@@ -59,9 +79,13 @@ TEST(TestRate, rate_basics) {
auto five = std::chrono::system_clock::now();
delta = five - four;
ASSERT_TRUE(epsilon > delta);
rclcpp::shutdown();
}
TEST(TestRate, wall_rate_basics) {
rclcpp::init(0, nullptr);
auto period = std::chrono::milliseconds(100);
auto offset = std::chrono::milliseconds(50);
auto epsilon = std::chrono::milliseconds(1);
@@ -69,8 +93,25 @@ TEST(TestRate, wall_rate_basics) {
auto start = std::chrono::steady_clock::now();
rclcpp::WallRate r(period);
EXPECT_EQ(period, r.period());
EXPECT_EQ(rclcpp::Duration(period), r.period());
// suppress deprecated warnings
#if !defined(_WIN32)
# pragma GCC diagnostic push
# pragma GCC diagnostic ignored "-Wdeprecated-declarations"
#else // !defined(_WIN32)
# pragma warning(push)
# pragma warning(disable: 4996)
#endif
ASSERT_TRUE(r.is_steady());
// suppress deprecated warnings
#if !defined(_WIN32)
# pragma GCC diagnostic push
# pragma GCC diagnostic ignored "-Wdeprecated-declarations"
#else // !defined(_WIN32)
# pragma warning(push)
# pragma warning(disable: 4996)
#endif
ASSERT_EQ(RCL_STEADY_TIME, r.get_type());
ASSERT_TRUE(r.sleep());
auto one = std::chrono::steady_clock::now();
auto delta = one - start;
@@ -99,23 +140,288 @@ TEST(TestRate, wall_rate_basics) {
auto five = std::chrono::steady_clock::now();
delta = five - four;
EXPECT_GT(epsilon, delta);
rclcpp::shutdown();
}
TEST(TestRate, ros_rate_basics) {
rclcpp::init(0, nullptr);
auto period = std::chrono::milliseconds(100);
auto offset = std::chrono::milliseconds(50);
auto epsilon = std::chrono::milliseconds(1);
double overrun_ratio = 1.5;
rclcpp::Clock clock(RCL_ROS_TIME);
auto start = clock.now();
rclcpp::ROSRate r(period);
EXPECT_EQ(rclcpp::Duration(period), r.period());
// suppress deprecated warnings
#if !defined(_WIN32)
# pragma GCC diagnostic push
# pragma GCC diagnostic ignored "-Wdeprecated-declarations"
#else // !defined(_WIN32)
# pragma warning(push)
# pragma warning(disable: 4996)
#endif
ASSERT_FALSE(r.is_steady());
// suppress deprecated warnings
#if !defined(_WIN32)
# pragma GCC diagnostic push
# pragma GCC diagnostic ignored "-Wdeprecated-declarations"
#else // !defined(_WIN32)
# pragma warning(push)
# pragma warning(disable: 4996)
#endif
ASSERT_EQ(RCL_ROS_TIME, r.get_type());
ASSERT_TRUE(r.sleep());
auto one = clock.now();
auto delta = one - start;
EXPECT_LT(rclcpp::Duration(period), delta);
EXPECT_GT(rclcpp::Duration(period * overrun_ratio), delta);
clock.sleep_for(offset);
ASSERT_TRUE(r.sleep());
auto two = clock.now();
delta = two - start;
EXPECT_LT(rclcpp::Duration(2 * period), delta + epsilon);
EXPECT_GT(rclcpp::Duration(2 * period * overrun_ratio), delta);
clock.sleep_for(offset);
auto two_offset = clock.now();
r.reset();
ASSERT_TRUE(r.sleep());
auto three = clock.now();
delta = three - two_offset;
EXPECT_LT(rclcpp::Duration(period), delta);
EXPECT_GT(rclcpp::Duration(period * overrun_ratio), delta);
clock.sleep_for(offset + period);
auto four = clock.now();
ASSERT_FALSE(r.sleep());
auto five = clock.now();
delta = five - four;
EXPECT_GT(rclcpp::Duration(epsilon), delta);
rclcpp::shutdown();
}
/*
Basic test for the deprecated GenericRate class.
*/
TEST(TestRate, generic_rate) {
auto period = std::chrono::milliseconds(100);
auto offset = std::chrono::milliseconds(50);
auto epsilon = std::chrono::milliseconds(1);
double overrun_ratio = 1.5;
{
auto start = std::chrono::system_clock::now();
// suppress deprecated warnings
#if !defined(_WIN32)
# pragma GCC diagnostic push
# pragma GCC diagnostic ignored "-Wdeprecated-declarations"
#else // !defined(_WIN32)
# pragma warning(push)
# pragma warning(disable: 4996)
#endif
rclcpp::GenericRate<std::chrono::system_clock> gr(period);
ASSERT_FALSE(gr.is_steady());
// remove warning suppression
#if !defined(_WIN32)
# pragma GCC diagnostic pop
#else // !defined(_WIN32)
# pragma warning(pop)
#endif
ASSERT_EQ(gr.get_type(), RCL_SYSTEM_TIME);
EXPECT_EQ(period, gr.period());
ASSERT_TRUE(gr.sleep());
auto one = std::chrono::system_clock::now();
auto delta = one - start;
EXPECT_LT(period, delta + epsilon);
EXPECT_GT(period * overrun_ratio, delta);
rclcpp::sleep_for(offset);
ASSERT_TRUE(gr.sleep());
auto two = std::chrono::system_clock::now();
delta = two - start;
EXPECT_LT(2 * period, delta);
EXPECT_GT(2 * period * overrun_ratio, delta);
rclcpp::sleep_for(offset);
auto two_offset = std::chrono::system_clock::now();
gr.reset();
ASSERT_TRUE(gr.sleep());
auto three = std::chrono::system_clock::now();
delta = three - two_offset;
EXPECT_LT(period, delta + epsilon);
EXPECT_GT(period * overrun_ratio, delta);
rclcpp::sleep_for(offset + period);
auto four = std::chrono::system_clock::now();
ASSERT_FALSE(gr.sleep());
auto five = std::chrono::system_clock::now();
delta = five - four;
ASSERT_TRUE(epsilon > delta);
}
{
auto start = std::chrono::steady_clock::now();
// suppress deprecated warnings
#if !defined(_WIN32)
# pragma GCC diagnostic push
# pragma GCC diagnostic ignored "-Wdeprecated-declarations"
#else // !defined(_WIN32)
# pragma warning(push)
# pragma warning(disable: 4996)
#endif
rclcpp::GenericRate<std::chrono::steady_clock> gr(period);
ASSERT_TRUE(gr.is_steady());
// remove warning suppression
#if !defined(_WIN32)
# pragma GCC diagnostic pop
#else // !defined(_WIN32)
# pragma warning(pop)
#endif
ASSERT_EQ(gr.get_type(), RCL_STEADY_TIME);
EXPECT_EQ(period, gr.period());
ASSERT_TRUE(gr.sleep());
auto one = std::chrono::steady_clock::now();
auto delta = one - start;
EXPECT_LT(period, delta);
EXPECT_GT(period * overrun_ratio, delta);
rclcpp::sleep_for(offset);
ASSERT_TRUE(gr.sleep());
auto two = std::chrono::steady_clock::now();
delta = two - start;
EXPECT_LT(2 * period, delta + epsilon);
EXPECT_GT(2 * period * overrun_ratio, delta);
rclcpp::sleep_for(offset);
auto two_offset = std::chrono::steady_clock::now();
gr.reset();
ASSERT_TRUE(gr.sleep());
auto three = std::chrono::steady_clock::now();
delta = three - two_offset;
EXPECT_LT(period, delta);
EXPECT_GT(period * overrun_ratio, delta);
rclcpp::sleep_for(offset + period);
auto four = std::chrono::steady_clock::now();
ASSERT_FALSE(gr.sleep());
auto five = std::chrono::steady_clock::now();
delta = five - four;
EXPECT_GT(epsilon, delta);
}
}
TEST(TestRate, from_double) {
{
rclcpp::WallRate rate(1.0);
EXPECT_EQ(std::chrono::seconds(1), rate.period());
rclcpp::Rate rate(1.0);
EXPECT_EQ(rclcpp::Duration(std::chrono::seconds(1)), rate.period());
}
{
rclcpp::WallRate rate(2.0);
EXPECT_EQ(std::chrono::milliseconds(500), rate.period());
EXPECT_EQ(rclcpp::Duration(std::chrono::milliseconds(500)), rate.period());
}
{
rclcpp::WallRate rate(0.5);
EXPECT_EQ(std::chrono::seconds(2), rate.period());
EXPECT_EQ(rclcpp::Duration(std::chrono::seconds(2)), rate.period());
}
{
rclcpp::WallRate rate(4.0);
EXPECT_EQ(std::chrono::milliseconds(250), rate.period());
rclcpp::ROSRate rate(4.0);
EXPECT_EQ(rclcpp::Duration(std::chrono::milliseconds(250)), rate.period());
}
}
TEST(TestRate, clock_types) {
{
rclcpp::Rate rate(1.0, std::make_shared<rclcpp::Clock>(RCL_SYSTEM_TIME));
// suppress deprecated warnings
#if !defined(_WIN32)
# pragma GCC diagnostic push
# pragma GCC diagnostic ignored "-Wdeprecated-declarations"
#else // !defined(_WIN32)
# pragma warning(push)
# pragma warning(disable: 4996)
#endif
EXPECT_FALSE(rate.is_steady());
// remove warning suppression
#if !defined(_WIN32)
# pragma GCC diagnostic pop
#else // !defined(_WIN32)
# pragma warning(pop)
#endif
EXPECT_EQ(RCL_SYSTEM_TIME, rate.get_type());
}
{
rclcpp::Rate rate(1.0, std::make_shared<rclcpp::Clock>(RCL_STEADY_TIME));
// suppress deprecated warnings
#if !defined(_WIN32)
# pragma GCC diagnostic push
# pragma GCC diagnostic ignored "-Wdeprecated-declarations"
#else // !defined(_WIN32)
# pragma warning(push)
# pragma warning(disable: 4996)
#endif
EXPECT_TRUE(rate.is_steady());
// remove warning suppression
#if !defined(_WIN32)
# pragma GCC diagnostic pop
#else // !defined(_WIN32)
# pragma warning(pop)
#endif
EXPECT_EQ(RCL_STEADY_TIME, rate.get_type());
}
{
rclcpp::Rate rate(1.0, std::make_shared<rclcpp::Clock>(RCL_ROS_TIME));
// suppress deprecated warnings
#if !defined(_WIN32)
# pragma GCC diagnostic push
# pragma GCC diagnostic ignored "-Wdeprecated-declarations"
#else // !defined(_WIN32)
# pragma warning(push)
# pragma warning(disable: 4996)
#endif
EXPECT_FALSE(rate.is_steady());
// remove warning suppression
#if !defined(_WIN32)
# pragma GCC diagnostic pop
#else // !defined(_WIN32)
# pragma warning(pop)
#endif
EXPECT_EQ(RCL_ROS_TIME, rate.get_type());
}
}
TEST(TestRate, incorrect_constuctor) {
// Constructor with 0-frequency
RCLCPP_EXPECT_THROW_EQ(
rclcpp::Rate rate(0.0),
std::invalid_argument("rate must be greater than 0"));
// Constructor with negative frequency
RCLCPP_EXPECT_THROW_EQ(
rclcpp::Rate rate(-1.0),
std::invalid_argument("rate must be greater than 0"));
// Constructor with 0-duration
RCLCPP_EXPECT_THROW_EQ(
rclcpp::Rate rate(rclcpp::Duration(0, 0)),
std::invalid_argument("period must be greater than 0"));
// Constructor with negative duration
RCLCPP_EXPECT_THROW_EQ(
rclcpp::Rate rate(rclcpp::Duration(-1, 0)),
std::invalid_argument("period must be greater than 0"));
}

View File

@@ -15,6 +15,7 @@
#include <gtest/gtest.h>
#include <chrono>
#include <cmath>
#include <memory>
#include <utility>
@@ -65,8 +66,10 @@ TEST_F(TestTimersManager, empty_manager)
TEST_F(TestTimersManager, add_run_remove_timer)
{
size_t t_runs = 0;
std::chrono::milliseconds timer_period(10);
auto t = TimerT::make_shared(
10ms,
timer_period,
[&t_runs]() {
t_runs++;
},
@@ -79,7 +82,7 @@ TEST_F(TestTimersManager, add_run_remove_timer)
timers_manager->add_timer(t);
// Sleep for more 3 times the timer period
std::this_thread::sleep_for(30ms);
std::this_thread::sleep_for(3 * timer_period);
// The timer is executed only once, even if we slept 3 times the period
execute_all_ready_timers(timers_manager);
@@ -191,67 +194,6 @@ TEST_F(TestTimersManager, head_not_ready)
EXPECT_EQ(0u, t_runs);
}
TEST_F(TestTimersManager, timers_order)
{
auto timers_manager = std::make_shared<TimersManager>(
rclcpp::contexts::get_global_default_context());
size_t t1_runs = 0;
auto t1 = TimerT::make_shared(
10ms,
[&t1_runs]() {
t1_runs++;
},
rclcpp::contexts::get_global_default_context());
size_t t2_runs = 0;
auto t2 = TimerT::make_shared(
30ms,
[&t2_runs]() {
t2_runs++;
},
rclcpp::contexts::get_global_default_context());
size_t t3_runs = 0;
auto t3 = TimerT::make_shared(
100ms,
[&t3_runs]() {
t3_runs++;
},
rclcpp::contexts::get_global_default_context());
// Add timers in a random order
timers_manager->add_timer(t2);
timers_manager->add_timer(t3);
timers_manager->add_timer(t1);
std::this_thread::sleep_for(10ms);
execute_all_ready_timers(timers_manager);
EXPECT_EQ(1u, t1_runs);
EXPECT_EQ(0u, t2_runs);
EXPECT_EQ(0u, t3_runs);
std::this_thread::sleep_for(30ms);
execute_all_ready_timers(timers_manager);
EXPECT_EQ(2u, t1_runs);
EXPECT_EQ(1u, t2_runs);
EXPECT_EQ(0u, t3_runs);
std::this_thread::sleep_for(100ms);
execute_all_ready_timers(timers_manager);
EXPECT_EQ(3u, t1_runs);
EXPECT_EQ(2u, t2_runs);
EXPECT_EQ(1u, t3_runs);
timers_manager->remove_timer(t1);
std::this_thread::sleep_for(30ms);
execute_all_ready_timers(timers_manager);
EXPECT_EQ(3u, t1_runs);
EXPECT_EQ(3u, t2_runs);
EXPECT_EQ(1u, t3_runs);
}
TEST_F(TestTimersManager, start_stop_timers_thread)
{
auto timers_manager = std::make_shared<TimersManager>(
@@ -274,7 +216,7 @@ TEST_F(TestTimersManager, timers_thread)
auto timers_manager = std::make_shared<TimersManager>(
rclcpp::contexts::get_global_default_context());
size_t t1_runs = 0;
int t1_runs = 0;
auto t1 = TimerT::make_shared(
1ms,
[&t1_runs]() {
@@ -282,7 +224,7 @@ TEST_F(TestTimersManager, timers_thread)
},
rclcpp::contexts::get_global_default_context());
size_t t2_runs = 0;
int t2_runs = 0;
auto t2 = TimerT::make_shared(
1ms,
[&t2_runs]() {
@@ -296,12 +238,12 @@ TEST_F(TestTimersManager, timers_thread)
// Run timers thread for a while
timers_manager->start();
std::this_thread::sleep_for(5ms);
std::this_thread::sleep_for(50ms);
timers_manager->stop();
EXPECT_LT(1u, t1_runs);
EXPECT_LT(1u, t2_runs);
EXPECT_EQ(t1_runs, t2_runs);
EXPECT_LE(std::abs(t1_runs - t2_runs), 1);
}
TEST_F(TestTimersManager, destructor)
@@ -365,13 +307,13 @@ TEST_F(TestTimersManager, add_remove_while_thread_running)
timers_manager->start();
// After a while remove t1 and add t2
std::this_thread::sleep_for(5ms);
std::this_thread::sleep_for(50ms);
timers_manager->remove_timer(t1);
size_t tmp_t1 = t1_runs;
timers_manager->add_timer(t2);
// Wait some more time and then stop
std::this_thread::sleep_for(5ms);
std::this_thread::sleep_for(50ms);
timers_manager->stop();
// t1 has stopped running

View File

@@ -3,6 +3,19 @@ Changelog for package rclcpp_action
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
23.0.0 (2023-09-08)
-------------------
* Update API docs links in package READMEs (`#2302 <https://github.com/ros2/rclcpp/issues/2302>`_)
* fix(ClientGoalHandle): Made mutex recursive to prevent deadlocks (`#2267 <https://github.com/ros2/rclcpp/issues/2267>`_)
* Contributors: Christophe Bedard, jmachowinski
22.2.0 (2023-09-07)
-------------------
* Correct the position of a comment. (`#2290 <https://github.com/ros2/rclcpp/issues/2290>`_)
* Fix a typo in a comment. (`#2283 <https://github.com/ros2/rclcpp/issues/2283>`_)
* doc fix: call `canceled` only after goal state is in canceling. (`#2266 <https://github.com/ros2/rclcpp/issues/2266>`_)
* Contributors: Chris Lalancette, Jiaqi Li, Tomoya Fujita
22.1.0 (2023-08-21)
-------------------

View File

@@ -2,7 +2,8 @@
Adds action APIs for C++.
Visit the [rclcpp_action API documentation](http://docs.ros2.org/latest/api/rclcpp_action/) for a complete list of its main components and features. For more information about Actions in ROS 2, see the [design document](http://design.ros2.org/articles/actions.html).
The link to the latest rclcpp_action API documentation, which includes a complete list of its main components and features, can be found on the rclcpp_action package info page, at the [ROS Index](https://index.ros.org/p/rclcpp_action/).
For more information about Actions in ROS 2, see the [design document](http://design.ros2.org/articles/actions.html).
## Quality Declaration

View File

@@ -163,7 +163,7 @@ private:
ResultCallback result_callback_{nullptr};
int8_t status_{GoalStatus::STATUS_ACCEPTED};
std::mutex handle_mutex_;
std::recursive_mutex handle_mutex_;
};
} // namespace rclcpp_action

View File

@@ -59,7 +59,7 @@ template<typename ActionT>
std::shared_future<typename ClientGoalHandle<ActionT>::WrappedResult>
ClientGoalHandle<ActionT>::async_get_result()
{
std::lock_guard<std::mutex> guard(handle_mutex_);
std::lock_guard<std::recursive_mutex> guard(handle_mutex_);
if (!is_result_aware_) {
throw exceptions::UnawareGoalHandleError();
}
@@ -70,7 +70,7 @@ template<typename ActionT>
void
ClientGoalHandle<ActionT>::set_result(const WrappedResult & wrapped_result)
{
std::lock_guard<std::mutex> guard(handle_mutex_);
std::lock_guard<std::recursive_mutex> guard(handle_mutex_);
status_ = static_cast<int8_t>(wrapped_result.code);
result_promise_.set_value(wrapped_result);
if (result_callback_) {
@@ -82,7 +82,7 @@ template<typename ActionT>
void
ClientGoalHandle<ActionT>::set_feedback_callback(FeedbackCallback callback)
{
std::lock_guard<std::mutex> guard(handle_mutex_);
std::lock_guard<std::recursive_mutex> guard(handle_mutex_);
feedback_callback_ = callback;
}
@@ -90,7 +90,7 @@ template<typename ActionT>
void
ClientGoalHandle<ActionT>::set_result_callback(ResultCallback callback)
{
std::lock_guard<std::mutex> guard(handle_mutex_);
std::lock_guard<std::recursive_mutex> guard(handle_mutex_);
result_callback_ = callback;
}
@@ -98,7 +98,7 @@ template<typename ActionT>
int8_t
ClientGoalHandle<ActionT>::get_status()
{
std::lock_guard<std::mutex> guard(handle_mutex_);
std::lock_guard<std::recursive_mutex> guard(handle_mutex_);
return status_;
}
@@ -106,7 +106,7 @@ template<typename ActionT>
void
ClientGoalHandle<ActionT>::set_status(int8_t status)
{
std::lock_guard<std::mutex> guard(handle_mutex_);
std::lock_guard<std::recursive_mutex> guard(handle_mutex_);
status_ = status;
}
@@ -114,7 +114,7 @@ template<typename ActionT>
bool
ClientGoalHandle<ActionT>::is_feedback_aware()
{
std::lock_guard<std::mutex> guard(handle_mutex_);
std::lock_guard<std::recursive_mutex> guard(handle_mutex_);
return feedback_callback_ != nullptr;
}
@@ -122,7 +122,7 @@ template<typename ActionT>
bool
ClientGoalHandle<ActionT>::is_result_aware()
{
std::lock_guard<std::mutex> guard(handle_mutex_);
std::lock_guard<std::recursive_mutex> guard(handle_mutex_);
return is_result_aware_;
}
@@ -130,7 +130,7 @@ template<typename ActionT>
bool
ClientGoalHandle<ActionT>::set_result_awareness(bool awareness)
{
std::lock_guard<std::mutex> guard(handle_mutex_);
std::lock_guard<std::recursive_mutex> guard(handle_mutex_);
bool previous = is_result_aware_;
is_result_aware_ = awareness;
return previous;
@@ -140,7 +140,7 @@ template<typename ActionT>
void
ClientGoalHandle<ActionT>::invalidate(const exceptions::UnawareGoalHandleError & ex)
{
std::lock_guard<std::mutex> guard(handle_mutex_);
std::lock_guard<std::recursive_mutex> guard(handle_mutex_);
// Guard against multiple calls
if (is_invalidated()) {
return;
@@ -168,7 +168,7 @@ ClientGoalHandle<ActionT>::call_feedback_callback(
RCLCPP_ERROR(rclcpp::get_logger("rclcpp_action"), "Sent feedback to wrong goal handle.");
return;
}
std::lock_guard<std::mutex> guard(handle_mutex_);
std::lock_guard<std::recursive_mutex> guard(handle_mutex_);
if (nullptr == feedback_callback_) {
// Normal, some feedback messages may arrive after the goal result.
RCLCPP_DEBUG(rclcpp::get_logger("rclcpp_action"), "Received feedback but goal ignores it.");

View File

@@ -128,7 +128,7 @@ class Server;
* accepted.
* A `Server` will create an instance and give it to the user in their `handle_accepted` callback.
*
* Internally, this class is responsible for coverting between the C++ action type and generic
* Internally, this class is responsible for converting between the C++ action type and generic
* types for `rclcpp_action::ServerGoalHandleBase`.
*/
template<typename ActionT>
@@ -196,7 +196,7 @@ public:
/// Indicate that a goal has been canceled.
/**
* Only call this if the goal is executing or pending, but has been canceled.
* Only call this if the goal is canceling.
* This is a terminal state, no more methods should be called on a goal handle after this is
* called.
*

View File

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

View File

@@ -163,7 +163,6 @@ bool
ClientBase::wait_for_action_server_nanoseconds(std::chrono::nanoseconds timeout)
{
auto start = std::chrono::steady_clock::now();
// make an event to reuse, rather than create a new one each time
auto node_ptr = pimpl_->node_graph_.lock();
if (!node_ptr) {
throw rclcpp::exceptions::InvalidNodeError();
@@ -172,6 +171,7 @@ ClientBase::wait_for_action_server_nanoseconds(std::chrono::nanoseconds timeout)
if (this->action_server_is_ready()) {
return true;
}
// make an event to reuse, rather than create a new one each time
auto event = node_ptr->get_graph_event();
if (timeout == std::chrono::nanoseconds(0)) {
// check was non-blocking, return immediately

View File

@@ -852,6 +852,86 @@ TEST_F(TestClientAgainstServer, async_cancel_some_goals_with_callback)
EXPECT_EQ(rclcpp_action::GoalStatus::STATUS_CANCELED, goal_handle0->get_status());
}
TEST_F(TestClientAgainstServer, deadlock_in_callbacks)
{
std::atomic<bool> feedback_callback_called = false;
std::atomic<bool> response_callback_called = false;
std::atomic<bool> result_callback_called = false;
std::atomic<bool> no_deadlock = false;
std::thread tr = std::thread(
[&]() {
auto action_client = rclcpp_action::create_client<ActionType>(client_node, action_name);
ASSERT_TRUE(action_client->wait_for_action_server(WAIT_FOR_SERVER_TIMEOUT));
ActionGoal goal;
using GoalHandle = rclcpp_action::ClientGoalHandle<ActionType>;
rclcpp_action::Client<ActionType>::SendGoalOptions ops;
ops.feedback_callback =
[&feedback_callback_called](const GoalHandle::SharedPtr handle,
ActionType::Feedback::ConstSharedPtr) {
// call functions on the handle that acquire the lock
handle->get_status();
handle->is_feedback_aware();
handle->is_result_aware();
feedback_callback_called = true;
};
ops.goal_response_callback = [&response_callback_called](
const GoalHandle::SharedPtr & handle) {
// call functions on the handle that acquire the lock
handle->get_status();
handle->is_feedback_aware();
handle->is_result_aware();
response_callback_called = true;
};
ops.result_callback = [&result_callback_called](
const GoalHandle::WrappedResult &) {
result_callback_called = true;
};
goal.order = 6;
auto future_goal_handle = action_client->async_send_goal(goal, ops);
dual_spin_until_future_complete(future_goal_handle);
auto goal_handle = future_goal_handle.get();
ASSERT_TRUE(goal_handle);
ASSERT_EQ(RCL_RET_OK, rcl_set_ros_time_override(clock.get_clock_handle(), RCL_S_TO_NS(2)));
auto result_future = action_client->async_get_result(goal_handle);
dual_spin_until_future_complete(result_future);
EXPECT_TRUE(result_future.valid());
auto result = result_future.get();
no_deadlock = true;
});
auto start_time = std::chrono::system_clock::now();
while (std::chrono::system_clock::now() - start_time < std::chrono::milliseconds(2000) &&
!no_deadlock)
{
std::this_thread::sleep_for(std::chrono::milliseconds(10));
}
if (no_deadlock) {
tr.join();
} else {
// In case of a failure, the thread is assumed to be in a deadlock.
// We detach the thread so we don't block further tests.
tr.detach();
}
EXPECT_TRUE(no_deadlock);
EXPECT_TRUE(response_callback_called);
EXPECT_TRUE(result_callback_called);
EXPECT_TRUE(feedback_callback_called);
}
TEST_F(TestClientAgainstServer, send_rcl_errors)
{
auto action_client = rclcpp_action::create_client<ActionType>(client_node, action_name);

View File

@@ -2,6 +2,14 @@
Changelog for package rclcpp_components
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
23.0.0 (2023-09-08)
-------------------
* Update API docs links in package READMEs (`#2302 <https://github.com/ros2/rclcpp/issues/2302>`_)
* Contributors: Christophe Bedard
22.2.0 (2023-09-07)
-------------------
22.1.0 (2023-08-21)
-------------------

View File

@@ -2,7 +2,7 @@
Package containing tools for dynamically loadable components.
Visit the [rclcpp_components API documentation](http://docs.ros2.org/latest/api/rclcpp_components/) for a complete list of its main components and features.
The link to the latest rclcpp_components API documentation, which includes a complete list of its main components and features, can be found on the rclcpp_components package info page, at the [ROS Index](https://index.ros.org/p/rclcpp_components/).
## Quality Declaration

View File

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

View File

@@ -3,6 +3,16 @@ Changelog for package rclcpp_lifecycle
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
23.0.0 (2023-09-08)
-------------------
* Update API docs links in package READMEs (`#2302 <https://github.com/ros2/rclcpp/issues/2302>`_)
* Contributors: Christophe Bedard
22.2.0 (2023-09-07)
-------------------
* add logger level service to lifecycle node. (`#2277 <https://github.com/ros2/rclcpp/issues/2277>`_)
* Contributors: Tomoya Fujita
22.1.0 (2023-08-21)
-------------------
* Stop using constref signature of benchmark DoNotOptimize. (`#2238 <https://github.com/ros2/rclcpp/issues/2238>`_)

View File

@@ -2,7 +2,8 @@
Package containing a prototype for lifecycle implementation.
Visit the [rclcpp_lifecycle API documentation](http://docs.ros2.org/latest/api/rclcpp_lifecycle/) for a complete list of its main components and features. For more information about LifeCycle in ROS 2, see the [design document](http://design.ros2.org/articles/node_lifecycle.html).
The link to the latest rclcpp_lifecycle API documentation, which includes a complete list of its main components and features, can be found on the rclcpp_lifecycle package info page, at the [ROS Index](https://index.ros.org/p/rclcpp_lifecycle/).
For more information about LifeCycle in ROS 2, see the [design document](http://design.ros2.org/articles/node_lifecycle.html).
## Quality Declaration

View File

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

View File

@@ -144,6 +144,10 @@ LifecycleNode::LifecycleNode(
&LifecycleNodeInterface::on_deactivate, this,
std::placeholders::_1));
register_on_error(std::bind(&LifecycleNodeInterface::on_error, this, std::placeholders::_1));
if (options.enable_logger_service()) {
node_logging_->create_logger_services(node_services_);
}
}
LifecycleNode::~LifecycleNode()

View File

@@ -25,6 +25,8 @@
#include "lifecycle_msgs/msg/transition.hpp"
#include "rcl_lifecycle/rcl_lifecycle.h"
#include "rcl_interfaces/srv/get_logger_levels.hpp"
#include "rcl_interfaces/srv/set_logger_levels.hpp"
#include "rclcpp/rclcpp.hpp"
#include "rclcpp_lifecycle/lifecycle_node.hpp"
@@ -34,6 +36,8 @@
using lifecycle_msgs::msg::State;
using lifecycle_msgs::msg::Transition;
using namespace std::chrono_literals;
static const std::chrono::nanoseconds DEFAULT_EVENT_TIMEOUT = std::chrono::seconds(3);
static const std::chrono::nanoseconds DEFAULT_EVENT_SLEEP_PERIOD = std::chrono::milliseconds(100);
@@ -249,6 +253,35 @@ TEST_F(TestDefaultStateMachine, empty_initializer_rcl_errors) {
}
}
TEST_F(TestDefaultStateMachine, check_logger_services_exist) {
// Logger level services are disabled
{
rclcpp::NodeOptions options = rclcpp::NodeOptions();
options.enable_logger_service(false);
auto node = std::make_shared<rclcpp_lifecycle::LifecycleNode>(
"test_logger_service", "/test", options);
auto get_client = node->create_client<rcl_interfaces::srv::GetLoggerLevels>(
"/test/test_logger_service/get_logger_levels");
ASSERT_FALSE(get_client->wait_for_service(2s));
auto set_client = node->create_client<rcl_interfaces::srv::SetLoggerLevels>(
"/test/test_logger_service/set_logger_levels");
ASSERT_FALSE(set_client->wait_for_service(2s));
}
// Logger level services are enabled
{
rclcpp::NodeOptions options = rclcpp::NodeOptions();
options.enable_logger_service(true);
auto node = std::make_shared<rclcpp_lifecycle::LifecycleNode>(
"test_logger_service", "/test", options);
auto get_client = node->create_client<rcl_interfaces::srv::GetLoggerLevels>(
"/test/test_logger_service/get_logger_levels");
ASSERT_TRUE(get_client->wait_for_service(2s));
auto set_client = node->create_client<rcl_interfaces::srv::SetLoggerLevels>(
"/test/test_logger_service/set_logger_levels");
ASSERT_TRUE(set_client->wait_for_service(2s));
}
}
TEST_F(TestDefaultStateMachine, trigger_transition) {
auto test_node = std::make_shared<EmptyLifecycleNode>("testnode");