Compare commits

...

20 Commits

Author SHA1 Message Date
Marco A. Gutierrez
985e320de3 28.1.6
Signed-off-by: Marco A. Gutierrez <marcogg@marcogg.com>
2024-12-18 06:28:23 +00:00
Marco A. Gutierrez
eac2e8a7dd Changelog.
Signed-off-by: Marco A. Gutierrez <marcogg@marcogg.com>
2024-12-18 06:27:18 +00:00
mergify[bot]
cff2711aba apply actual QoS from rmw to the IPC publisher. (#2707) (#2712)
* apply actual QoS from rmw to the IPC publisher.

Signed-off-by: Tomoya Fujita <Tomoya.Fujita@sony.com>

* address uncrustify warning.

Signed-off-by: Tomoya Fujita <Tomoya.Fujita@sony.com>

---------

Signed-off-by: Tomoya Fujita <Tomoya.Fujita@sony.com>
(cherry picked from commit 016cfeac99)

Co-authored-by: Tomoya Fujita <Tomoya.Fujita@sony.com>
2024-12-16 17:27:51 -08:00
mergify[bot]
608e2f2e56 Adding in topic name to logging on IPC issues (#2706) (#2710)
* Adding in topic name to logging on IPC issues

Signed-off-by: Steve Macenski <stevenmacenski@gmail.com>

* Update test matching output logging

Signed-off-by: Steve Macenski <stevenmacenski@gmail.com>

* adding in single quotes

Signed-off-by: Steve Macenski <stevenmacenski@gmail.com>

---------

Signed-off-by: Steve Macenski <stevenmacenski@gmail.com>
(cherry picked from commit a13e16e2cb)

Co-authored-by: Steve Macenski <stevenmacenski@gmail.com>
2024-12-14 15:16:19 -08:00
Tomoya Fujita
bd695f4a3b enable testRaceConditionAddNode for rmw_connextdds. (#2698)
Signed-off-by: Tomoya Fujita <Tomoya.Fujita@sony.com>
2024-12-04 12:45:09 -08:00
mergify[bot]
74162b19bf Re-enable executor test on rmw_connextdds. (#2693) (#2695)
It supports the events executor now, so re-enable the test.

Signed-off-by: Chris Lalancette <clalancette@gmail.com>
(cherry picked from commit d7245365ed)

Co-authored-by: Chris Lalancette <clalancette@gmail.com>
2024-12-04 08:32:48 -08:00
mergify[bot]
e217532817 Fix warnings on Windows. (backport #2692) (#2694)
* Fix warnings on Windows. (#2692)

For reasons I admit I do not understand, the deprecation
warnings for StaticSingleThreadedExecutor on Windows
happen when we construct a shared_ptr for it in the tests.
If we construct a regular object, then it is fine.  Luckily
this test does not require a shared_ptr, so just make it
a regular object here, which rixes the warning.

While we are in here, make all of the tests camel case to
be consistent.

Signed-off-by: Chris Lalancette <clalancette@gmail.com>
(cherry picked from commit 3310f9eaed)

# Conflicts:
#	rclcpp/test/rclcpp/executors/test_executors.cpp

* resolve backport conflict.

Signed-off-by: Tomoya Fujita <Tomoya.Fujita@sony.com>

---------

Signed-off-by: Tomoya Fujita <Tomoya.Fujita@sony.com>
Co-authored-by: Chris Lalancette <clalancette@gmail.com>
Co-authored-by: Tomoya Fujita <Tomoya.Fujita@sony.com>
2024-12-04 09:50:19 +01:00
mergify[bot]
a7b8ada6a7 Omnibus fixes for running tests with Connext. (backport #2684) (#2690)
* Omnibus fixes for running tests with Connext. (#2684)

* Omnibus fixes for running tests with Connext.

When running the tests with RTI Connext as the default
RMW, some of the tests are failing.  There are three
different failures fixed here:

1.  Setting the liveliness duration to a value smaller than
a microsecond causes Connext to throw an error.  Set it to
a millisecond.

2.  Using the SystemDefaultsQoS sets the QoS to KEEP_LAST 1.
Connext is somewhat slow in this regard, so it can be the case
that we are overwriting a previous service introspection event
with the next one.  Switch to the ServicesDefaultQoS in the test,
which ensures we will not lose events.

3.  Connext is slow to match publishers and subscriptions.  Thus,
when creating a subscription "on-the-fly", we should wait for the
publisher to match it before expecting the subscription to actually
receive data from it.

With these fixes in place, the test_client_common, test_generic_service,
test_service_introspection, and test_executors tests all pass for
me with rmw_connextdds.

Signed-off-by: Chris Lalancette <clalancette@gmail.com>

* Fixes for executors.

Signed-off-by: Chris Lalancette <clalancette@gmail.com>

* One more fix for services.

Signed-off-by: Chris Lalancette <clalancette@gmail.com>

* More fixes for service_introspection.

Signed-off-by: Chris Lalancette <clalancette@gmail.com>

* More fixes for introspection.

Signed-off-by: Chris Lalancette <clalancette@gmail.com>

---------

Signed-off-by: Chris Lalancette <clalancette@gmail.com>
(cherry picked from commit 9984197c29)

# Conflicts:
#	rclcpp/test/rclcpp/executors/test_executors.cpp
#	rclcpp/test/rclcpp/test_generic_service.cpp

* address backport merge conflicts.

Signed-off-by: Tomoya Fujita <Tomoya.Fujita@sony.com>

---------

Signed-off-by: Tomoya Fujita <Tomoya.Fujita@sony.com>
Co-authored-by: Chris Lalancette <clalancette@gmail.com>
Co-authored-by: Tomoya Fujita <Tomoya.Fujita@sony.com>
2024-12-03 15:04:57 -08:00
jmachowinski
df09c6e77d fix(Executor): Fix segfault if callback group is deleted during rmw_wait (#2682)
Signed-off-by: Janosch Machowinski <J.Machowinski@cellumation.com>
2024-12-01 12:31:09 -08:00
mergify[bot]
06ae92ae96 Remove CODEOWNERS and the rolling-to-master job. (#2686) (#2687)
They are both legacy from times past, and are both
no longer serving their intended purposes.  Just remove them.

Signed-off-by: Chris Lalancette <clalancette@gmail.com>
(cherry picked from commit e64627004f)

Co-authored-by: Chris Lalancette <clalancette@gmail.com>
2024-11-27 10:45:36 +01:00
mergify[bot]
99b3803ce2 Fix NodeOptions assignment operator (#2656) (#2660)
* Fix NodeOptions assignment operator

Also copy the enable_logger_service_ member during the assignment operation

* Add more checks for NodeOptions copy test

* Set non default values by avoiding the copy-assignement

Signed-off-by: Romain DESILLE <r.desille@gmail.com>
Co-authored-by: Christophe Bedard <bedard.christophe@gmail.com>
(cherry picked from commit 9b654942f9)

Co-authored-by: Romain DESILLE <r.desille@gmail.com>
2024-11-07 09:47:55 -08:00
Cristóbal Arroyo
bbe2b99893 set QoS History KEEP_ALL explicitly for statistics publisher. (#2650) (#2657)
* set QoS History KEEP_ALL explicitly for statistics publisher.

* test_subscription_options adjustment.

Signed-off-by: Tomoya Fujita <Tomoya.Fujita@sony.com>
Co-authored-by: Tomoya Fujita <Tomoya.Fujita@sony.com>
2024-10-30 12:57:18 -04:00
Marco A. Gutierrez
09e5dd70f4 28.1.5 2024-09-19 17:15:53 +00:00
Marco A. Gutierrez
5551facd9b Changelog.
Signed-off-by: Marco A. Gutierrez <marcogg@marcogg.com>
2024-09-19 17:14:46 +00:00
Alberto Soragna
38ed9ed172 backport fix events-executor warm-up bug and add unit-tests (#2591) (#2628)
Signed-off-by: Alberto Soragna <alberto.soragna@gmail.com>
2024-09-17 13:38:52 +02:00
Marco A. Gutierrez
fad351ff85 28.1.4
Signed-off-by: Marco A. Gutierrez <marcogg@marcogg.com>
2024-09-06 19:00:36 +00:00
Marco A. Gutierrez
08c6cd9f9a Changelog.
Signed-off-by: Marco A. Gutierrez <marcogg@marcogg.com>
2024-09-06 18:59:53 +00:00
mergify[bot]
830a1f0212 Split test_executors.cpp even further. (#2572) (#2619)
That's because it is too large for Windows Debug to compile,
so split into smaller bits.

Even with this split, the file is too big; that's likely
because we are using TYPED_TEST here, which generates multiple
symbols per test case.  To deal with this, without further
breaking up the file, also add in the /bigobj flag when
compiling on Windows Debug.

Signed-off-by: Chris Lalancette <clalancette@gmail.com>
(cherry picked from commit c743c173e6)

Co-authored-by: Chris Lalancette <clalancette@gmail.com>
2024-09-06 10:28:31 -04:00
mergify[bot]
1b4485443d Correct node name in service test code (#2615) (#2616)
Signed-off-by: Barry Xu <barry.xu@sony.com>
(cherry picked from commit e846f56224)

Co-authored-by: Barry Xu <barry.xu@sony.com>
2024-09-03 09:14:43 +02:00
mergify[bot]
d73fc2a86a Release ownership of entities after spinning cancelled (backport #2556) (#2580)
* Release ownership of entities after spinning cancelled (#2556)

* Release ownership of entities after spinning cancelled

Signed-off-by: Barry Xu <barry.xu@sony.com>

* Move release action to every exit point in different spin functions

Signed-off-by: Barry Xu <barry.xu@sony.com>

* Move wait_result_.reset() before setting spinning to false

Signed-off-by: Barry Xu <barry.xu@sony.com>

* Update test code

Signed-off-by: Barry Xu <barry.xu@sony.com>

* Move test code to test_executors.cpp

Signed-off-by: Barry Xu <barry.xu@sony.com>

---------

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

# Conflicts:
#	rclcpp/test/rclcpp/executors/test_executors.cpp

* Fix backport issue (#2581)

Signed-off-by: Barry Xu <barry.xu@sony.com>

---------

Signed-off-by: Barry Xu <barry.xu@sony.com>
Co-authored-by: Barry Xu <barry.xu@sony.com>
2024-07-18 12:43:49 -07:00
32 changed files with 1095 additions and 318 deletions

View File

@@ -1,13 +0,0 @@
name: Mirror rolling to master
on:
push:
branches: [ rolling ]
jobs:
mirror-to-master:
runs-on: ubuntu-latest
steps:
- uses: zofrex/mirror-branch@v1
with:
target-branch: master

View File

@@ -1,2 +0,0 @@
# This file was generated by https://github.com/audrow/update-ros2-repos
* @ivanpauno @hidmic @wjwwood

View File

@@ -2,6 +2,128 @@
Changelog for package rclcpp
^^^^^^^^^^^^^^^^^^^^^^^^^^^^
28.1.6 (2024-12-18)
-------------------
* apply actual QoS from rmw to the IPC publisher. (`#2707 <https://github.com/ros2/rclcpp/issues/2707>`_) (`#2712 <https://github.com/ros2/rclcpp/issues/2712>`_)
* apply actual QoS from rmw to the IPC publisher.
* address uncrustify warning.
---------
(cherry picked from commit 016cfeac99e4b67f58abdf247e57f05b85c09ec4)
Co-authored-by: Tomoya Fujita <Tomoya.Fujita@sony.com>
* Adding in topic name to logging on IPC issues (`#2706 <https://github.com/ros2/rclcpp/issues/2706>`_) (`#2710 <https://github.com/ros2/rclcpp/issues/2710>`_)
* Adding in topic name to logging on IPC issues
* Update test matching output logging
* adding in single quotes
---------
(cherry picked from commit a13e16e2cbaeacb14ff31272d01cbb21bd8ac037)
Co-authored-by: Steve Macenski <stevenmacenski@gmail.com>
* enable testRaceConditionAddNode for rmw_connextdds. (`#2698 <https://github.com/ros2/rclcpp/issues/2698>`_)
* Re-enable executor test on rmw_connextdds. (`#2693 <https://github.com/ros2/rclcpp/issues/2693>`_) (`#2695 <https://github.com/ros2/rclcpp/issues/2695>`_)
It supports the events executor now, so re-enable the test.
(cherry picked from commit d7245365ed867db9b309ed3efbfb0391bda09bd5)
Co-authored-by: Chris Lalancette <clalancette@gmail.com>
* Fix warnings on Windows. (backport `#2692 <https://github.com/ros2/rclcpp/issues/2692>`_) (`#2694 <https://github.com/ros2/rclcpp/issues/2694>`_)
* Fix warnings on Windows. (`#2692 <https://github.com/ros2/rclcpp/issues/2692>`_)
For reasons I admit I do not understand, the deprecation
warnings for StaticSingleThreadedExecutor on Windows
happen when we construct a shared_ptr for it in the tests.
If we construct a regular object, then it is fine. Luckily
this test does not require a shared_ptr, so just make it
a regular object here, which rixes the warning.
While we are in here, make all of the tests camel case to
be consistent.
(cherry picked from commit 3310f9eaed967e0c18d17bb2f82d2def838bb7a5)
# Conflicts:
# rclcpp/test/rclcpp/executors/test_executors.cpp
* resolve backport conflict.
---------
Co-authored-by: Chris Lalancette <clalancette@gmail.com>
Co-authored-by: Tomoya Fujita <Tomoya.Fujita@sony.com>
* Omnibus fixes for running tests with Connext. (backport `#2684 <https://github.com/ros2/rclcpp/issues/2684>`_) (`#2690 <https://github.com/ros2/rclcpp/issues/2690>`_)
* Omnibus fixes for running tests with Connext. (`#2684 <https://github.com/ros2/rclcpp/issues/2684>`_)
* Omnibus fixes for running tests with Connext.
When running the tests with RTI Connext as the default
RMW, some of the tests are failing. There are three
different failures fixed here:
1. Setting the liveliness duration to a value smaller than
a microsecond causes Connext to throw an error. Set it to
a millisecond.
2. Using the SystemDefaultsQoS sets the QoS to KEEP_LAST 1.
Connext is somewhat slow in this regard, so it can be the case
that we are overwriting a previous service introspection event
with the next one. Switch to the ServicesDefaultQoS in the test,
which ensures we will not lose events.
3. Connext is slow to match publishers and subscriptions. Thus,
when creating a subscription "on-the-fly", we should wait for the
publisher to match it before expecting the subscription to actually
receive data from it.
With these fixes in place, the test_client_common, test_generic_service,
test_service_introspection, and test_executors tests all pass for
me with rmw_connextdds.
* Fixes for executors.
* One more fix for services.
* More fixes for service_introspection.
* More fixes for introspection.
---------
(cherry picked from commit 9984197c292d6c5ae0e7661aaea245ffb0fea057)
# Conflicts:
# rclcpp/test/rclcpp/executors/test_executors.cpp
# rclcpp/test/rclcpp/test_generic_service.cpp
* address backport merge conflicts.
---------
Co-authored-by: Chris Lalancette <clalancette@gmail.com>
Co-authored-by: Tomoya Fujita <Tomoya.Fujita@sony.com>
* fix(Executor): Fix segfault if callback group is deleted during rmw_wait (`#2682 <https://github.com/ros2/rclcpp/issues/2682>`_)
* Fix NodeOptions assignment operator (`#2656 <https://github.com/ros2/rclcpp/issues/2656>`_) (`#2660 <https://github.com/ros2/rclcpp/issues/2660>`_)
* Fix NodeOptions assignment operator
Also copy the enable_logger_service\_ member during the assignment operation
* Add more checks for NodeOptions copy test
* Set non default values by avoiding the copy-assignement
Co-authored-by: Christophe Bedard <bedard.christophe@gmail.com>
(cherry picked from commit 9b654942f99f17850e0e95480958abdbb508bc00)
Co-authored-by: Romain DESILLE <r.desille@gmail.com>
* set QoS History KEEP_ALL explicitly for statistics publisher. (`#2650 <https://github.com/ros2/rclcpp/issues/2650>`_) (`#2657 <https://github.com/ros2/rclcpp/issues/2657>`_)
* set QoS History KEEP_ALL explicitly for statistics publisher.
* test_subscription_options adjustment.
Co-authored-by: Tomoya Fujita <Tomoya.Fujita@sony.com>
* Contributors: Cristóbal Arroyo, Tomoya Fujita, jmachowinski, mergify[bot]
28.1.5 (2024-09-19)
-------------------
* backport fix events-executor warm-up bug and add unit-tests (`#2591 <https://github.com/ros2/rclcpp/issues/2591>`_) (`#2628 <https://github.com/ros2/rclcpp/issues/2628>`_)
* Contributors: Alberto Soragna
28.1.4 (2024-09-06)
-------------------
* Split test_executors.cpp even further. (`#2572 <https://github.com/ros2/rclcpp/issues/2572>`_) (`#2619 <https://github.com/ros2/rclcpp/issues/2619>`_)
That's because it is too large for Windows Debug to compile,
so split into smaller bits.
Even with this split, the file is too big; that's likely
because we are using TYPED_TEST here, which generates multiple
symbols per test case. To deal with this, without further
breaking up the file, also add in the /bigobj flag when
compiling on Windows Debug.
(cherry picked from commit c743c173e68d92af872cf163e10721a8dbe51dd0)
Co-authored-by: Chris Lalancette <clalancette@gmail.com>
* Correct node name in service test code (`#2615 <https://github.com/ros2/rclcpp/issues/2615>`_) (`#2616 <https://github.com/ros2/rclcpp/issues/2616>`_)
(cherry picked from commit e846f56224a39b93f1c609e7ee03fff0662b7453)
Co-authored-by: Barry Xu <barry.xu@sony.com>
* Release ownership of entities after spinning cancelled (backport `#2556 <https://github.com/ros2/rclcpp/issues/2556>`_) (`#2580 <https://github.com/ros2/rclcpp/issues/2580>`_)
* Release ownership of entities after spinning cancelled (`#2556 <https://github.com/ros2/rclcpp/issues/2556>`_)
* Release ownership of entities after spinning cancelled
* Move release action to every exit point in different spin functions
* Move wait_result\_.reset() before setting spinning to false
* Update test code
* Move test code to test_executors.cpp
---------
(cherry picked from commit 069a0018935b33a14632a1cdf4074984a1cf80fe)
# Conflicts:
# rclcpp/test/rclcpp/executors/test_executors.cpp
* Fix backport issue (`#2581 <https://github.com/ros2/rclcpp/issues/2581>`_)
---------
Co-authored-by: Barry Xu <barry.xu@sony.com>
* Contributors: mergify[bot]
28.1.3 (2024-06-27)
-------------------
* Add test creating two content filter topics with the same topic name (`#2546 <https://github.com/ros2/rclcpp/issues/2546>`_) (`#2549 <https://github.com/ros2/rclcpp/issues/2549>`_) (`#2552 <https://github.com/ros2/rclcpp/issues/2552>`_)

View File

@@ -162,8 +162,7 @@ public:
const rclcpp::QoS & qos,
const rclcpp::PublisherOptionsWithAllocator<AllocatorT> & options)
{
// Topic is unused for now.
(void)topic;
(void)qos;
(void)options;
// If needed, setup intra process communication.
@@ -171,22 +170,26 @@ public:
auto context = node_base->get_context();
// Get the intra process manager instance for this context.
auto ipm = context->get_sub_context<rclcpp::experimental::IntraProcessManager>();
// 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 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) {
if (qos_profile.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::TransientLocal) {
if (qos_profile.durability() == rclcpp::DurabilityPolicy::TransientLocal) {
buffer_ = rclcpp::experimental::create_intra_process_buffer<
ROSMessageType, ROSMessageTypeAllocator, ROSMessageTypeDeleter>(
rclcpp::detail::resolve_intra_process_buffer_type(options_.intra_process_buffer_type),
qos,
qos_profile,
std::make_shared<ROSMessageTypeAllocator>(ros_message_type_allocator_));
}
// Register the publisher with the intra process manager.
uint64_t intra_process_publisher_id = ipm->add_publisher(this->shared_from_this(), buffer_);
this->setup_intra_process(
intra_process_publisher_id,

View File

@@ -159,11 +159,13 @@ public:
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");
}
using SubscriptionIntraProcessT = rclcpp::experimental::SubscriptionIntraProcess<

View File

@@ -80,7 +80,8 @@ struct SubscriptionOptionsBase
// An optional QoS which can provide topic_statistics with a stable QoS separate from
// the subscription's current QoS settings which could be unstable.
rclcpp::QoS qos = SystemDefaultsQoS();
// Explicitly set the enough depth to avoid missing the statistics messages.
rclcpp::QoS qos = SystemDefaultsQoS().keep_last(10);
};
TopicStatisticsOptions topic_stats_options;

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>28.1.3</version>
<version>28.1.6</version>
<description>The ROS client library in C++.</description>
<maintainer email="ivanpauno@ekumenlabs.com">Ivan Paunovic</maintainer>

View File

@@ -275,7 +275,7 @@ Executor::spin_until_future_complete_impl(
if (spinning.exchange(true)) {
throw std::runtime_error("spin_until_future_complete() called while already spinning");
}
RCPPUTILS_SCOPE_EXIT(this->spinning.store(false); );
RCPPUTILS_SCOPE_EXIT(wait_result_.reset();this->spinning.store(false););
while (rclcpp::ok(this->context_) && spinning.load()) {
// Do one item of work.
spin_once_impl(timeout_left);
@@ -364,7 +364,7 @@ Executor::spin_some_impl(std::chrono::nanoseconds max_duration, bool exhaustive)
if (spinning.exchange(true)) {
throw std::runtime_error("spin_some() called while already spinning");
}
RCPPUTILS_SCOPE_EXIT(this->spinning.store(false); );
RCPPUTILS_SCOPE_EXIT(wait_result_.reset();this->spinning.store(false););
// clear the wait result and wait for work without blocking to collect the work
// for the first time
@@ -431,7 +431,7 @@ Executor::spin_once(std::chrono::nanoseconds timeout)
if (spinning.exchange(true)) {
throw std::runtime_error("spin_once() called while already spinning");
}
RCPPUTILS_SCOPE_EXIT(this->spinning.store(false); );
RCPPUTILS_SCOPE_EXIT(wait_result_.reset();this->spinning.store(false););
spin_once_impl(timeout);
}
@@ -729,13 +729,33 @@ Executor::wait_for_work(std::chrono::nanoseconds timeout)
// Clear any previous wait result
this->wait_result_.reset();
// we need to make sure that callback groups don't get out of scope
// during the wait. As in jazzy, they are not covered by the DynamicStorage,
// we explicitly hold them here as a bugfix
std::vector<rclcpp::CallbackGroup::SharedPtr> cbgs;
{
std::lock_guard<std::mutex> guard(mutex_);
if (this->entities_need_rebuild_.exchange(false) || current_collection_.empty()) {
this->collect_entities();
}
auto callback_groups = this->collector_.get_all_callback_groups();
cbgs.resize(callback_groups.size());
for(const auto & w_ptr : callback_groups) {
auto shr_ptr = w_ptr.lock();
if(shr_ptr) {
cbgs.push_back(std::move(shr_ptr));
}
}
}
this->wait_result_.emplace(wait_set_.wait(timeout));
// drop references to the callback groups, before trying to execute anything
cbgs.clear();
if (!this->wait_result_ || this->wait_result_->kind() == WaitResultKind::Empty) {
RCUTILS_LOG_WARN_NAMED(
"rclcpp",

View File

@@ -55,7 +55,7 @@ MultiThreadedExecutor::spin()
if (spinning.exchange(true)) {
throw std::runtime_error("spin() called while already spinning");
}
RCPPUTILS_SCOPE_EXIT(this->spinning.store(false););
RCPPUTILS_SCOPE_EXIT(wait_result_.reset();this->spinning.store(false););
std::vector<std::thread> threads;
size_t thread_id = 0;
{

View File

@@ -30,7 +30,7 @@ SingleThreadedExecutor::spin()
if (spinning.exchange(true)) {
throw std::runtime_error("spin() called while already spinning");
}
RCPPUTILS_SCOPE_EXIT(this->spinning.store(false); );
RCPPUTILS_SCOPE_EXIT(wait_result_.reset();this->spinning.store(false););
// Clear any previous result and rebuild the waitset
this->wait_result_.reset();

View File

@@ -110,10 +110,29 @@ StaticSingleThreadedExecutor::spin_once_impl(std::chrono::nanoseconds timeout)
std::optional<rclcpp::WaitResult<rclcpp::WaitSet>>
StaticSingleThreadedExecutor::collect_and_wait(std::chrono::nanoseconds timeout)
{
// we need to make sure that callback groups don't get out of scope
// during the wait. As in jazzy, they are not covered by the DynamicStorage,
// we explicitly hold them here as a bugfix
std::vector<rclcpp::CallbackGroup::SharedPtr> cbgs;
if (this->entities_need_rebuild_.exchange(false) || current_collection_.empty()) {
this->collect_entities();
}
auto callback_groups = this->collector_.get_all_callback_groups();
cbgs.resize(callback_groups.size());
for(const auto & w_ptr : callback_groups) {
auto shr_ptr = w_ptr.lock();
if(shr_ptr) {
cbgs.push_back(std::move(shr_ptr));
}
}
auto wait_result = wait_set_.wait(std::chrono::nanoseconds(timeout));
// drop references to the callback groups, before trying to execute anything
cbgs.clear();
if (wait_result.kind() == WaitResultKind::Empty) {
RCUTILS_LOG_WARN_NAMED(
"rclcpp",

View File

@@ -62,7 +62,6 @@ EventsExecutor::EventsExecutor(
// ---> we need to wake up the executor so that it can terminate
// - a node or callback group guard condition is triggered:
// ---> the entities collection is changed, we need to update callbacks
notify_waitable_event_pushed_ = false;
this->refresh_current_collection_from_callback_groups();
});
@@ -164,6 +163,14 @@ EventsExecutor::spin_some_impl(std::chrono::nanoseconds max_duration, bool exhau
return false;
};
// If this spin is not exhaustive (e.g. spin_some), we need to explicitly check
// if entities need to be rebuilt here rather than letting the notify waitable event do it.
// A non-exhaustive spin would not check for work a second time, thus delaying the execution
// of some entities to the next invocation of spin.
if (!exhaustive) {
this->refresh_current_collection_from_callback_groups();
}
// Get the number of events and timers ready at start
const size_t ready_events_at_start = events_queue_->size();
size_t executed_events = 0;
@@ -403,6 +410,16 @@ EventsExecutor::get_automatically_added_callback_groups_from_nodes()
void
EventsExecutor::refresh_current_collection_from_callback_groups()
{
// Do not rebuild if we don't need to.
// A rebuild event could be generated, but then
// this function could end up being called from somewhere else
// before that event gets processed, for example if
// a node or callback group is manually added to the executor.
const bool notify_waitable_triggered = notify_waitable_event_pushed_.exchange(false);
if (!notify_waitable_triggered && !this->entities_collector_->has_pending()) {
return;
}
// Build the new collection
this->entities_collector_->update_collections();
auto callback_groups = this->entities_collector_->get_all_callback_groups();

View File

@@ -80,6 +80,7 @@ NodeOptions::operator=(const NodeOptions & other)
this->clock_type_ = other.clock_type_;
this->clock_qos_ = other.clock_qos_;
this->use_clock_thread_ = other.use_clock_thread_;
this->enable_logger_service_ = other.enable_logger_service_;
this->parameter_event_qos_ = other.parameter_event_qos_;
this->rosout_qos_ = other.rosout_qos_;
this->parameter_event_publisher_options_ = other.parameter_event_publisher_options_;

View File

@@ -462,8 +462,12 @@ endif()
ament_add_gtest(
test_executors
executors/test_executors.cpp
executors/test_waitable.cpp
APPEND_LIBRARY_DIRS "${append_library_dirs}"
TIMEOUT 180)
if(CMAKE_BUILD_TYPE STREQUAL "Debug" AND MSVC)
target_compile_options(test_executors PRIVATE "/bigobj")
endif()
if(TARGET test_executors)
target_link_libraries(test_executors ${PROJECT_NAME} rcl::rcl ${test_msgs_TARGETS})
endif()
@@ -495,6 +499,26 @@ if(TARGET test_executors)
target_link_libraries(test_executors_intraprocess ${PROJECT_NAME} ${test_msgs_TARGETS})
endif()
ament_add_gtest(
test_executors_busy_waiting
executors/test_executors_busy_waiting.cpp
executors/test_waitable.cpp
APPEND_LIBRARY_DIRS "${append_library_dirs}"
TIMEOUT 180)
if(TARGET test_executors_busy_waiting)
target_link_libraries(test_executors_busy_waiting ${PROJECT_NAME})
endif()
ament_add_gtest(
test_executors_warmup
executors/test_executors_warmup.cpp
executors/test_waitable.cpp
APPEND_LIBRARY_DIRS "${append_library_dirs}"
TIMEOUT 180)
if(TARGET test_executors_warmup)
target_link_libraries(test_executors_warmup ${PROJECT_NAME} ${test_msgs_TARGETS})
endif()
ament_add_gtest(test_static_single_threaded_executor executors/test_static_single_threaded_executor.cpp
APPEND_LIBRARY_DIRS "${append_library_dirs}")
if(TARGET test_static_single_threaded_executor)
@@ -598,7 +622,7 @@ ament_add_gtest(test_executor test_executor.cpp
APPEND_LIBRARY_DIRS "${append_library_dirs}"
TIMEOUT 120)
if(TARGET test_executor)
target_link_libraries(test_executor ${PROJECT_NAME} mimick)
target_link_libraries(test_executor ${PROJECT_NAME} mimick ${test_msgs_TARGETS})
endif()
ament_add_gtest(test_graph_listener test_graph_listener.cpp)

View File

@@ -39,8 +39,10 @@
#include "rclcpp/time_source.hpp"
#include "test_msgs/msg/empty.hpp"
#include "test_msgs/srv/empty.hpp"
#include "./executor_types.hpp"
#include "./test_waitable.hpp"
using namespace std::chrono_literals;
@@ -331,114 +333,6 @@ TYPED_TEST(TestExecutors, testSpinUntilFutureCompleteWithTimeout)
spinner.join();
}
class TestWaitable : public rclcpp::Waitable
{
public:
TestWaitable() = default;
void
add_to_wait_set(rcl_wait_set_t & wait_set) override
{
if (trigger_count_ > 0) {
// Keep the gc triggered until the trigger count is reduced back to zero.
// This is necessary if trigger() results in the wait set waking, but not
// executing this waitable, in which case it needs to be re-triggered.
gc_.trigger();
}
rclcpp::detail::add_guard_condition_to_rcl_wait_set(wait_set, gc_);
}
void trigger()
{
trigger_count_++;
gc_.trigger();
}
bool
is_ready(const rcl_wait_set_t & wait_set) override
{
is_ready_count_++;
for (size_t i = 0; i < wait_set.size_of_guard_conditions; ++i) {
auto rcl_guard_condition = wait_set.guard_conditions[i];
if (&gc_.get_rcl_guard_condition() == rcl_guard_condition) {
return true;
}
}
return false;
}
std::shared_ptr<void>
take_data() override
{
return nullptr;
}
std::shared_ptr<void>
take_data_by_entity_id(size_t id) override
{
(void) id;
return nullptr;
}
void
execute(const std::shared_ptr<void> &) override
{
trigger_count_--;
count_++;
if (nullptr != on_execute_callback_) {
on_execute_callback_();
} else {
// TODO(wjwwood): I don't know why this was here, but probably it should
// not be there, or test cases where that is important should use the
// on_execute_callback?
std::this_thread::sleep_for(3ms);
}
}
void
set_on_execute_callback(std::function<void()> on_execute_callback)
{
on_execute_callback_ = on_execute_callback;
}
void
set_on_ready_callback(std::function<void(size_t, int)> callback) override
{
auto gc_callback = [callback](size_t count) {
callback(count, 0);
};
gc_.set_on_trigger_callback(gc_callback);
}
void
clear_on_ready_callback() override
{
gc_.set_on_trigger_callback(nullptr);
}
size_t
get_number_of_ready_guard_conditions() override {return 1;}
size_t
get_count() const
{
return count_;
}
size_t
get_is_ready_call_count() const
{
return is_ready_count_;
}
private:
std::atomic<size_t> trigger_count_ = 0;
std::atomic<size_t> is_ready_count_ = 0;
std::atomic<size_t> count_ = 0;
rclcpp::GuardCondition gc_;
std::function<void()> on_execute_callback_ = nullptr;
};
TYPED_TEST(TestExecutors, spinAll)
{
using ExecutorType = TypeParam;
@@ -494,7 +388,7 @@ to_nanoseconds_helper(DurationT duration)
// - works nominally (it can execute entities)
// - it can execute multiple items at once
// - it does not wait for work to be available before returning
TYPED_TEST(TestExecutors, spin_some)
TYPED_TEST(TestExecutors, spinSome)
{
using ExecutorType = TypeParam;
@@ -586,7 +480,7 @@ TYPED_TEST(TestExecutors, spin_some)
// The purpose of this test is to check that the ExecutorT.spin_some() method:
// - does not continue executing after max_duration has elapsed
TYPED_TEST(TestExecutors, spin_some_max_duration)
TYPED_TEST(TestExecutors, spinSomeMaxDuration)
{
using ExecutorType = TypeParam;
@@ -753,13 +647,6 @@ TYPED_TEST(TestExecutors, testSpinUntilFutureCompleteInterrupted)
TYPED_TEST(TestExecutors, testRaceConditionAddNode)
{
using ExecutorType = TypeParam;
// rmw_connextdds doesn't support events-executor
if (
std::is_same<ExecutorType, rclcpp::experimental::executors::EventsExecutor>() &&
std::string(rmw_get_implementation_identifier()).find("rmw_connextdds") == 0)
{
GTEST_SKIP();
}
// Spawn some threads to do some heavy work
std::atomic<bool> should_cancel = false;
@@ -878,154 +765,59 @@ TEST(TestExecutors, testSpinWithNonDefaultContext)
rclcpp::shutdown(non_default_context);
}
template<typename T>
class TestBusyWaiting : public ::testing::Test
TYPED_TEST(TestExecutors, releaseOwnershipEntityAfterSpinningCancel)
{
public:
void SetUp() override
{
rclcpp::init(0, nullptr);
using ExecutorType = TypeParam;
ExecutorType executor;
const auto test_info = ::testing::UnitTest::GetInstance()->current_test_info();
std::stringstream test_name;
test_name << test_info->test_case_name() << "_" << test_info->name();
node = std::make_shared<rclcpp::Node>("node", test_name.str());
callback_group = node->create_callback_group(
rclcpp::CallbackGroupType::MutuallyExclusive,
/* automatically_add_to_executor_with_node =*/ false);
auto future = std::async(std::launch::async, [&executor] {executor.spin();});
auto waitable_interfaces = node->get_node_waitables_interface();
waitable = std::make_shared<TestWaitable>();
waitable_interfaces->add_waitable(waitable, callback_group);
executor = std::make_shared<T>();
executor->add_callback_group(callback_group, node->get_node_base_interface());
auto node = std::make_shared<rclcpp::Node>("test_node");
auto callback = [](
const test_msgs::srv::Empty::Request::SharedPtr, test_msgs::srv::Empty::Response::SharedPtr) {
};
auto server = node->create_service<test_msgs::srv::Empty>("test_service", callback);
while (!executor.is_spinning()) {
std::this_thread::sleep_for(50ms);
}
executor.add_node(node);
std::this_thread::sleep_for(50ms);
executor.cancel();
std::future_status future_status = future.wait_for(1s);
EXPECT_EQ(future_status, std::future_status::ready);
void TearDown() override
{
rclcpp::shutdown();
}
void
set_up_and_trigger_waitable(std::function<void()> extra_callback = nullptr)
{
this->has_executed = false;
this->waitable->set_on_execute_callback([this, extra_callback]() {
if (!this->has_executed) {
// trigger once to see if the second trigger is handled or not
// this follow up trigger simulates new entities becoming ready while
// the executor is executing something else, e.g. subscription got data
// or a timer expired, etc.
// spin_some would not handle this second trigger, since it collects
// work only once, whereas spin_all should handle it since it
// collects work multiple times
this->waitable->trigger();
this->has_executed = true;
}
if (nullptr != extra_callback) {
extra_callback();
}
});
this->waitable->trigger();
}
void
check_for_busy_waits(std::chrono::steady_clock::time_point start_time)
{
// rough time based check, since the work to be done was very small it
// should be safe to check that we didn't use more than half the
// max duration, which itself is much larger than necessary
// however, it could still produce a false-positive
EXPECT_LT(
std::chrono::steady_clock::now() - start_time,
max_duration / 2)
<< "executor took a long time to execute when it should have done "
<< "nothing and should not have blocked either, but this could be a "
<< "false negative if the computer is really slow";
// this check is making some assumptions about the implementation of the
// executors, but it should be safe to say that a busy wait may result in
// hundreds or thousands of calls to is_ready(), but "normal" executor
// behavior should be within an order of magnitude of the number of
// times that the waitable was executed
ASSERT_LT(waitable->get_is_ready_call_count(), 10u * this->waitable->get_count());
}
static constexpr auto max_duration = 10s;
rclcpp::Node::SharedPtr node;
rclcpp::CallbackGroup::SharedPtr callback_group;
std::shared_ptr<TestWaitable> waitable;
std::chrono::steady_clock::time_point start_time;
std::shared_ptr<T> executor;
bool has_executed;
};
TYPED_TEST_SUITE(TestBusyWaiting, ExecutorTypes, ExecutorTypeNames);
TYPED_TEST(TestBusyWaiting, test_spin_all)
{
this->set_up_and_trigger_waitable();
auto start_time = std::chrono::steady_clock::now();
this->executor->spin_all(this->max_duration);
this->check_for_busy_waits(start_time);
// this should get the initial trigger, and the follow up from in the callback
ASSERT_EQ(this->waitable->get_count(), 2u);
EXPECT_EQ(server.use_count(), 1);
}
TYPED_TEST(TestBusyWaiting, test_spin_some)
TYPED_TEST(TestExecutors, testRaceDropCallbackGroupFromSecondThread)
{
this->set_up_and_trigger_waitable();
using ExecutorType = TypeParam;
auto start_time = std::chrono::steady_clock::now();
this->executor->spin_some(this->max_duration);
this->check_for_busy_waits(start_time);
// this should get the inital trigger, but not the follow up in the callback
ASSERT_EQ(this->waitable->get_count(), 1u);
}
// Create an executor
ExecutorType executor;
executor.add_node(this->node);
TYPED_TEST(TestBusyWaiting, test_spin)
{
std::condition_variable cv;
std::mutex cv_m;
bool first_check_passed = false;
this->set_up_and_trigger_waitable([&cv, &cv_m, &first_check_passed]() {
cv.notify_one();
if (!first_check_passed) {
std::unique_lock<std::mutex> lk(cv_m);
cv.wait_for(lk, 1s, [&]() {return first_check_passed;});
}
});
auto start_time = std::chrono::steady_clock::now();
std::thread t([this]() {
this->executor->spin();
// Start spinning
auto executor_thread = std::thread(
[&executor]() {
executor.spin();
});
// wait until thread has started (first execute of waitable)
{
std::unique_lock<std::mutex> lk(cv_m);
cv.wait_for(lk, 10s);
// As the problem is a race, we do this multiple times,
// to raise our chances of hitting the problem
for (size_t i = 0; i < 10; i++) {
auto cg = this->node->create_callback_group(
rclcpp::CallbackGroupType::MutuallyExclusive);
auto timer = this->node->create_timer(1s, [] {}, cg);
// sleep a bit, so that the spin thread can pick up the callback group
// and add it to the executor
std::this_thread::sleep_for(5ms);
// At this point the callbackgroup should be used within the waitset of the executor
// as we leave the scope, the reference to cg will be dropped.
// If the executor has a race, we will experience a segfault at this point.
}
EXPECT_GT(this->waitable->get_count(), 0u);
first_check_passed = true;
cv.notify_one();
// wait until the executor has finished (second execute of waitable)
{
std::unique_lock<std::mutex> lk(cv_m);
cv.wait_for(lk, 10s);
}
EXPECT_EQ(this->waitable->get_count(), 2u);
this->executor->cancel();
t.join();
this->check_for_busy_waits(start_time);
// this should get the initial trigger, and the follow up from in the callback
ASSERT_EQ(this->waitable->get_count(), 2u);
executor.cancel();
executor_thread.join();
}

View File

@@ -0,0 +1,181 @@
// Copyright 2024 Open Source Robotics Foundation, Inc.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
#include <gtest/gtest.h>
#include <chrono>
#include <functional>
#include <memory>
#include <mutex>
#include <sstream>
#include <thread>
#include "rclcpp/rclcpp.hpp"
#include "./executor_types.hpp"
#include "./test_waitable.hpp"
using namespace std::chrono_literals;
template<typename T>
class TestBusyWaiting : public ::testing::Test
{
public:
void SetUp() override
{
rclcpp::init(0, nullptr);
const auto test_info = ::testing::UnitTest::GetInstance()->current_test_info();
std::stringstream test_name;
test_name << test_info->test_case_name() << "_" << test_info->name();
node = std::make_shared<rclcpp::Node>("node", test_name.str());
callback_group = node->create_callback_group(
rclcpp::CallbackGroupType::MutuallyExclusive,
/* automatically_add_to_executor_with_node =*/ false);
auto waitable_interfaces = node->get_node_waitables_interface();
waitable = std::make_shared<TestWaitable>();
waitable_interfaces->add_waitable(waitable, callback_group);
executor = std::make_shared<T>();
executor->add_callback_group(callback_group, node->get_node_base_interface());
}
void TearDown() override
{
rclcpp::shutdown();
}
void
set_up_and_trigger_waitable(std::function<void()> extra_callback = nullptr)
{
this->has_executed = false;
this->waitable->set_on_execute_callback([this, extra_callback]() {
if (!this->has_executed) {
// trigger once to see if the second trigger is handled or not
// this follow up trigger simulates new entities becoming ready while
// the executor is executing something else, e.g. subscription got data
// or a timer expired, etc.
// spin_some would not handle this second trigger, since it collects
// work only once, whereas spin_all should handle it since it
// collects work multiple times
this->waitable->trigger();
this->has_executed = true;
}
if (nullptr != extra_callback) {
extra_callback();
}
});
this->waitable->trigger();
}
void
check_for_busy_waits(std::chrono::steady_clock::time_point start_time)
{
// rough time based check, since the work to be done was very small it
// should be safe to check that we didn't use more than half the
// max duration, which itself is much larger than necessary
// however, it could still produce a false-positive
EXPECT_LT(
std::chrono::steady_clock::now() - start_time,
max_duration / 2)
<< "executor took a long time to execute when it should have done "
<< "nothing and should not have blocked either, but this could be a "
<< "false negative if the computer is really slow";
// this check is making some assumptions about the implementation of the
// executors, but it should be safe to say that a busy wait may result in
// hundreds or thousands of calls to is_ready(), but "normal" executor
// behavior should be within an order of magnitude of the number of
// times that the waitable was executed
ASSERT_LT(waitable->get_is_ready_call_count(), 10u * this->waitable->get_count());
}
static constexpr auto max_duration = 10s;
rclcpp::Node::SharedPtr node;
rclcpp::CallbackGroup::SharedPtr callback_group;
std::shared_ptr<TestWaitable> waitable;
std::chrono::steady_clock::time_point start_time;
std::shared_ptr<T> executor;
bool has_executed;
};
TYPED_TEST_SUITE(TestBusyWaiting, ExecutorTypes, ExecutorTypeNames);
TYPED_TEST(TestBusyWaiting, test_spin_all)
{
this->set_up_and_trigger_waitable();
auto start_time = std::chrono::steady_clock::now();
this->executor->spin_all(this->max_duration);
this->check_for_busy_waits(start_time);
// this should get the initial trigger, and the follow up from in the callback
ASSERT_EQ(this->waitable->get_count(), 2u);
}
TYPED_TEST(TestBusyWaiting, test_spin_some)
{
this->set_up_and_trigger_waitable();
auto start_time = std::chrono::steady_clock::now();
this->executor->spin_some(this->max_duration);
this->check_for_busy_waits(start_time);
// this should get the inital trigger, but not the follow up in the callback
ASSERT_EQ(this->waitable->get_count(), 1u);
}
TYPED_TEST(TestBusyWaiting, test_spin)
{
std::condition_variable cv;
std::mutex cv_m;
bool first_check_passed = false;
this->set_up_and_trigger_waitable([&cv, &cv_m, &first_check_passed]() {
cv.notify_one();
if (!first_check_passed) {
std::unique_lock<std::mutex> lk(cv_m);
cv.wait_for(lk, 1s, [&]() {return first_check_passed;});
}
});
auto start_time = std::chrono::steady_clock::now();
std::thread t([this]() {
this->executor->spin();
});
// wait until thread has started (first execute of waitable)
{
std::unique_lock<std::mutex> lk(cv_m);
cv.wait_for(lk, 10s);
}
EXPECT_GT(this->waitable->get_count(), 0u);
first_check_passed = true;
cv.notify_one();
// wait until the executor has finished (second execute of waitable)
{
std::unique_lock<std::mutex> lk(cv_m);
cv.wait_for(lk, 10s);
}
EXPECT_EQ(this->waitable->get_count(), 2u);
this->executor->cancel();
t.join();
this->check_for_busy_waits(start_time);
// this should get the initial trigger, and the follow up from in the callback
ASSERT_EQ(this->waitable->get_count(), 2u);
}

View File

@@ -0,0 +1,244 @@
// Copyright 2024 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.
/**
* This test checks all implementations of rclcpp::executor to check they pass they basic API
* tests. Anything specific to any executor in particular should go in a separate test file.
*/
#include <gtest/gtest.h>
#include <chrono>
#include <cstddef>
#include <memory>
#include <string>
#include <type_traits>
#include <utility>
#include "rclcpp/rclcpp.hpp"
#include "test_msgs/msg/empty.hpp"
#include "./executor_types.hpp"
using namespace std::chrono_literals;
template<typename T>
class TestExecutorsWarmup : public ::testing::Test
{
public:
void SetUp()
{
rclcpp::init(0, nullptr);
}
void TearDown()
{
rclcpp::shutdown();
}
};
TYPED_TEST_SUITE(TestExecutorsWarmup, ExecutorTypes, ExecutorTypeNames);
// This test verifies that spin_all is correctly collecting work multiple times
// even when one of the items of work is a notifier waitable event and thus results in
// rebuilding the entities collection.
// When spin_all goes back to collect more work, it should see the ready items from
// the new added entities
TYPED_TEST(TestExecutorsWarmup, spin_all_doesnt_require_warmup)
{
using ExecutorType = TypeParam;
ExecutorType executor;
// Enable intra-process to guarantee deterministic and synchronous delivery of the message / event
auto node_options = rclcpp::NodeOptions().use_intra_process_comms(true);
auto node = std::make_shared<rclcpp::Node>("test_node", node_options);
// Add node to the executor before creating the entities
executor.add_node(node);
// Create entities, this will produce a notifier waitable event, telling the executor to refresh
// the entities collection
auto publisher = node->create_publisher<test_msgs::msg::Empty>("test_topic", rclcpp::QoS(10));
size_t callback_count = 0;
auto callback = [&callback_count](test_msgs::msg::Empty::ConstSharedPtr) {callback_count++;};
auto subscription =
node->create_subscription<test_msgs::msg::Empty>(
"test_topic", rclcpp::QoS(10), std::move(callback));
ASSERT_EQ(callback_count, 0u);
// Publish a message so that the new entities (i.e. the subscriber) already have work to do
publisher->publish(test_msgs::msg::Empty());
// We need to select a duration that is greater than
// the time taken to refresh the entities collection and rebuild the waitset.
// spin-all is expected to process the notifier waitable event, rebuild the collection,
// and then collect more work, finding the subscription message event.
// This duration has been selected empirically.
executor.spin_all(std::chrono::milliseconds(500));
// Verify that the callback is called as part of the spin above
EXPECT_EQ(callback_count, 1u);
}
// Same test as `spin_all_doesnt_require_warmup`, but uses a callback group
// This test reproduces the bug reported by https://github.com/ros2/rclcpp/issues/2589
TYPED_TEST(TestExecutorsWarmup, spin_all_doesnt_require_warmup_with_cbgroup)
{
using ExecutorType = TypeParam;
// TODO(alsora): Enable when https://github.com/ros2/rclcpp/pull/2595 gets merged
if (
std::is_same<ExecutorType, rclcpp::executors::SingleThreadedExecutor>() ||
std::is_same<ExecutorType, rclcpp::executors::MultiThreadedExecutor>())
{
GTEST_SKIP();
}
ExecutorType executor;
// Enable intra-process to guarantee deterministic and synchronous delivery of the message / event
auto node_options = rclcpp::NodeOptions().use_intra_process_comms(true);
auto node = std::make_shared<rclcpp::Node>("test_node", node_options);
auto callback_group = node->create_callback_group(
rclcpp::CallbackGroupType::MutuallyExclusive,
false);
// Add callback group to the executor before creating the entities
executor.add_callback_group(callback_group, node->get_node_base_interface());
// Create entities, this will produce a notifier waitable event, telling the executor to refresh
// the entities collection
auto publisher = node->create_publisher<test_msgs::msg::Empty>("test_topic", rclcpp::QoS(10));
size_t callback_count = 0;
auto callback = [&callback_count](test_msgs::msg::Empty::ConstSharedPtr) {callback_count++;};
rclcpp::SubscriptionOptions sub_options;
sub_options.callback_group = callback_group;
auto subscription =
node->create_subscription<test_msgs::msg::Empty>(
"test_topic", rclcpp::QoS(10), std::move(callback), sub_options);
ASSERT_EQ(callback_count, 0u);
// Publish a message so that the new entities (i.e. the subscriber) already have work to do
publisher->publish(test_msgs::msg::Empty());
// We need to select a duration that is greater than
// the time taken to refresh the entities collection and rebuild the waitset.
// spin-all is expected to process the notifier waitable event, rebuild the collection,
// and then collect more work, finding the subscription message event.
// This duration has been selected empirically.
executor.spin_all(std::chrono::milliseconds(500));
// Verify that the callback is called as part of the spin above
EXPECT_EQ(callback_count, 1u);
}
TYPED_TEST(TestExecutorsWarmup, spin_some_doesnt_require_warmup)
{
using ExecutorType = TypeParam;
// TODO(alsora): currently only the events-executor passes this test.
// Enable single-threaded and multi-threaded executors
// when https://github.com/ros2/rclcpp/pull/2595 gets merged
if (
!std::is_same<ExecutorType, rclcpp::experimental::executors::EventsExecutor>())
{
GTEST_SKIP();
}
ExecutorType executor;
// Enable intra-process to guarantee deterministic and synchronous delivery of the message / event
auto node_options = rclcpp::NodeOptions().use_intra_process_comms(true);
auto node = std::make_shared<rclcpp::Node>("test_node", node_options);
// Add node to the executor before creating the entities
executor.add_node(node);
// Create entities, this will produce a notifier waitable event, telling the executor to refresh
// the entities collection
auto publisher = node->create_publisher<test_msgs::msg::Empty>("test_topic", rclcpp::QoS(10));
size_t callback_count = 0;
auto callback = [&callback_count](test_msgs::msg::Empty::ConstSharedPtr) {callback_count++;};
auto subscription =
node->create_subscription<test_msgs::msg::Empty>(
"test_topic", rclcpp::QoS(10), std::move(callback));
ASSERT_EQ(callback_count, 0u);
// Publish a message so that the new entities (i.e. the subscriber) already have work to do
publisher->publish(test_msgs::msg::Empty());
// NOTE: intra-process communication is enabled, so the subscription will immediately see
// the new message, no risk of race conditions where spin_some gets called before the
// message has been delivered.
executor.spin_some();
// Verify that the callback is called as part of the spin above
EXPECT_EQ(callback_count, 1u);
}
TYPED_TEST(TestExecutorsWarmup, spin_some_doesnt_require_warmup_with_cbgroup)
{
using ExecutorType = TypeParam;
// TODO(alsora): currently only the events-executor passes this test.
// Enable single-threaded and multi-threaded executors
// when https://github.com/ros2/rclcpp/pull/2595 gets merged
if (
!std::is_same<ExecutorType, rclcpp::experimental::executors::EventsExecutor>())
{
GTEST_SKIP();
}
ExecutorType executor;
// Enable intra-process to guarantee deterministic and synchronous delivery of the message / event
auto node_options = rclcpp::NodeOptions().use_intra_process_comms(true);
auto node = std::make_shared<rclcpp::Node>("test_node", node_options);
auto callback_group = node->create_callback_group(
rclcpp::CallbackGroupType::MutuallyExclusive,
false);
// Add callback group to the executor before creating the entities
executor.add_callback_group(callback_group, node->get_node_base_interface());
// Create entities, this will produce a notifier waitable event, telling the executor to refresh
// the entities collection
auto publisher = node->create_publisher<test_msgs::msg::Empty>("test_topic", rclcpp::QoS(10));
size_t callback_count = 0;
auto callback = [&callback_count](test_msgs::msg::Empty::ConstSharedPtr) {callback_count++;};
rclcpp::SubscriptionOptions sub_options;
sub_options.callback_group = callback_group;
auto subscription =
node->create_subscription<test_msgs::msg::Empty>(
"test_topic", rclcpp::QoS(10), std::move(callback), sub_options);
ASSERT_EQ(callback_count, 0u);
// Publish a message so that the new entities (i.e. the subscriber) already have work to do
publisher->publish(test_msgs::msg::Empty());
// NOTE: intra-process communication is enabled, so the subscription will immediately see
// the new message, no risk of race conditions where spin_some gets called before the
// message has been delivered.
executor.spin_some();
// Verify that the callback is called as part of the spin above
EXPECT_EQ(callback_count, 1u);
}

View File

@@ -0,0 +1,125 @@
// Copyright 2024 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 <atomic>
#include <functional>
#include <memory>
#include <thread>
#include "rclcpp/detail/add_guard_condition_to_rcl_wait_set.hpp"
#include "rclcpp/waitable.hpp"
#include "rcl/wait.h"
#include "test_waitable.hpp"
using namespace std::chrono_literals;
void
TestWaitable::add_to_wait_set(rcl_wait_set_t & wait_set)
{
if (trigger_count_ > 0) {
// Keep the gc triggered until the trigger count is reduced back to zero.
// This is necessary if trigger() results in the wait set waking, but not
// executing this waitable, in which case it needs to be re-triggered.
gc_.trigger();
}
rclcpp::detail::add_guard_condition_to_rcl_wait_set(wait_set, gc_);
}
void TestWaitable::trigger()
{
trigger_count_++;
gc_.trigger();
}
bool
TestWaitable::is_ready(const rcl_wait_set_t & wait_set)
{
is_ready_count_++;
for (size_t i = 0; i < wait_set.size_of_guard_conditions; ++i) {
auto rcl_guard_condition = wait_set.guard_conditions[i];
if (&gc_.get_rcl_guard_condition() == rcl_guard_condition) {
return true;
}
}
return false;
}
std::shared_ptr<void>
TestWaitable::take_data()
{
return nullptr;
}
std::shared_ptr<void>
TestWaitable::take_data_by_entity_id(size_t id)
{
(void) id;
return nullptr;
}
void
TestWaitable::execute(const std::shared_ptr<void> &)
{
trigger_count_--;
count_++;
if (nullptr != on_execute_callback_) {
on_execute_callback_();
} else {
// TODO(wjwwood): I don't know why this was here, but probably it should
// not be there, or test cases where that is important should use the
// on_execute_callback?
std::this_thread::sleep_for(3ms);
}
}
void
TestWaitable::set_on_execute_callback(std::function<void()> on_execute_callback)
{
on_execute_callback_ = on_execute_callback;
}
void
TestWaitable::set_on_ready_callback(std::function<void(size_t, int)> callback)
{
auto gc_callback = [callback](size_t count) {
callback(count, 0);
};
gc_.set_on_trigger_callback(gc_callback);
}
void
TestWaitable::clear_on_ready_callback()
{
gc_.set_on_trigger_callback(nullptr);
}
size_t
TestWaitable::get_number_of_ready_guard_conditions()
{
return 1;
}
size_t
TestWaitable::get_count() const
{
return count_;
}
size_t
TestWaitable::get_is_ready_call_count() const
{
return is_ready_count_;
}

View File

@@ -0,0 +1,75 @@
// Copyright 2024 Open Source Robotics Foundation, Inc.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
#ifndef RCLCPP__EXECUTORS__TEST_WAITABLE_HPP_
#define RCLCPP__EXECUTORS__TEST_WAITABLE_HPP_
#include <atomic>
#include <functional>
#include <memory>
#include "rclcpp/waitable.hpp"
#include "rclcpp/guard_condition.hpp"
#include "rcl/wait.h"
class TestWaitable : public rclcpp::Waitable
{
public:
TestWaitable() = default;
void
add_to_wait_set(rcl_wait_set_t & wait_set) override;
void trigger();
bool
is_ready(const rcl_wait_set_t & wait_set) override;
std::shared_ptr<void>
take_data() override;
std::shared_ptr<void>
take_data_by_entity_id(size_t id) override;
void
execute(const std::shared_ptr<void> &) override;
void
set_on_execute_callback(std::function<void()> on_execute_callback);
void
set_on_ready_callback(std::function<void(size_t, int)> callback) override;
void
clear_on_ready_callback() override;
size_t
get_number_of_ready_guard_conditions() override;
size_t
get_count() const;
size_t
get_is_ready_call_count() const;
private:
std::atomic<size_t> trigger_count_ = 0;
std::atomic<size_t> is_ready_count_ = 0;
std::atomic<size_t> count_ = 0;
rclcpp::GuardCondition gc_;
std::function<void()> on_execute_callback_ = nullptr;
};
#endif // RCLCPP__EXECUTORS__TEST_WAITABLE_HPP_

View File

@@ -431,7 +431,7 @@ TYPED_TEST(TestAllClientTypesWithServer, client_qos)
rclcpp::ServicesQoS qos_profile;
qos_profile.liveliness(rclcpp::LivelinessPolicy::Automatic);
rclcpp::Duration duration(std::chrono::nanoseconds(1));
rclcpp::Duration duration(std::chrono::milliseconds(1));
qos_profile.deadline(duration);
qos_profile.lifespan(duration);
qos_profile.liveliness_lease_duration(duration);

View File

@@ -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<rclcpp::Node>("my_node", "/ns");
auto options = rclcpp::SubscriptionOptions();
options.use_intra_process_comm = rclcpp::IntraProcessSetting::Enable;
auto callback = [](test_msgs::msg::Empty::ConstSharedPtr) {};
rclcpp::Subscription<test_msgs::msg::Empty>::SharedPtr subscription;
ASSERT_NO_THROW(
{
subscription = rclcpp::create_subscription<test_msgs::msg::Empty>(
node, "topic_name", rclcpp::SystemDefaultsQoS(), callback, options);
});
ASSERT_NE(nullptr, subscription);
EXPECT_STREQ("/ns/topic_name", subscription->get_topic_name());
}

View File

@@ -200,6 +200,57 @@ TEST(TestNodeOptions, copy) {
rcl_arguments_get_count_unparsed(&other_rcl_options->arguments),
rcl_arguments_get_count_unparsed(&rcl_options->arguments));
}
{
// The following scope test is missing:
// "arguments" because it is already tested in the above scopes
// "parameter_event_publisher_options" because it can not be directly compared with EXPECT_EQ
// "allocator" because it can not be directly compared with EXPECT_EQ
// We separate attribute modification from variable initialisation (copy assignment operator)
// to be sure the "non_default_options"'s properties are correctly set before testing the
// assignment operator.
auto non_default_options = rclcpp::NodeOptions();
non_default_options
.parameter_overrides({rclcpp::Parameter("foo", 0), rclcpp::Parameter("bar", "1")})
.use_global_arguments(false)
.enable_rosout(false)
.use_intra_process_comms(true)
.enable_topic_statistics(true)
.start_parameter_services(false)
.enable_logger_service(true)
.start_parameter_event_publisher(false)
.clock_type(RCL_SYSTEM_TIME)
.clock_qos(rclcpp::SensorDataQoS())
.use_clock_thread(false)
.parameter_event_qos(rclcpp::ClockQoS())
.rosout_qos(rclcpp::ParameterEventsQoS())
.allow_undeclared_parameters(true)
.automatically_declare_parameters_from_overrides(true);
auto copied_options = non_default_options;
EXPECT_EQ(non_default_options.parameter_overrides(), copied_options.parameter_overrides());
EXPECT_EQ(non_default_options.use_global_arguments(), copied_options.use_global_arguments());
EXPECT_EQ(non_default_options.enable_rosout(), copied_options.enable_rosout());
EXPECT_EQ(non_default_options.use_intra_process_comms(),
copied_options.use_intra_process_comms());
EXPECT_EQ(non_default_options.enable_topic_statistics(),
copied_options.enable_topic_statistics());
EXPECT_EQ(non_default_options.start_parameter_services(),
copied_options.start_parameter_services());
EXPECT_EQ(non_default_options.enable_logger_service(), copied_options.enable_logger_service());
EXPECT_EQ(non_default_options.start_parameter_event_publisher(),
copied_options.start_parameter_event_publisher());
EXPECT_EQ(non_default_options.clock_type(), copied_options.clock_type());
EXPECT_EQ(non_default_options.clock_qos(), copied_options.clock_qos());
EXPECT_EQ(non_default_options.use_clock_thread(), copied_options.use_clock_thread());
EXPECT_EQ(non_default_options.parameter_event_qos(), copied_options.parameter_event_qos());
EXPECT_EQ(non_default_options.rosout_qos(), copied_options.rosout_qos());
EXPECT_EQ(non_default_options.allow_undeclared_parameters(),
copied_options.allow_undeclared_parameters());
EXPECT_EQ(non_default_options.automatically_declare_parameters_from_overrides(),
copied_options.automatically_declare_parameters_from_overrides());
}
}
TEST(TestNodeOptions, append_parameter_override) {

View File

@@ -178,6 +178,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<Empty>("topic", rclcpp::SystemDefaultsQoS());
});
}
/*
Testing publisher with intraprocess enabled and invalid QoS
*/
@@ -423,11 +438,10 @@ TEST_F(TestPublisher, intra_process_publish_failures) {
publisher->get_publisher_handle().get(), msg.get()));
}
}
RCLCPP_EXPECT_THROW_EQ(
node->create_publisher<test_msgs::msg::Empty>(
"topic", rclcpp::QoS(0), options),
std::invalid_argument(
"intraprocess communication 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<test_msgs::msg::Empty>("topic", rclcpp::QoS(0), options));
}
TEST_F(TestPublisher, inter_process_publish_failures) {

View File

@@ -123,7 +123,7 @@ TEST_F(TestServiceSub, construction_and_destruction) {
{
ASSERT_THROW(
{
auto service = node->create_service<rcl_interfaces::srv::ListParameters>(
auto service = subnode->create_service<rcl_interfaces::srv::ListParameters>(
"invalid_service?", callback);
}, rclcpp::exceptions::InvalidServiceNameError);
}
@@ -335,7 +335,7 @@ TEST_F(TestService, rcl_service_request_subscription_get_actual_qos_error) {
TEST_F(TestService, server_qos) {
rclcpp::ServicesQoS qos_profile;
qos_profile.liveliness(rclcpp::LivelinessPolicy::Automatic);
rclcpp::Duration duration(std::chrono::nanoseconds(1));
rclcpp::Duration duration(std::chrono::milliseconds(1));
qos_profile.deadline(duration);
qos_profile.lifespan(duration);
qos_profile.liveliness_lease_duration(duration);

View File

@@ -92,9 +92,16 @@ TEST_F(TestServiceIntrospection, service_introspection_nominal)
request->set__int64_value(42);
client->configure_introspection(
node->get_clock(), rclcpp::SystemDefaultsQoS(), RCL_SERVICE_INTROSPECTION_CONTENTS);
node->get_clock(), rclcpp::ServicesQoS(), RCL_SERVICE_INTROSPECTION_CONTENTS);
service->configure_introspection(
node->get_clock(), rclcpp::SystemDefaultsQoS(), RCL_SERVICE_INTROSPECTION_CONTENTS);
node->get_clock(), rclcpp::ServicesQoS(), RCL_SERVICE_INTROSPECTION_CONTENTS);
// Wait for the introspection to attach to our subscription
size_t tries = 1000;
while (this->sub->get_publisher_count() < 2 && tries-- > 0) {
std::this_thread::sleep_for(std::chrono::milliseconds(1));
}
ASSERT_EQ(sub->get_publisher_count(), 2u);
auto future = client->async_send_request(request);
ASSERT_EQ(
@@ -149,9 +156,11 @@ TEST_F(TestServiceIntrospection, service_introspection_nominal)
TEST_F(TestServiceIntrospection, service_introspection_enable_disable_events)
{
client->configure_introspection(
node->get_clock(), rclcpp::SystemDefaultsQoS(), RCL_SERVICE_INTROSPECTION_OFF);
node->get_clock(), rclcpp::ServicesQoS(), RCL_SERVICE_INTROSPECTION_OFF);
service->configure_introspection(
node->get_clock(), rclcpp::SystemDefaultsQoS(), RCL_SERVICE_INTROSPECTION_OFF);
node->get_clock(), rclcpp::ServicesQoS(), RCL_SERVICE_INTROSPECTION_OFF);
ASSERT_EQ(sub->get_publisher_count(), 0);
auto request = std::make_shared<BasicTypes::Request>();
request->set__bool_value(true);
@@ -169,9 +178,16 @@ TEST_F(TestServiceIntrospection, service_introspection_enable_disable_events)
events.clear();
client->configure_introspection(
node->get_clock(), rclcpp::SystemDefaultsQoS(), RCL_SERVICE_INTROSPECTION_METADATA);
node->get_clock(), rclcpp::ServicesQoS(), RCL_SERVICE_INTROSPECTION_METADATA);
service->configure_introspection(
node->get_clock(), rclcpp::SystemDefaultsQoS(), RCL_SERVICE_INTROSPECTION_OFF);
node->get_clock(), rclcpp::ServicesQoS(), RCL_SERVICE_INTROSPECTION_OFF);
// Wait for the introspection to attach to our subscription
size_t tries = 1000;
while (this->sub->get_publisher_count() < 1 && tries-- > 0) {
std::this_thread::sleep_for(std::chrono::milliseconds(1));
}
ASSERT_EQ(sub->get_publisher_count(), 1u);
future = client->async_send_request(request);
ASSERT_EQ(
@@ -186,9 +202,16 @@ TEST_F(TestServiceIntrospection, service_introspection_enable_disable_events)
events.clear();
client->configure_introspection(
node->get_clock(), rclcpp::SystemDefaultsQoS(), RCL_SERVICE_INTROSPECTION_OFF);
node->get_clock(), rclcpp::ServicesQoS(), RCL_SERVICE_INTROSPECTION_OFF);
service->configure_introspection(
node->get_clock(), rclcpp::SystemDefaultsQoS(), RCL_SERVICE_INTROSPECTION_METADATA);
node->get_clock(), rclcpp::ServicesQoS(), RCL_SERVICE_INTROSPECTION_METADATA);
// Wait for the introspection to attach to our subscription
tries = 1000;
while (this->sub->get_publisher_count() < 1 && tries-- > 0) {
std::this_thread::sleep_for(std::chrono::milliseconds(1));
}
ASSERT_EQ(sub->get_publisher_count(), 1u);
future = client->async_send_request(request);
ASSERT_EQ(
@@ -203,9 +226,16 @@ TEST_F(TestServiceIntrospection, service_introspection_enable_disable_events)
events.clear();
client->configure_introspection(
node->get_clock(), rclcpp::SystemDefaultsQoS(), RCL_SERVICE_INTROSPECTION_METADATA);
node->get_clock(), rclcpp::ServicesQoS(), RCL_SERVICE_INTROSPECTION_METADATA);
service->configure_introspection(
node->get_clock(), rclcpp::SystemDefaultsQoS(), RCL_SERVICE_INTROSPECTION_METADATA);
node->get_clock(), rclcpp::ServicesQoS(), RCL_SERVICE_INTROSPECTION_METADATA);
// Wait for the introspection to attach to our subscription
tries = 1000;
while (this->sub->get_publisher_count() < 2 && tries-- > 0) {
std::this_thread::sleep_for(std::chrono::milliseconds(1));
}
ASSERT_EQ(sub->get_publisher_count(), 2u);
future = client->async_send_request(request);
ASSERT_EQ(
@@ -221,9 +251,16 @@ TEST_F(TestServiceIntrospection, service_introspection_enable_disable_events)
TEST_F(TestServiceIntrospection, service_introspection_enable_disable_event_content)
{
client->configure_introspection(
node->get_clock(), rclcpp::SystemDefaultsQoS(), RCL_SERVICE_INTROSPECTION_METADATA);
node->get_clock(), rclcpp::ServicesQoS(), RCL_SERVICE_INTROSPECTION_METADATA);
service->configure_introspection(
node->get_clock(), rclcpp::SystemDefaultsQoS(), RCL_SERVICE_INTROSPECTION_METADATA);
node->get_clock(), rclcpp::ServicesQoS(), RCL_SERVICE_INTROSPECTION_METADATA);
// Wait for the introspection to attach to our subscription
size_t tries = 1000;
while (this->sub->get_publisher_count() < 2 && tries-- > 0) {
std::this_thread::sleep_for(std::chrono::milliseconds(1));
}
ASSERT_EQ(sub->get_publisher_count(), 2u);
auto request = std::make_shared<BasicTypes::Request>();
request->set__bool_value(true);
@@ -245,9 +282,16 @@ TEST_F(TestServiceIntrospection, service_introspection_enable_disable_event_cont
events.clear();
client->configure_introspection(
node->get_clock(), rclcpp::SystemDefaultsQoS(), RCL_SERVICE_INTROSPECTION_CONTENTS);
node->get_clock(), rclcpp::ServicesQoS(), RCL_SERVICE_INTROSPECTION_CONTENTS);
service->configure_introspection(
node->get_clock(), rclcpp::SystemDefaultsQoS(), RCL_SERVICE_INTROSPECTION_METADATA);
node->get_clock(), rclcpp::ServicesQoS(), RCL_SERVICE_INTROSPECTION_METADATA);
// Wait for the introspection to attach to our subscription
tries = 1000;
while (this->sub->get_publisher_count() < 2 && tries-- > 0) {
std::this_thread::sleep_for(std::chrono::milliseconds(1));
}
ASSERT_EQ(sub->get_publisher_count(), 2u);
future = client->async_send_request(request);
ASSERT_EQ(
@@ -278,9 +322,16 @@ TEST_F(TestServiceIntrospection, service_introspection_enable_disable_event_cont
events.clear();
client->configure_introspection(
node->get_clock(), rclcpp::SystemDefaultsQoS(), RCL_SERVICE_INTROSPECTION_METADATA);
node->get_clock(), rclcpp::ServicesQoS(), RCL_SERVICE_INTROSPECTION_METADATA);
service->configure_introspection(
node->get_clock(), rclcpp::SystemDefaultsQoS(), RCL_SERVICE_INTROSPECTION_CONTENTS);
node->get_clock(), rclcpp::ServicesQoS(), RCL_SERVICE_INTROSPECTION_CONTENTS);
// Wait for the introspection to attach to our subscription
tries = 1000;
while (this->sub->get_publisher_count() < 2 && tries-- > 0) {
std::this_thread::sleep_for(std::chrono::milliseconds(1));
}
ASSERT_EQ(sub->get_publisher_count(), 2u);
future = client->async_send_request(request);
ASSERT_EQ(
@@ -311,9 +362,16 @@ TEST_F(TestServiceIntrospection, service_introspection_enable_disable_event_cont
events.clear();
client->configure_introspection(
node->get_clock(), rclcpp::SystemDefaultsQoS(), RCL_SERVICE_INTROSPECTION_CONTENTS);
node->get_clock(), rclcpp::ServicesQoS(), RCL_SERVICE_INTROSPECTION_CONTENTS);
service->configure_introspection(
node->get_clock(), rclcpp::SystemDefaultsQoS(), RCL_SERVICE_INTROSPECTION_CONTENTS);
node->get_clock(), rclcpp::ServicesQoS(), RCL_SERVICE_INTROSPECTION_CONTENTS);
// Wait for the introspection to attach to our subscription
tries = 1000;
while (this->sub->get_publisher_count() < 2 && tries-- > 0) {
std::this_thread::sleep_for(std::chrono::milliseconds(1));
}
ASSERT_EQ(sub->get_publisher_count(), 2u);
future = client->async_send_request(request);
ASSERT_EQ(

View File

@@ -60,7 +60,7 @@ TEST_F(TestSubscriptionOptions, topic_statistics_options_default_and_set) {
EXPECT_EQ(options.topic_stats_options.state, rclcpp::TopicStatisticsState::NodeDefault);
EXPECT_EQ(options.topic_stats_options.publish_topic, defaultPublishTopic);
EXPECT_EQ(options.topic_stats_options.publish_period, 1s);
EXPECT_EQ(options.topic_stats_options.qos, rclcpp::SystemDefaultsQoS());
EXPECT_EQ(options.topic_stats_options.qos, rclcpp::SystemDefaultsQoS().keep_last(10));
options.topic_stats_options.state = rclcpp::TopicStatisticsState::Enable;
options.topic_stats_options.publish_topic = "topic_statistics";

View File

@@ -3,6 +3,15 @@ Changelog for package rclcpp_action
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
28.1.6 (2024-12-18)
-------------------
28.1.5 (2024-09-19)
-------------------
28.1.4 (2024-09-06)
-------------------
28.1.3 (2024-06-27)
-------------------

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>28.1.3</version>
<version>28.1.6</version>
<description>Adds action APIs for C++.</description>
<maintainer email="ivanpauno@ekumenlabs.com">Ivan Paunovic</maintainer>

View File

@@ -2,6 +2,15 @@
Changelog for package rclcpp_components
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
28.1.6 (2024-12-18)
-------------------
28.1.5 (2024-09-19)
-------------------
28.1.4 (2024-09-06)
-------------------
28.1.3 (2024-06-27)
-------------------

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>28.1.3</version>
<version>28.1.6</version>
<description>Package containing tools for dynamically loadable components</description>
<maintainer email="ivanpauno@ekumenlabs.com">Ivan Paunovic</maintainer>

View File

@@ -3,6 +3,15 @@ Changelog for package rclcpp_lifecycle
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
28.1.6 (2024-12-18)
-------------------
28.1.5 (2024-09-19)
-------------------
28.1.4 (2024-09-06)
-------------------
28.1.3 (2024-06-27)
-------------------
* Revert "call shutdown in LifecycleNode dtor to avoid leaving the device in unknown state (2nd) (backport `#2528 <https://github.com/ros2/rclcpp/issues/2528>`_) (`#2542 <https://github.com/ros2/rclcpp/issues/2542>`_)" (`#2558 <https://github.com/ros2/rclcpp/issues/2558>`_)

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>28.1.3</version>
<version>28.1.6</version>
<description>Package containing a prototype for lifecycle implementation</description>
<maintainer email="ivanpauno@ekumenlabs.com">Ivan Paunovic</maintainer>