Compare commits

..

40 Commits
iron ... 22.1.0

Author SHA1 Message Date
Chris Lalancette
89f0afe9bc 22.1.0 2023-08-21 14:52:05 +00:00
Chris Lalancette
a4db4c57a6 Changelog.
Signed-off-by: Chris Lalancette <clalancette@gmail.com>
2023-08-21 14:51:57 +00:00
Tomoya Fujita
fbe8f28cd1 Do not crash Executor when send_response fails due to client failure. (#2276)
* Do not crash Executor when send_response fails due to client failure.

Related to https://github.com/ros2/ros2/issues/1253

It is not sane that a faulty client can crash our service Executor, as
discussed in the referred issue, if the client is not setup properly,
send_response may return RCL_RET_TIMEOUT, we should not throw an error
in this case.

Signed-off-by: Zang MingJie <zealot0630@gmail.com>

* Update rclcpp/include/rclcpp/service.hpp

Co-authored-by: Tomoya Fujita <Tomoya.Fujita@sony.com>
Signed-off-by: Zang MingJie <zealot0630@gmail.com>

* address review comments.

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

---------

Signed-off-by: Zang MingJie <zealot0630@gmail.com>
Signed-off-by: Tomoya Fujita <Tomoya.Fujita@sony.com>
Co-authored-by: Zang MingJie <zealot0630@gmail.com>
2023-08-18 09:15:04 -07:00
Lucas Wendland
65f0b70d4a Adding Custom Unknown Type Error (#2272)
Signed-off-by: CursedRock17 <mtglucas1@gmail.com>
Co-authored-by: Christophe Bedard <bedard.christophe@gmail.com>
2023-08-15 15:21:33 -07:00
Emerson Knapp
9b4b3da3d4 Add a pimpl inside rclcpp::Node for future distro backports (#2228)
* Add a pimpl inside rclcpp::Node for future distro backports

Signed-off-by: Emerson Knapp <emerson.b.knapp@gmail.com>
Co-authored-by: Chris Lalancette <clalancette@gmail.com>
2023-08-10 08:31:05 -04:00
Chris Lalancette
cd0440f1a5 Remove an unused variable from the events executor tests. (#2270)
Signed-off-by: Chris Lalancette <clalancette@gmail.com>
2023-08-09 16:56:01 -04:00
Tony Najjar
a17d26b20a Add spin_all shortcut (#2246)
Signed-off-by: Tony Najjar <tony.najjar@logivations.com>
2023-08-08 16:38:13 -05:00
Lucas Wendland
e2965831d5 Adding Missing Group Exceptions (#2256)
* Adding Missing Group Exceptions

Signed-off-by: CursedRock17 <mtglucas1@gmail.com>
Signed-off-by: Chris Lalancette <clalancette@gmail.com>
Co-authored-by: Tomoya Fujita <Tomoya.Fujita@sony.com>
2023-08-04 16:25:26 -04:00
Luca Della Vedova
ea29c142af Change associated clocks storage to unordered_set (#2257)
Signed-off-by: Luca Della Vedova <lucadv@intrinsic.ai>
2023-08-03 08:43:04 -04:00
Tomoya Fujita
5d6e5fa766 associated clocks should be protected by mutex. (#2255)
Signed-off-by: Tomoya Fujita <Tomoya.Fujita@sony.com>
2023-08-01 22:43:02 -07:00
Christophe Bedard
22a954e1b0 Instrument loaned message publication code path (#2240)
Signed-off-by: Christophe Bedard <christophe.bedard@apex.ai>
2023-07-21 11:26:42 -07:00
Chris Lalancette
c0d72c3ee0 Stop using constref signature of benchmark DoNotOptimize. (#2238)
* Stop using constref signature of benchmark DoNotOptimize.

Newer versions of google benchmark (1.8.2 in my case) warn
that the compiler may optimize away the DoNotOptimize calls
when using the constref version.  Get away from that here
by explicitly *not* calling the constref version, casting
where necessary.

Signed-off-by: Chris Lalancette <clalancette@gmail.com>
2023-07-18 10:18:22 -04:00
Chris Lalancette
6e97990a32 22.0.0 2023-07-11 19:48:37 +00:00
Chris Lalancette
4ebc5f61d8 Changelog.
Signed-off-by: Chris Lalancette <clalancette@gmail.com>
2023-07-11 19:48:30 +00:00
Emerson Knapp
a7a9b78fee Implement get_node_type_descriptions_interface for lifecyclenode and add smoke test for it (#2237)
Signed-off-by: Emerson Knapp <emerson.b.knapp@gmail.com>
2023-07-11 08:41:53 -04:00
Chris Lalancette
945d254e32 Switch lifecycle to use the RCLCPP macros. (#2233)
This ensures that they'll go out to /rosout and the disk.

Signed-off-by: Chris Lalancette <clalancette@gmail.com>
2023-07-10 15:59:35 -04:00
Emerson Knapp
deebbc3ad6 Add new node interface TypeDescriptionsInterface to provide GetTypeDescription service (#2224)
* TypeDescriptions interface with readonly param configuration

* Add parameter descriptor, to make read only

* example of spinning in thread for get_type_description service

* Add a basic test for the new interface

* Fix tests with new parameter

* Add comments about builtin parameters

Signed-off-by: Emerson Knapp <emerson.b.knapp@gmail.com>
Signed-off-by: William Woodall <william@osrfoundation.org>
2023-07-07 13:10:27 -04:00
Nathan Wiebe Neufeldt
588dba7a70 Move always_false_v to detail namespace (#2232)
Since this is a common idiom, especially under this name, we should
define the `always_false_v` template within a namespace to avoid
conflict with other libraries and user code. This could either be
`rclcpp::detail` if it's intended only for internal use or just `rclcpp`
if it's intended as a public helper. In this PR, I've initially chosen
the former.

Signed-off-by: Nathan Wiebe Neufeldt <nwiebeneufeldt@clearpath.ai>
2023-07-05 16:55:11 -04:00
Chris Lalancette
2e355e4849 Revamp the test_subscription.cpp tests. (#2227)
The original motiviation to do this was a crash during
teardown when using a newer version of gtest.  But while
I was in here, I did a small overall cleanup, including:

1.  Moving code closer to where it is actually used.
2.  Getting rid of unused 'using' statements.
3.  Adding in missing includes.
4.  Properly tearing down and recreating the rclcpp
    context during test teardown (this fixed the actual
    bug).
5.  Making class members private where possible.
6.  Renaming class methods to our usual conventions.

Signed-off-by: Chris Lalancette <clalancette@gmail.com>
2023-06-26 12:59:56 -04:00
Tomoya Fujita
fe2e0e4c64 warning: comparison of integer expressions of different signedness (#2219)
https://github.com/ros2/rclcpp/pull/2167#issuecomment-1597197552

Signed-off-by: Tomoya Fujita <Tomoya.Fujita@sony.com>
2023-06-22 08:02:27 -07:00
Eloy Briceno
005f6aefe9 Modifies timers API to select autostart state (#2005)
* Modifies timers API to select autostart state

* Removes unnecessary variables

* Adds autostart documentation and expands some timer test

Signed-off-by: Voldivh <eloyabmfcv@gmail.com>
2023-06-21 10:47:14 -04:00
Christopher Wecht
3a64349aec Enable callback group tests for connextdds (#2182)
* Enable callback group tests for connextdds

* Enable executors and event executor tests for connextdds

* Enable qos events tests for connextdds

* Less flaky qos_event tests

Signed-off-by: Christopher Wecht <cwecht@mailbox.org>
2023-06-14 08:33:33 -04:00
Chris Lalancette
3530b0959c 21.3.0 2023-06-12 12:45:11 +00:00
Chris Lalancette
4d12bcbca0 Changelog.
Signed-off-by: Chris Lalancette <clalancette@gmail.com>
2023-06-12 12:45:00 +00:00
Chris Lalancette
1fff79089a Fix up misspellings of "receive". (#2208)
Signed-off-by: Chris Lalancette <clalancette@gmail.com>
2023-06-11 22:48:56 -07:00
Michael Carroll
b3518d12ca Remove flaky stressAddRemoveNode test (#2206)
Signed-off-by: Michael Carroll <mjcarroll@intrinsic.ai>
2023-06-09 11:18:04 -05:00
Christophe Bedard
4efc05266b Use TRACETOOLS_ prefix for tracepoint-related macros (#2162)
Signed-off-by: Christophe Bedard <bedard.christophe@gmail.com>
2023-06-08 12:38:49 -05:00
Chris Lalancette
dab9c8acdc 21.2.0 2023-06-07 13:28:18 +00:00
Chris Lalancette
867ad62da2 Changelog.
Signed-off-by: Chris Lalancette <clalancette@gmail.com>
2023-06-07 13:28:05 +00:00
Chen Lihui
f8072f2fa2 remove nolint since ament_cpplint updated for the c++17 header (#2198)
Signed-off-by: Chen Lihui <lihui.chen@sony.com>
2023-05-22 21:22:13 -07:00
DensoADAS
fce021b149 Feature/available capacity of ipm (#2173)
* added available_capacity to get the lowest number of free capacity for intra-process communication for a publisher

Signed-off-by: Joshua Hampp <j.hampp@denso-adas.de>

* added unit tests for available_capacity

Signed-off-by: Joshua Hampp <j.hampp@eu.denso.com>
Signed-off-by: Joshua Hampp <j.hampp@denso-adas.de>

* fixed typos in comments

Signed-off-by: Joshua Hampp <j.hampp@denso-adas.de>

* Updated warning

Co-authored-by: Alberto Soragna <alberto.soragna@gmail.com>
Signed-off-by: Joshua Hampp <j.hampp@denso-adas.de>

* returning 0 if ipm is disabled in lowest_available_ipm_capacity

Signed-off-by: Joshua Hampp <j.hampp@denso-adas.de>

* return 0 if no subscribers are present in lowest_available_capacity

Signed-off-by: Joshua Hampp <j.hampp@denso-adas.de>

* updated unit test

Signed-off-by: Joshua Hampp <j.hampp@denso-adas.de>

* update unit test

Signed-off-by: Joshua Hampp <j.hampp@eu.denso.com>

* moved available_capacity to a lambda function to be able to handle subscriptions which went out of scope

Signed-off-by: Joshua Hampp <j.hampp@denso-adas.de>

* updated unit test to check subscriptions which went out of scope

Signed-off-by: Joshua Hampp <j.hampp@denso-adas.de>

---------

Signed-off-by: Joshua Hampp <j.hampp@denso-adas.de>
Signed-off-by: Joshua Hampp <j.hampp@eu.denso.com>
Co-authored-by: Joshua Hampp <j.hampp@denso-adas.de>
Co-authored-by: Joshua Hampp <j.hampp@eu.denso.com>
Co-authored-by: Alberto Soragna <alberto.soragna@gmail.com>
2023-05-19 19:31:59 +01:00
Alberto Soragna
c4f57a7998 add mutex to protect events_executor current entity collection (#2187)
* add mutex to protect events_executor current entity collection and unit-test

Signed-off-by: Alberto Soragna <alberto.soragna@gmail.com>

* be more precise with mutex locks; make stress test less stressfull

Signed-off-by: Alberto Soragna <alberto.soragna@gmail.com>

* fix uncrustify error

Signed-off-by: Alberto Soragna <alberto.soragna@gmail.com>

---------

Signed-off-by: Alberto Soragna <alberto.soragna@gmail.com>
2023-05-18 14:43:26 +01:00
mauropasse
d7fdb6184c Declare rclcpp callbacks before the rcl entities (#2024)
This is to ensure callbacks are destroyed last
on entities destruction, avoiding the gap in time
in which rmw entities hold a reference to a
destroyed function.

Signed-off-by: Mauro Passerino <mpasserino@irobot.com>
Co-authored-by: Mauro Passerino <mpasserino@irobot.com>
2023-05-15 15:55:08 -04:00
Yadunund
58bcd3b822 21.1.1 2023-05-11 18:59:17 +08:00
Yadunund
26426adda9 Changelog
Signed-off-by: Yadunund <yadunund@openrobotics.org>
2023-05-11 18:58:48 +08:00
Alberto Soragna
6e1fea14e1 Fix race condition in events-executor (#2177)
The initial implementation of the events-executor contained a bug where the executor
would end up in an inconsistent state and stop processing interrupt/shutdown notifications.
Manually adding a node to the executor results in a) producing a notify waitable event
and b) refreshing the executor collections.
The inconsistent state would happen if the event was processed before the collections were
finished to be refreshed: the executor would pick up the event but be unable to process it.
This would leave the `notify_waitable_event_pushed_` flag to true, preventing additional
notify waitable events to be pushed.
The behavior is observable only under heavy load.

Signed-off-by: Alberto Soragna <alberto.soragna@gmail.com>
2023-05-05 09:04:35 -05:00
Øystein Sture
86c77143c9 Add missing stdexcept include (#2186)
Signed-off-by: Øystein Sture <os@skarvtech.com>
2023-05-04 15:48:20 -04:00
Chris Lalancette
b812790ee3 Fix a format-security warning when building with clang (#2171)
In particular, you should never have a "bare" string in a
printf-like call; that could potentially access uninitialized
memory. Instead, make sure to format the string with %s.

Signed-off-by: Chris Lalancette <clalancette@gmail.com>
2023-05-01 17:06:55 -04:00
methylDragon
6ca1023ef7 Fix delivered message kind (#2175)
Signed-off-by: methylDragon <methylDragon@gmail.com>
2023-05-01 16:56:29 -04:00
Yadunund
77ede02251 21.1.0 2023-04-27 16:53:54 +08:00
85 changed files with 1408 additions and 1587 deletions

View File

@@ -2,56 +2,55 @@
Changelog for package rclcpp
^^^^^^^^^^^^^^^^^^^^^^^^^^^^
21.0.8 (2024-11-08)
22.1.0 (2023-08-21)
-------------------
* associated clocks should be protected by mutex. (`#2258 <https://github.com/ros2/rclcpp/issues/2258>`_)
* Skip client_qos test (`#2658 <https://github.com/ros2/rclcpp/issues/2658>`_)
* Use the same context for the specified node in rclcpp::spin functions. (`#2618 <https://github.com/ros2/rclcpp/issues/2618>`_)
* Contributors: Cristóbal Arroyo, Tomoya Fujita
* Do not crash Executor when send_response fails due to client failure. (`#2276 <https://github.com/ros2/rclcpp/issues/2276>`_)
* Adding Custom Unknown Type Error (`#2272 <https://github.com/ros2/rclcpp/issues/2272>`_)
* Add a pimpl inside rclcpp::Node for future distro backports (`#2228 <https://github.com/ros2/rclcpp/issues/2228>`_)
* Remove an unused variable from the events executor tests. (`#2270 <https://github.com/ros2/rclcpp/issues/2270>`_)
* Add spin_all shortcut (`#2246 <https://github.com/ros2/rclcpp/issues/2246>`_)
* Adding Missing Group Exceptions (`#2256 <https://github.com/ros2/rclcpp/issues/2256>`_)
* Change associated clocks storage to unordered_set (`#2257 <https://github.com/ros2/rclcpp/issues/2257>`_)
* associated clocks should be protected by mutex. (`#2255 <https://github.com/ros2/rclcpp/issues/2255>`_)
* Instrument loaned message publication code path (`#2240 <https://github.com/ros2/rclcpp/issues/2240>`_)
* Contributors: Chris Lalancette, Christophe Bedard, Emerson Knapp, Luca Della Vedova, Lucas Wendland, Tomoya Fujita, Tony Najjar
21.0.7 (2024-07-10)
22.0.0 (2023-07-11)
-------------------
* Add test creating two content filter topics with the same topic name (`#2550 <https://github.com/ros2/rclcpp/issues/2550>`_)
* Revise the description of service configure_introspection() (`#2514 <https://github.com/ros2/rclcpp/issues/2514>`_)
* Contributors: Alejandro Hernández Cordero, Barry Xu
* Implement get_node_type_descriptions_interface for lifecyclenode and add smoke test for it (`#2237 <https://github.com/ros2/rclcpp/issues/2237>`_)
* Add new node interface TypeDescriptionsInterface to provide GetTypeDescription service (`#2224 <https://github.com/ros2/rclcpp/issues/2224>`_)
* Move always_false_v to detail namespace (`#2232 <https://github.com/ros2/rclcpp/issues/2232>`_)
* Revamp the test_subscription.cpp tests. (`#2227 <https://github.com/ros2/rclcpp/issues/2227>`_)
* warning: comparison of integer expressions of different signedness (`#2219 <https://github.com/ros2/rclcpp/issues/2219>`_)
* Modifies timers API to select autostart state (`#2005 <https://github.com/ros2/rclcpp/issues/2005>`_)
* Enable callback group tests for connextdds (`#2182 <https://github.com/ros2/rclcpp/issues/2182>`_)
* Contributors: Chris Lalancette, Christopher Wecht, Eloy Briceno, Emerson Knapp, Nathan Wiebe Neufeldt, Tomoya Fujita
21.0.6 (2024-04-19)
21.3.0 (2023-06-12)
-------------------
* address ambiguous auto variable. (`#2486 <https://github.com/ros2/rclcpp/issues/2486>`_)
* Contributors: Tomoya Fujita
* Fix up misspellings of "receive". (`#2208 <https://github.com/ros2/rclcpp/issues/2208>`_)
* Remove flaky stressAddRemoveNode test (`#2206 <https://github.com/ros2/rclcpp/issues/2206>`_)
* Use TRACETOOLS\_ prefix for tracepoint-related macros (`#2162 <https://github.com/ros2/rclcpp/issues/2162>`_)
* Contributors: Chris Lalancette, Christophe Bedard, Michael Carroll
21.0.5 (2024-02-07)
21.2.0 (2023-06-07)
-------------------
* Fix data race in EventHandlerBase (`#2387 <https://github.com/ros2/rclcpp/issues/2387>`_)
* Contributors: mauropasse
* remove nolint since ament_cpplint updated for the c++17 header (`#2198 <https://github.com/ros2/rclcpp/issues/2198>`_)
* Feature/available capacity of ipm (`#2173 <https://github.com/ros2/rclcpp/issues/2173>`_)
* add mutex to protect events_executor current entity collection (`#2187 <https://github.com/ros2/rclcpp/issues/2187>`_)
* Declare rclcpp callbacks before the rcl entities (`#2024 <https://github.com/ros2/rclcpp/issues/2024>`_)
* Contributors: Alberto Soragna, Chen Lihui, DensoADAS, mauropasse
21.0.4 (2023-11-17)
21.1.1 (2023-05-11)
-------------------
* Disable the loaned messages inside the executor. (`#2365 <https://github.com/ros2/rclcpp/issues/2365>`_)
* Add missing 'enable_rosout' comments (`#2346 <https://github.com/ros2/rclcpp/issues/2346>`_)
* Address rate related flaky tests. (`#2341 <https://github.com/ros2/rclcpp/issues/2341>`_)
* Add missing stdexcept include (`#2333 <https://github.com/ros2/rclcpp/issues/2333>`_)
* Update SignalHandler get_global_signal_handler to avoid complex types in static memory (`#2322 <https://github.com/ros2/rclcpp/issues/2322>`_)
* Fix C++20 allocator construct deprecation (`#2318 <https://github.com/ros2/rclcpp/issues/2318>`_)
* Topic correct typeadapter deduction (`#2298 <https://github.com/ros2/rclcpp/issues/2298>`_)
* Contributors: AiVerisimilitude, Chen Lihui, Chris Lalancette, Jiaqi Li, Øystein Sture, Tomoya Fujita, William Woodall
* Fix race condition in events-executor (`#2177 <https://github.com/ros2/rclcpp/issues/2177>`_)
* Add missing stdexcept include (`#2186 <https://github.com/ros2/rclcpp/issues/2186>`_)
* Fix a format-security warning when building with clang (`#2171 <https://github.com/ros2/rclcpp/issues/2171>`_)
* Fix delivered message kind (`#2175 <https://github.com/ros2/rclcpp/issues/2175>`_)
* Contributors: Alberto Soragna, Chris Lalancette, methylDragon, Øystein Sture
21.0.3 (2023-09-08)
21.1.0 (2023-04-27)
-------------------
* Do not crash Executor when send_response fails due to client failure. (`#2279 <https://github.com/ros2/rclcpp/issues/2279>`_)
* Add new node interface TypeDescriptionsInterface to provide GetTypeDescription service (`#2236 <https://github.com/ros2/rclcpp/issues/2236>`_)
* Contributors: Emerson Knapp, Tomoya Fujita, Zang MingJie
21.0.2 (2023-07-14)
-------------------
* Fix warnings related to comparison of integer expressions of different signedness (`#2222 <https://github.com/ros2/rclcpp/issues/2222>`_)
* Fix race condition in events-executor (`#2191 <https://github.com/ros2/rclcpp/issues/2191>`_)
* Contributors: Alberto Soragna, Tomoya Fujita
21.0.1 (2023-05-11)
-------------------
* Fix delivered message kind (`#2175 <https://github.com/ros2/rclcpp/issues/2175>`_) (`#2178 <https://github.com/ros2/rclcpp/issues/2178>`_)
* Contributors: mergify[bot]
21.0.0 (2023-04-18)
-------------------

View File

@@ -156,7 +156,7 @@ public:
const std::shared_ptr<rmw_request_id_t> & request_header,
std::shared_ptr<typename ServiceT::Request> request)
{
TRACEPOINT(callback_start, static_cast<const void *>(this), false);
TRACETOOLS_TRACEPOINT(callback_start, static_cast<const void *>(this), false);
if (std::holds_alternative<std::monostate>(callback_)) {
// TODO(ivanpauno): Remove the set method, and force the users of this class
// to pass a callback at construnciton.
@@ -182,7 +182,7 @@ public:
const auto & cb = std::get<SharedPtrWithRequestHeaderCallback>(callback_);
cb(request_header, std::move(request), response);
}
TRACEPOINT(callback_end, static_cast<const void *>(this));
TRACETOOLS_TRACEPOINT(callback_end, static_cast<const void *>(this));
return response;
}
@@ -191,9 +191,9 @@ public:
#ifndef TRACETOOLS_DISABLED
std::visit(
[this](auto && arg) {
if (TRACEPOINT_ENABLED(rclcpp_callback_register)) {
if (TRACETOOLS_TRACEPOINT_ENABLED(rclcpp_callback_register)) {
char * symbol = tracetools::get_symbol(arg);
DO_TRACEPOINT(
TRACETOOLS_DO_TRACEPOINT(
rclcpp_callback_register,
static_cast<const void *>(this),
symbol);

View File

@@ -20,7 +20,7 @@
#include <stdexcept>
#include <type_traits>
#include <utility>
#include <variant> // NOLINT[build/include_order]
#include <variant>
#include "rosidl_runtime_cpp/traits.hpp"
#include "tracetools/tracetools.h"
@@ -34,15 +34,15 @@
#include "rclcpp/type_adapter.hpp"
template<class>
inline constexpr bool always_false_v = false;
namespace rclcpp
{
namespace detail
{
template<class>
inline constexpr bool always_false_v = false;
template<typename MessageT, typename AllocatorT>
struct MessageDeleterHelper
{
@@ -492,7 +492,7 @@ public:
std::shared_ptr<ROSMessageType> message,
const rclcpp::MessageInfo & message_info)
{
TRACEPOINT(callback_start, static_cast<const void *>(this), false);
TRACETOOLS_TRACEPOINT(callback_start, static_cast<const void *>(this), false);
// Check if the variant is "unset", throw if it is.
if (callback_variant_.index() == 0) {
if (std::get<0>(callback_variant_) == nullptr) {
@@ -580,10 +580,10 @@ public:
}
// condition to catch unhandled callback types
else { // NOLINT[readability/braces]
static_assert(always_false_v<T>, "unhandled callback type");
static_assert(detail::always_false_v<T>, "unhandled callback type");
}
}, callback_variant_);
TRACEPOINT(callback_end, static_cast<const void *>(this));
TRACETOOLS_TRACEPOINT(callback_end, static_cast<const void *>(this));
}
// Dispatch when input is a serialized message and the output could be anything.
@@ -592,7 +592,7 @@ public:
std::shared_ptr<rclcpp::SerializedMessage> serialized_message,
const rclcpp::MessageInfo & message_info)
{
TRACEPOINT(callback_start, static_cast<const void *>(this), false);
TRACETOOLS_TRACEPOINT(callback_start, static_cast<const void *>(this), false);
// Check if the variant is "unset", throw if it is.
if (callback_variant_.index() == 0) {
if (std::get<0>(callback_variant_) == nullptr) {
@@ -660,10 +660,10 @@ public:
}
// condition to catch unhandled callback types
else { // NOLINT[readability/braces]
static_assert(always_false_v<T>, "unhandled callback type");
static_assert(detail::always_false_v<T>, "unhandled callback type");
}
}, callback_variant_);
TRACEPOINT(callback_end, static_cast<const void *>(this));
TRACETOOLS_TRACEPOINT(callback_end, static_cast<const void *>(this));
}
void
@@ -671,7 +671,7 @@ public:
std::shared_ptr<const SubscribedType> message,
const rclcpp::MessageInfo & message_info)
{
TRACEPOINT(callback_start, static_cast<const void *>(this), true);
TRACETOOLS_TRACEPOINT(callback_start, static_cast<const void *>(this), true);
// Check if the variant is "unset", throw if it is.
if (callback_variant_.index() == 0) {
if (std::get<0>(callback_variant_) == nullptr) {
@@ -790,10 +790,10 @@ public:
}
// condition to catch unhandled callback types
else { // NOLINT[readability/braces]
static_assert(always_false_v<T>, "unhandled callback type");
static_assert(detail::always_false_v<T>, "unhandled callback type");
}
}, callback_variant_);
TRACEPOINT(callback_end, static_cast<const void *>(this));
TRACETOOLS_TRACEPOINT(callback_end, static_cast<const void *>(this));
}
void
@@ -801,7 +801,7 @@ public:
std::unique_ptr<SubscribedType, SubscribedTypeDeleter> message,
const rclcpp::MessageInfo & message_info)
{
TRACEPOINT(callback_start, static_cast<const void *>(this), true);
TRACETOOLS_TRACEPOINT(callback_start, static_cast<const void *>(this), true);
// Check if the variant is "unset", throw if it is.
if (callback_variant_.index() == 0) {
if (std::get<0>(callback_variant_) == nullptr) {
@@ -924,10 +924,10 @@ public:
}
// condition to catch unhandled callback types
else { // NOLINT[readability/braces]
static_assert(always_false_v<T>, "unhandled callback type");
static_assert(detail::always_false_v<T>, "unhandled callback type");
}
}, callback_variant_);
TRACEPOINT(callback_end, static_cast<const void *>(this));
TRACETOOLS_TRACEPOINT(callback_end, static_cast<const void *>(this));
}
constexpr
@@ -965,9 +965,9 @@ public:
#ifndef TRACETOOLS_DISABLED
std::visit(
[this](auto && callback) {
if (TRACEPOINT_ENABLED(rclcpp_callback_register)) {
if (TRACETOOLS_TRACEPOINT_ENABLED(rclcpp_callback_register)) {
char * symbol = tracetools::get_symbol(callback);
DO_TRACEPOINT(
TRACETOOLS_DO_TRACEPOINT(
rclcpp_callback_register,
static_cast<const void *>(this),
symbol);

View File

@@ -20,13 +20,13 @@
#include <future>
#include <memory>
#include <mutex>
#include <optional> // NOLINT, cpplint doesn't think this is a cpp std header
#include <optional>
#include <sstream>
#include <string>
#include <tuple>
#include <unordered_map>
#include <utility>
#include <variant> // NOLINT
#include <variant>
#include <vector>
#include "rcl/client.h"
@@ -363,12 +363,16 @@ protected:
std::shared_ptr<rclcpp::Context> context_;
rclcpp::Logger node_logger_;
std::recursive_mutex callback_mutex_;
// It is important to declare on_new_response_callback_ before
// client_handle_, so on destruction the client is
// destroyed first. Otherwise, the rmw client callback
// would point briefly to a destroyed function.
std::function<void(size_t)> on_new_response_callback_{nullptr};
// Declare client_handle_ after callback
std::shared_ptr<rcl_client_t> client_handle_;
std::atomic<bool> in_use_by_wait_set_{false};
std::recursive_mutex callback_mutex_;
std::function<void(size_t)> on_new_response_callback_{nullptr};
};
template<typename ServiceT>
@@ -845,7 +849,7 @@ protected:
"Received invalid sequence number. Ignoring...");
return std::nullopt;
}
std::optional<CallbackInfoVariant> value = std::move(it->second.second);
auto value = std::move(it->second.second);
this->pending_requests_.erase(request_number);
return value;
}

View File

@@ -90,7 +90,8 @@ create_timer(
rclcpp::Clock::SharedPtr clock,
rclcpp::Duration period,
CallbackT && callback,
rclcpp::CallbackGroup::SharedPtr group = nullptr)
rclcpp::CallbackGroup::SharedPtr group = nullptr,
bool autostart = true)
{
return create_timer(
clock,
@@ -98,7 +99,8 @@ create_timer(
std::forward<CallbackT>(callback),
group,
node_base.get(),
node_timers.get());
node_timers.get(),
autostart);
}
/// Create a timer with a given clock
@@ -109,7 +111,8 @@ create_timer(
rclcpp::Clock::SharedPtr clock,
rclcpp::Duration period,
CallbackT && callback,
rclcpp::CallbackGroup::SharedPtr group = nullptr)
rclcpp::CallbackGroup::SharedPtr group = nullptr,
bool autostart = true)
{
return create_timer(
clock,
@@ -117,7 +120,8 @@ create_timer(
std::forward<CallbackT>(callback),
group,
rclcpp::node_interfaces::get_node_base_interface(node).get(),
rclcpp::node_interfaces::get_node_timers_interface(node).get());
rclcpp::node_interfaces::get_node_timers_interface(node).get(),
autostart);
}
/// Convenience method to create a general timer with node resources.
@@ -132,6 +136,7 @@ create_timer(
* \param group callback group
* \param node_base node base interface
* \param node_timers node timer interface
* \param autostart defines if the timer should start it's countdown on initialization or not.
* \return shared pointer to a generic timer
* \throws std::invalid_argument if either clock, node_base or node_timers
* are nullptr, or period is negative or too large
@@ -144,7 +149,8 @@ create_timer(
CallbackT callback,
rclcpp::CallbackGroup::SharedPtr group,
node_interfaces::NodeBaseInterface * node_base,
node_interfaces::NodeTimersInterface * node_timers)
node_interfaces::NodeTimersInterface * node_timers,
bool autostart = true)
{
if (clock == nullptr) {
throw std::invalid_argument{"clock cannot be null"};
@@ -160,7 +166,7 @@ create_timer(
// Add a new generic timer.
auto timer = rclcpp::GenericTimer<CallbackT>::make_shared(
std::move(clock), period_ns, std::move(callback), node_base->get_context());
std::move(clock), period_ns, std::move(callback), node_base->get_context(), autostart);
node_timers->add_timer(timer, group);
return timer;
}
@@ -187,7 +193,8 @@ create_wall_timer(
CallbackT callback,
rclcpp::CallbackGroup::SharedPtr group,
node_interfaces::NodeBaseInterface * node_base,
node_interfaces::NodeTimersInterface * node_timers)
node_interfaces::NodeTimersInterface * node_timers,
bool autostart = true)
{
if (node_base == nullptr) {
throw std::invalid_argument{"input node_base cannot be null"};
@@ -201,7 +208,7 @@ create_wall_timer(
// Add a new wall timer.
auto timer = rclcpp::WallTimer<CallbackT>::make_shared(
period_ns, std::move(callback), node_base->get_context());
period_ns, std::move(callback), node_base->get_context(), autostart);
node_timers->add_timer(timer, group);
return timer;
}

View File

@@ -260,16 +260,6 @@ public:
}
}
~EventHandler()
{
// Since the rmw event listener holds a reference to the
// "on ready" callback, we need to clear it on destruction of this class.
// This clearing is not needed for other rclcpp entities like pub/subs, since
// they do own the underlying rmw entities, which are destroyed
// on their rclcpp destructors, thus no risk of dangling pointers.
clear_on_ready_callback();
}
/// Take data so that the callback cannot be scheduled again
std::shared_ptr<void>
take_data() override

View File

@@ -206,6 +206,14 @@ public:
const std::vector<std::string> unknown_ros_args;
};
/// Thrown when an unknown type is passed
class UnknownTypeError : public std::runtime_error
{
public:
explicit UnknownTypeError(const std::string & type)
: std::runtime_error("Unknown type: " + type) {}
};
/// Thrown when an invalid rclcpp::Event object or SharedPtr is encountered.
class InvalidEventError : public std::runtime_error
{
@@ -222,6 +230,14 @@ public:
: std::runtime_error("event already registered") {}
};
/// Thrown when a callback group is missing from the node, when it wants to utilize the group.
class MissingGroupNodeException : public std::runtime_error
{
public:
explicit MissingGroupNodeException(const std::string & obj_type)
: std::runtime_error("cannot create: " + obj_type + " , callback group not in node") {}
};
/// Thrown if passed parameters are inconsistent or invalid
class InvalidParametersException : public std::runtime_error
{

View File

@@ -297,6 +297,21 @@ public:
virtual void
spin_some(std::chrono::nanoseconds max_duration = std::chrono::nanoseconds(0));
/// Add a node, complete all immediately available work exhaustively, and remove the node.
/**
* \param[in] node Shared pointer to the node to add.
*/
RCLCPP_PUBLIC
void
spin_node_all(
rclcpp::node_interfaces::NodeBaseInterface::SharedPtr node,
std::chrono::nanoseconds max_duration);
/// Convenience function which takes Node and forwards NodeBaseInterface.
RCLCPP_PUBLIC
void
spin_node_all(std::shared_ptr<rclcpp::Node> node, std::chrono::nanoseconds max_duration);
/// Collect and execute work repeatedly within a duration or until no more work is available.
/**
* This function can be overridden. The default implementation is suitable for a

View File

@@ -29,6 +29,18 @@
namespace rclcpp
{
/// Create a default single-threaded executor and execute all available work exhaustively.
/** \param[in] node_ptr Shared pointer to the node to spin. */
RCLCPP_PUBLIC
void
spin_all(
rclcpp::node_interfaces::NodeBaseInterface::SharedPtr node_ptr,
std::chrono::nanoseconds max_duration);
RCLCPP_PUBLIC
void
spin_all(rclcpp::Node::SharedPtr node_ptr, std::chrono::nanoseconds max_duration);
/// Create a default single-threaded executor and execute any immediately available work.
/** \param[in] node_ptr Shared pointer to the node to spin. */
RCLCPP_PUBLIC
@@ -108,9 +120,7 @@ spin_until_future_complete(
const FutureT & future,
std::chrono::duration<TimeRepT, TimeT> timeout = std::chrono::duration<TimeRepT, TimeT>(-1))
{
rclcpp::ExecutorOptions options;
options.context = node_ptr->get_context();
rclcpp::executors::SingleThreadedExecutor executor(options);
rclcpp::executors::SingleThreadedExecutor executor;
return executors::spin_node_until_future_complete<FutureT>(executor, node_ptr, future, timeout);
}

View File

@@ -33,6 +33,7 @@ public:
virtual void clear() = 0;
virtual bool has_data() const = 0;
virtual size_t available_capacity() const = 0;
};
} // namespace buffers

View File

@@ -44,6 +44,7 @@ public:
virtual bool has_data() const = 0;
virtual bool use_take_shared_method() const = 0;
virtual size_t available_capacity() const = 0;
};
template<
@@ -95,7 +96,7 @@ public:
buffer_ = std::move(buffer_impl);
TRACEPOINT(
TRACETOOLS_TRACEPOINT(
rclcpp_buffer_to_ipb,
static_cast<const void *>(buffer_.get()),
static_cast<const void *>(this));
@@ -143,6 +144,11 @@ public:
return std::is_same<BufferT, MessageSharedPtr>::value;
}
size_t available_capacity() const override
{
return buffer_->available_capacity();
}
private:
std::unique_ptr<BufferImplementationBase<BufferT>> buffer_;

View File

@@ -52,7 +52,10 @@ public:
if (capacity == 0) {
throw std::invalid_argument("capacity must be a positive, non-zero value");
}
TRACEPOINT(rclcpp_construct_ring_buffer, static_cast<const void *>(this), capacity_);
TRACETOOLS_TRACEPOINT(
rclcpp_construct_ring_buffer,
static_cast<const void *>(this),
capacity_);
}
virtual ~RingBufferImplementation() {}
@@ -69,7 +72,7 @@ public:
write_index_ = next_(write_index_);
ring_buffer_[write_index_] = std::move(request);
TRACEPOINT(
TRACETOOLS_TRACEPOINT(
rclcpp_ring_buffer_enqueue,
static_cast<const void *>(this),
write_index_,
@@ -98,7 +101,7 @@ public:
}
auto request = std::move(ring_buffer_[read_index_]);
TRACEPOINT(
TRACETOOLS_TRACEPOINT(
rclcpp_ring_buffer_dequeue,
static_cast<const void *>(this),
read_index_,
@@ -148,9 +151,21 @@ public:
return is_full_();
}
/// Get the remaining capacity to store messages
/**
* This member function is thread-safe.
*
* \return the number of free capacity for new messages
*/
size_t available_capacity() const
{
std::lock_guard<std::mutex> lock(mutex_);
return available_capacity_();
}
void clear()
{
TRACEPOINT(rclcpp_ring_buffer_clear, static_cast<const void *>(this));
TRACETOOLS_TRACEPOINT(rclcpp_ring_buffer_clear, static_cast<const void *>(this));
}
private:
@@ -189,6 +204,17 @@ private:
return size_ == capacity_;
}
/// Get the remaining capacity to store messages
/**
* This member function is not thread-safe.
*
* \return the number of free capacity for new messages
*/
inline size_t available_capacity_() const
{
return capacity_ - size_;
}
size_t capacity_;
std::vector<BufferT> ring_buffer_;

View File

@@ -274,9 +274,12 @@ private:
rclcpp::experimental::executors::EventsQueue::UniquePtr events_queue_;
std::shared_ptr<rclcpp::executors::ExecutorEntitiesCollector> entities_collector_;
std::shared_ptr<rclcpp::executors::ExecutorEntitiesCollection> current_entities_collection_;
std::shared_ptr<rclcpp::executors::ExecutorNotifyWaitable> notify_waitable_;
/// Mutex to protect the current_entities_collection_
std::recursive_mutex collection_mutex_;
std::shared_ptr<rclcpp::executors::ExecutorEntitiesCollection> current_entities_collection_;
/// Flag used to reduce the number of unnecessary waitable events
std::atomic<bool> notify_waitable_event_pushed_ {false};

View File

@@ -306,6 +306,11 @@ public:
rclcpp::experimental::SubscriptionIntraProcessBase::SharedPtr
get_subscription_intra_process(uint64_t intra_process_subscription_id);
/// Return the lowest available capacity for all subscription buffers for a publisher id.
RCLCPP_PUBLIC
size_t
lowest_available_capacity(const uint64_t intra_process_publisher_id) const;
private:
struct SplittedSubscriptions
{
@@ -481,13 +486,13 @@ private:
"subscription use different allocator types, which is not supported");
}
if constexpr (rclcpp::TypeAdapter<MessageT, ROSMessageType>::is_specialized::value) {
if constexpr (rclcpp::TypeAdapter<MessageT>::is_specialized::value) {
ROSMessageTypeAllocator ros_message_alloc(allocator);
auto ptr = ROSMessageTypeAllocatorTraits::allocate(ros_message_alloc, 1);
ROSMessageTypeAllocatorTraits::construct(ros_message_alloc, ptr);
auto ptr = ros_message_alloc.allocate(1);
ros_message_alloc.construct(ptr);
ROSMessageTypeDeleter deleter;
allocator::set_allocator_for_deleter(&deleter, &allocator);
rclcpp::TypeAdapter<MessageT, ROSMessageType>::convert_to_ros_message(*message, *ptr);
rclcpp::TypeAdapter<MessageT>::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

@@ -87,7 +87,7 @@ public:
buffer_type),
any_callback_(callback)
{
TRACEPOINT(
TRACETOOLS_TRACEPOINT(
rclcpp_subscription_callback_added,
static_cast<const void *>(this),
static_cast<const void *>(&any_callback_));

View File

@@ -62,6 +62,11 @@ public:
void
add_to_wait_set(rcl_wait_set_t * wait_set) override;
RCLCPP_PUBLIC
virtual
size_t
available_capacity() const = 0;
bool
is_ready(rcl_wait_set_t * wait_set) override = 0;

View File

@@ -93,7 +93,7 @@ public:
buffer_type,
qos_profile,
std::make_shared<Alloc>(subscribed_type_allocator_));
TRACEPOINT(
TRACETOOLS_TRACEPOINT(
rclcpp_ipb_to_subscription,
static_cast<const void *>(buffer_.get()),
static_cast<const void *>(this));
@@ -169,6 +169,11 @@ public:
return buffer_->use_take_shared_method();
}
size_t available_capacity() const override
{
return buffer_->available_capacity();
}
protected:
void
trigger_guard_condition() override

View File

@@ -233,13 +233,15 @@ public:
* \param[in] period Time interval between triggers of the callback.
* \param[in] callback User-defined callback function.
* \param[in] group Callback group to execute this timer's callback in.
* \param[in] autostart The state of the clock on initialization.
*/
template<typename DurationRepT = int64_t, typename DurationT = std::milli, typename CallbackT>
typename rclcpp::WallTimer<CallbackT>::SharedPtr
create_wall_timer(
std::chrono::duration<DurationRepT, DurationT> period,
CallbackT callback,
rclcpp::CallbackGroup::SharedPtr group = nullptr);
rclcpp::CallbackGroup::SharedPtr group = nullptr,
bool autostart = true);
/// Create a timer that uses the node clock to drive the callback.
/**
@@ -1592,16 +1594,18 @@ private:
rclcpp::node_interfaces::NodeClockInterface::SharedPtr node_clock_;
rclcpp::node_interfaces::NodeParametersInterface::SharedPtr node_parameters_;
rclcpp::node_interfaces::NodeTimeSourceInterface::SharedPtr node_time_source_;
rclcpp::node_interfaces::NodeTypeDescriptionsInterface::SharedPtr node_type_descriptions_;
rclcpp::node_interfaces::NodeWaitablesInterface::SharedPtr node_waitables_;
const rclcpp::NodeOptions node_options_;
const std::string sub_namespace_;
const std::string effective_namespace_;
/// Static map(s) containing extra member variables for Node without changing its ABI.
// See node.cpp for more details.
class BackportMembers;
static BackportMembers backport_members_;
class NodeImpl;
// This member is meant to be a place to backport features into stable distributions,
// and new features targeting Rolling should not use this.
// See the comment in node.cpp for more information.
std::shared_ptr<NodeImpl> hidden_impl_{nullptr};
};
} // namespace rclcpp

View File

@@ -110,14 +110,16 @@ typename rclcpp::WallTimer<CallbackT>::SharedPtr
Node::create_wall_timer(
std::chrono::duration<DurationRepT, DurationT> period,
CallbackT callback,
rclcpp::CallbackGroup::SharedPtr group)
rclcpp::CallbackGroup::SharedPtr group,
bool autostart)
{
return rclcpp::create_wall_timer(
period,
std::move(callback),
group,
this->node_base_.get(),
this->node_timers_.get());
this->node_timers_.get(),
autostart);
}
template<typename DurationRepT, typename DurationT, typename CallbackT>

View File

@@ -43,7 +43,6 @@ public:
* - arguments = {}
* - parameter_overrides = {}
* - use_global_arguments = true
* - enable_rosout = true
* - use_intra_process_comms = false
* - enable_topic_statistics = false
* - start_parameter_services = true

View File

@@ -48,7 +48,7 @@ public:
*
* Example Usage:
*
* If you have recieved a parameter event and are only interested in parameters foo and
* If you have received a parameter event and are only interested in parameters foo and
* bar being added or changed but don't care about deletion.
*
* ```cpp

View File

@@ -24,6 +24,7 @@
#include "rcl_interfaces/msg/parameter_type.hpp"
#include "rcl_interfaces/msg/parameter_value.hpp"
#include "rclcpp/exceptions/exceptions.hpp"
#include "rclcpp/visibility_control.hpp"
namespace rclcpp

View File

@@ -421,7 +421,7 @@ protected:
void
do_inter_process_publish(const ROSMessageType & msg)
{
TRACEPOINT(rclcpp_publish, nullptr, static_cast<const void *>(&msg));
TRACETOOLS_TRACEPOINT(rclcpp_publish, nullptr, static_cast<const void *>(&msg));
auto status = rcl_publish(publisher_handle_.get(), &msg, nullptr);
if (RCL_RET_PUBLISHER_INVALID == status) {
@@ -456,6 +456,7 @@ protected:
do_loaned_message_publish(
std::unique_ptr<ROSMessageType, std::function<void(ROSMessageType *)>> msg)
{
TRACETOOLS_TRACEPOINT(rclcpp_publish, nullptr, static_cast<const void *>(msg.get()));
auto status = rcl_publish_loaned_message(publisher_handle_.get(), msg.get(), nullptr);
if (RCL_RET_PUBLISHER_INVALID == status) {
@@ -484,7 +485,7 @@ protected:
if (!msg) {
throw std::runtime_error("cannot publish msg which is a null pointer");
}
TRACEPOINT(
TRACETOOLS_TRACEPOINT(
rclcpp_intra_publish,
static_cast<const void *>(publisher_handle_.get()),
msg.get());
@@ -506,7 +507,7 @@ protected:
if (!msg) {
throw std::runtime_error("cannot publish msg which is a null pointer");
}
TRACEPOINT(
TRACETOOLS_TRACEPOINT(
rclcpp_intra_publish,
static_cast<const void *>(publisher_handle_.get()),
msg.get());
@@ -529,7 +530,7 @@ protected:
if (!msg) {
throw std::runtime_error("cannot publish msg which is a null pointer");
}
TRACEPOINT(
TRACETOOLS_TRACEPOINT(
rclcpp_intra_publish,
static_cast<const void *>(publisher_handle_.get()),
msg.get());

View File

@@ -215,6 +215,17 @@ public:
std::vector<rclcpp::NetworkFlowEndpoint>
get_network_flow_endpoints() const;
/// Return the lowest available capacity for all subscription buffers.
/**
* For intraprocess communication return the lowest buffer capacity for all subscriptions.
* If intraprocess is disabled or no intraprocess subscriptions present, return maximum of size_t.
* On failure return 0.
* \return lowest buffer capacity for all subscriptions
*/
RCLCPP_PUBLIC
size_t
lowest_available_ipm_capacity() const;
/// Wait until all published messages are acknowledged or until the specified timeout elapses.
/**
* This method waits until all published messages are acknowledged by all matching

View File

@@ -265,15 +265,19 @@ protected:
std::shared_ptr<rcl_node_t> node_handle_;
std::recursive_mutex callback_mutex_;
// It is important to declare on_new_request_callback_ before
// service_handle_, so on destruction the service is
// destroyed first. Otherwise, the rmw service callback
// would point briefly to a destroyed function.
std::function<void(size_t)> on_new_request_callback_{nullptr};
// Declare service_handle_ after callback
std::shared_ptr<rcl_service_t> service_handle_;
bool owns_rcl_handle_ = true;
rclcpp::Logger node_logger_;
std::atomic<bool> in_use_by_wait_set_{false};
std::recursive_mutex callback_mutex_;
std::function<void(size_t)> on_new_request_callback_{nullptr};
};
template<typename ServiceT>
@@ -348,7 +352,7 @@ public:
rclcpp::exceptions::throw_from_rcl_error(ret, "could not create service");
}
TRACEPOINT(
TRACETOOLS_TRACEPOINT(
rclcpp_service_callback_added,
static_cast<const void *>(get_service_handle().get()),
static_cast<const void *>(&any_callback_));
@@ -383,7 +387,7 @@ public:
}
service_handle_ = service_handle;
TRACEPOINT(
TRACETOOLS_TRACEPOINT(
rclcpp_service_callback_added,
static_cast<const void *>(get_service_handle().get()),
static_cast<const void *>(&any_callback_));
@@ -420,7 +424,7 @@ public:
// In this case, rcl owns the service handle memory
service_handle_ = std::shared_ptr<rcl_service_t>(new rcl_service_t);
service_handle_->impl = service_handle->impl;
TRACEPOINT(
TRACETOOLS_TRACEPOINT(
rclcpp_service_callback_added,
static_cast<const void *>(get_service_handle().get()),
static_cast<const void *>(&any_callback_));
@@ -495,7 +499,7 @@ public:
}
}
/// Configure service introspection.
/// Configure client introspection.
/**
* \param[in] clock clock to use to generate introspection timestamps
* \param[in] qos_service_event_pub QoS settings to use when creating the introspection publisher

View File

@@ -185,7 +185,7 @@ public:
this->get_topic_name(), // important to get like this, as it has the fully-qualified name
qos_profile,
resolve_intra_process_buffer_type(options_.intra_process_buffer_type, callback));
TRACEPOINT(
TRACETOOLS_TRACEPOINT(
rclcpp_subscription_init,
static_cast<const void *>(get_subscription_handle().get()),
static_cast<const void *>(subscription_intra_process_.get()));
@@ -201,11 +201,11 @@ public:
this->subscription_topic_statistics_ = std::move(subscription_topic_statistics);
}
TRACEPOINT(
TRACETOOLS_TRACEPOINT(
rclcpp_subscription_init,
static_cast<const void *>(get_subscription_handle().get()),
static_cast<const void *>(this));
TRACEPOINT(
TRACETOOLS_TRACEPOINT(
rclcpp_subscription_callback_added,
static_cast<const void *>(this),
static_cast<const void *>(&any_callback_));

View File

@@ -645,6 +645,14 @@ protected:
rclcpp::node_interfaces::NodeBaseInterface * const node_base_;
std::shared_ptr<rcl_node_t> node_handle_;
std::recursive_mutex callback_mutex_;
// It is important to declare on_new_message_callback_ before
// subscription_handle_, so on destruction the subscription is
// destroyed first. Otherwise, the rmw subscription callback
// would point briefly to a destroyed function.
std::function<void(size_t)> on_new_message_callback_{nullptr};
// Declare subscription_handle_ after callback
std::shared_ptr<rcl_subscription_t> subscription_handle_;
std::shared_ptr<rcl_subscription_t> intra_process_subscription_handle_;
rclcpp::Logger node_logger_;
@@ -669,9 +677,6 @@ private:
std::atomic<bool> intra_process_subscription_waitable_in_use_by_wait_set_{false};
std::unordered_map<rclcpp::EventHandlerBase *,
std::atomic<bool>> qos_events_in_use_by_wait_set_;
std::recursive_mutex callback_mutex_;
std::function<void(size_t)> on_new_message_callback_{nullptr};
};
} // namespace rclcpp

View File

@@ -53,12 +53,17 @@ public:
* \param clock A clock to use for time and sleeping
* \param period The interval at which the timer fires
* \param context node context
* \param autostart timer state on initialization
*
* In order to activate a timer that is not started on initialization,
* user should call the reset() method.
*/
RCLCPP_PUBLIC
explicit TimerBase(
Clock::SharedPtr clock,
std::chrono::nanoseconds period,
rclcpp::Context::SharedPtr context);
rclcpp::Context::SharedPtr context,
bool autostart = true);
/// TimerBase destructor
RCLCPP_PUBLIC
@@ -216,21 +221,22 @@ public:
* \param[in] period The interval at which the timer fires.
* \param[in] callback User-specified callback function.
* \param[in] context custom context to be used.
* \param autostart timer state on initialization
*/
explicit GenericTimer(
Clock::SharedPtr clock, std::chrono::nanoseconds period, FunctorT && callback,
rclcpp::Context::SharedPtr context
rclcpp::Context::SharedPtr context, bool autostart = true
)
: TimerBase(clock, period, context), callback_(std::forward<FunctorT>(callback))
: TimerBase(clock, period, context, autostart), callback_(std::forward<FunctorT>(callback))
{
TRACEPOINT(
TRACETOOLS_TRACEPOINT(
rclcpp_timer_callback_added,
static_cast<const void *>(get_timer_handle().get()),
reinterpret_cast<const void *>(&callback_));
#ifndef TRACETOOLS_DISABLED
if (TRACEPOINT_ENABLED(rclcpp_callback_register)) {
if (TRACETOOLS_TRACEPOINT_ENABLED(rclcpp_callback_register)) {
char * symbol = tracetools::get_symbol(callback_);
DO_TRACEPOINT(
TRACETOOLS_DO_TRACEPOINT(
rclcpp_callback_register,
reinterpret_cast<const void *>(&callback_),
symbol);
@@ -269,9 +275,9 @@ public:
void
execute_callback() override
{
TRACEPOINT(callback_start, reinterpret_cast<const void *>(&callback_), false);
TRACETOOLS_TRACEPOINT(callback_start, reinterpret_cast<const void *>(&callback_), false);
execute_callback_delegate<>();
TRACEPOINT(callback_end, reinterpret_cast<const void *>(&callback_));
TRACETOOLS_TRACEPOINT(callback_end, reinterpret_cast<const void *>(&callback_));
}
// void specialization
@@ -330,13 +336,15 @@ public:
* \param period The interval at which the timer fires
* \param callback The callback function to execute every interval
* \param context node context
* \param autostart timer state on initialization
*/
WallTimer(
std::chrono::nanoseconds period,
FunctorT && callback,
rclcpp::Context::SharedPtr context)
rclcpp::Context::SharedPtr context,
bool autostart = true)
: GenericTimer<FunctorT>(
std::make_shared<Clock>(RCL_STEADY_TIME), period, std::move(callback), context)
std::make_shared<Clock>(RCL_STEADY_TIME), period, std::move(callback), context, autostart)
{}
protected:

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

View File

@@ -496,7 +496,7 @@ Context::sleep_for(const std::chrono::nanoseconds & nanoseconds)
std::unique_lock<std::mutex> lock(interrupt_mutex_);
auto start = std::chrono::steady_clock::now();
// this will release the lock while waiting
interrupt_condition_variable_.wait_for(lock, time_left);
interrupt_condition_variable_.wait_for(lock, nanoseconds);
time_left -= std::chrono::steady_clock::now() - start;
}
} while (time_left > std::chrono::nanoseconds::zero() && this->is_valid());

View File

@@ -39,6 +39,13 @@ UnsupportedEventTypeException::UnsupportedEventTypeException(
EventHandlerBase::~EventHandlerBase()
{
// Since the rmw event listener holds a reference to
// this callback, we need to clear it on destruction of this class.
// This clearing is not needed for other rclcpp entities like pub/subs, since
// they do own the underlying rmw entities, which are destroyed
// on their rclcpp destructors, thus no risk of dangling pointers.
clear_on_ready_callback();
if (rcl_event_fini(&event_handle_) != RCL_RET_OK) {
RCUTILS_LOG_ERROR_NAMED(
"rclcpp",

View File

@@ -431,6 +431,22 @@ void Executor::spin_some(std::chrono::nanoseconds max_duration)
return this->spin_some_impl(max_duration, false);
}
void
Executor::spin_node_all(
rclcpp::node_interfaces::NodeBaseInterface::SharedPtr node,
std::chrono::nanoseconds max_duration)
{
this->add_node(node, false);
spin_all(max_duration);
this->remove_node(node, false);
}
void
Executor::spin_node_all(std::shared_ptr<rclcpp::Node> node, std::chrono::nanoseconds max_duration)
{
this->spin_node_all(node->get_node_base_interface(), max_duration);
}
void Executor::spin_all(std::chrono::nanoseconds max_duration)
{
if (max_duration < 0ns) {
@@ -525,13 +541,13 @@ Executor::execute_any_executable(AnyExecutable & any_exec)
return;
}
if (any_exec.timer) {
TRACEPOINT(
TRACETOOLS_TRACEPOINT(
rclcpp_executor_execute,
static_cast<const void *>(any_exec.timer->get_timer_handle().get()));
execute_timer(any_exec.timer);
}
if (any_exec.subscription) {
TRACEPOINT(
TRACETOOLS_TRACEPOINT(
rclcpp_executor_execute,
static_cast<const void *>(any_exec.subscription->get_subscription_handle().get()));
execute_subscription(any_exec.subscription);
@@ -653,11 +669,6 @@ Executor::execute_subscription(rclcpp::SubscriptionBase::SharedPtr subscription)
subscription->get_topic_name(),
[&]() {return subscription->take_type_erased(message.get(), message_info);},
[&]() {subscription->handle_message(message, message_info);});
// TODO(clalancette): In the case that the user is using the MessageMemoryPool,
// and they take a shared_ptr reference to the message in the callback, this can
// inadvertently return the message to the pool when the user is still using it.
// This is a bug that needs to be fixed in the pool, and we should probably have
// a custom deleter for the message that actually does the return_message().
subscription->return_message(message);
}
break;
@@ -731,7 +742,7 @@ Executor::execute_client(
void
Executor::wait_for_work(std::chrono::nanoseconds timeout)
{
TRACEPOINT(rclcpp_executor_wait_for_work, timeout.count());
TRACETOOLS_TRACEPOINT(rclcpp_executor_wait_for_work, timeout.count());
{
std::lock_guard<std::mutex> guard(mutex_);
@@ -887,7 +898,7 @@ Executor::get_next_ready_executable_from_map(
const rclcpp::memory_strategy::MemoryStrategy::WeakCallbackGroupsToNodesMap &
weak_groups_to_nodes)
{
TRACEPOINT(rclcpp_executor_get_next_ready);
TRACETOOLS_TRACEPOINT(rclcpp_executor_get_next_ready);
bool success = false;
std::lock_guard<std::mutex> guard{mutex_};
// Check the timers to see if there are any that are ready

View File

@@ -14,12 +14,25 @@
#include "rclcpp/executors.hpp"
void
rclcpp::spin_all(
rclcpp::node_interfaces::NodeBaseInterface::SharedPtr node_ptr,
std::chrono::nanoseconds max_duration)
{
rclcpp::executors::SingleThreadedExecutor exec;
exec.spin_node_all(node_ptr, max_duration);
}
void
rclcpp::spin_all(rclcpp::Node::SharedPtr node_ptr, std::chrono::nanoseconds max_duration)
{
rclcpp::spin_all(node_ptr->get_node_base_interface(), max_duration);
}
void
rclcpp::spin_some(rclcpp::node_interfaces::NodeBaseInterface::SharedPtr node_ptr)
{
rclcpp::ExecutorOptions options;
options.context = node_ptr->get_context();
rclcpp::executors::SingleThreadedExecutor exec(options);
rclcpp::executors::SingleThreadedExecutor exec;
exec.spin_node_some(node_ptr);
}
@@ -32,9 +45,7 @@ rclcpp::spin_some(rclcpp::Node::SharedPtr node_ptr)
void
rclcpp::spin(rclcpp::node_interfaces::NodeBaseInterface::SharedPtr node_ptr)
{
rclcpp::ExecutorOptions options;
options.context = node_ptr->get_context();
rclcpp::executors::SingleThreadedExecutor exec(options);
rclcpp::executors::SingleThreadedExecutor exec;
exec.add_node(node_ptr);
exec.spin();
exec.remove_node(node_ptr);

View File

@@ -273,10 +273,13 @@ EventsExecutor::execute_event(const ExecutorEvent & event)
switch (event.type) {
case ExecutorEventType::CLIENT_EVENT:
{
auto client = this->retrieve_entity(
static_cast<const rcl_client_t *>(event.entity_key),
current_entities_collection_->clients);
rclcpp::ClientBase::SharedPtr client;
{
std::lock_guard<std::recursive_mutex> lock(collection_mutex_);
client = this->retrieve_entity(
static_cast<const rcl_client_t *>(event.entity_key),
current_entities_collection_->clients);
}
if (client) {
for (size_t i = 0; i < event.num_events; i++) {
execute_client(client);
@@ -287,9 +290,13 @@ EventsExecutor::execute_event(const ExecutorEvent & event)
}
case ExecutorEventType::SUBSCRIPTION_EVENT:
{
auto subscription = this->retrieve_entity(
static_cast<const rcl_subscription_t *>(event.entity_key),
current_entities_collection_->subscriptions);
rclcpp::SubscriptionBase::SharedPtr subscription;
{
std::lock_guard<std::recursive_mutex> lock(collection_mutex_);
subscription = this->retrieve_entity(
static_cast<const rcl_subscription_t *>(event.entity_key),
current_entities_collection_->subscriptions);
}
if (subscription) {
for (size_t i = 0; i < event.num_events; i++) {
execute_subscription(subscription);
@@ -299,10 +306,13 @@ EventsExecutor::execute_event(const ExecutorEvent & event)
}
case ExecutorEventType::SERVICE_EVENT:
{
auto service = this->retrieve_entity(
static_cast<const rcl_service_t *>(event.entity_key),
current_entities_collection_->services);
rclcpp::ServiceBase::SharedPtr service;
{
std::lock_guard<std::recursive_mutex> lock(collection_mutex_);
service = this->retrieve_entity(
static_cast<const rcl_service_t *>(event.entity_key),
current_entities_collection_->services);
}
if (service) {
for (size_t i = 0; i < event.num_events; i++) {
execute_service(service);
@@ -319,9 +329,13 @@ EventsExecutor::execute_event(const ExecutorEvent & event)
}
case ExecutorEventType::WAITABLE_EVENT:
{
auto waitable = this->retrieve_entity(
static_cast<const rclcpp::Waitable *>(event.entity_key),
current_entities_collection_->waitables);
rclcpp::Waitable::SharedPtr waitable;
{
std::lock_guard<std::recursive_mutex> lock(collection_mutex_);
waitable = this->retrieve_entity(
static_cast<const rclcpp::Waitable *>(event.entity_key),
current_entities_collection_->waitables);
}
if (waitable) {
for (size_t i = 0; i < event.num_events; i++) {
auto data = waitable->take_data_by_entity_id(event.waitable_data);
@@ -386,6 +400,7 @@ EventsExecutor::get_automatically_added_callback_groups_from_nodes()
void
EventsExecutor::refresh_current_collection_from_callback_groups()
{
// Build the new collection
this->entities_collector_->update_collections();
auto callback_groups = this->entities_collector_->get_all_callback_groups();
rclcpp::executors::ExecutorEntitiesCollection new_collection;
@@ -400,6 +415,9 @@ EventsExecutor::refresh_current_collection_from_callback_groups()
// To do it, we need to add the notify waitable as an entry in both the new and
// current collections such that it's neither added or removed.
this->add_notify_waitable_to_collection(new_collection.waitables);
// Acquire lock before modifying the current collection
std::lock_guard<std::recursive_mutex> lock(collection_mutex_);
this->add_notify_waitable_to_collection(current_entities_collection_->waitables);
this->refresh_current_collection(new_collection);
@@ -409,6 +427,9 @@ void
EventsExecutor::refresh_current_collection(
const rclcpp::executors::ExecutorEntitiesCollection & new_collection)
{
// Acquire lock before modifying the current collection
std::lock_guard<std::recursive_mutex> lock(collection_mutex_);
current_entities_collection_->timers.update(
new_collection.timers,
[this](rclcpp::TimerBase::SharedPtr timer) {timers_manager_->add_timer(timer);},

View File

@@ -225,5 +225,52 @@ IntraProcessManager::can_communicate(
return true;
}
size_t
IntraProcessManager::lowest_available_capacity(const uint64_t intra_process_publisher_id) const
{
size_t capacity = std::numeric_limits<size_t>::max();
auto publisher_it = pub_to_subs_.find(intra_process_publisher_id);
if (publisher_it == pub_to_subs_.end()) {
// Publisher is either invalid or no longer exists.
RCLCPP_WARN(
rclcpp::get_logger("rclcpp"),
"Calling lowest_available_capacity for invalid or no longer existing publisher id");
return 0u;
}
if (publisher_it->second.take_shared_subscriptions.empty() &&
publisher_it->second.take_ownership_subscriptions.empty())
{
// no subscriptions available
return 0u;
}
auto available_capacity = [this, &capacity](const uint64_t intra_process_subscription_id)
{
auto subscription_it = subscriptions_.find(intra_process_subscription_id);
if (subscription_it != subscriptions_.end()) {
auto subscription = subscription_it->second.lock();
if (subscription) {
capacity = std::min(capacity, subscription->available_capacity());
}
} else {
// Subscription is either invalid or no longer exists.
RCLCPP_WARN(
rclcpp::get_logger("rclcpp"),
"Calling available_capacity for invalid or no longer existing subscription id");
}
};
for (const auto sub_id : publisher_it->second.take_shared_subscriptions) {
available_capacity(sub_id);
}
for (const auto sub_id : publisher_it->second.take_ownership_subscriptions) {
available_capacity(sub_id);
}
return capacity;
}
} // namespace experimental
} // namespace rclcpp

View File

@@ -17,10 +17,7 @@
#include <limits>
#include <map>
#include <memory>
#include <mutex>
#include <shared_mutex>
#include <string>
#include <unordered_map>
#include <utility>
#include <vector>
@@ -113,71 +110,21 @@ create_effective_namespace(const std::string & node_namespace, const std::string
} // namespace
/// \brief Associate new extra member variables with instances of Node without changing ABI.
/// Internal implementation to provide hidden and API/ABI stable changes to the Node.
/**
* It is used only for bugfixes or backported features that require new members.
* Atomically constructs/destroys all extra members.
* Node instance will register and remove itself, and use its methods to retrieve members.
* Note for performance consideration that accessing these members uses a map lookup.
* This class is intended to be an "escape hatch" within a stable distribution, so that certain
* smaller features and bugfixes can be backported, having a place to put new members, while
* maintaining the ABI.
*
* This is not intended to be a parking place for new features, it should be used for backports
* only, left empty and unallocated in Rolling.
*/
class Node::BackportMembers
class Node::NodeImpl
{
public:
BackportMembers() = default;
~BackportMembers() = default;
/// \brief Add all backported members for a new Node.
/**
* \param[in] key Raw pointer to the Node instance that will use new members.
*/
void add(Node * key)
{
// Adding a new instance to the maps requires exclusive access
std::unique_lock lock(map_access_mutex_);
type_descriptions_map_.emplace(
key,
std::make_shared<rclcpp::node_interfaces::NodeTypeDescriptions>(
key->get_node_base_interface(),
key->get_node_logging_interface(),
key->get_node_parameters_interface(),
key->get_node_services_interface()));
}
/// \brief Remove the members for an instance of Node
/**
* \param[in] key Raw pointer to the Node
*/
void remove(const Node * key)
{
// Removing an instance from the maps requires exclusive access
std::unique_lock lock(map_access_mutex_);
type_descriptions_map_.erase(key);
}
/// \brief Retrieve the NodeTypeDescriptionsInterface for a Node.
/**
* \param[in] key Raw pointer to an instance of Node.
* \return A shared ptr to this Node's NodeTypeDescriptionsInterface instance.
*/
rclcpp::node_interfaces::NodeTypeDescriptionsInterface::SharedPtr
get_node_type_descriptions_interface(const Node * key) const
{
// Multiple threads can retrieve from the maps at the same time
std::shared_lock lock(map_access_mutex_);
return type_descriptions_map_.at(key);
}
private:
/// \brief Map that stored TypeDescriptionsInterface members
std::unordered_map<
const Node *, rclcpp::node_interfaces::NodeTypeDescriptionsInterface::SharedPtr
> type_descriptions_map_;
/// \brief Controls access to all private maps
mutable std::shared_mutex map_access_mutex_;
NodeImpl() = default;
~NodeImpl() = default;
};
// Definition of static member declaration
Node::BackportMembers Node::backport_members_;
Node::Node(
const std::string & node_name,
@@ -276,13 +223,17 @@ Node::Node(
options.clock_qos(),
options.use_clock_thread()
)),
node_type_descriptions_(new rclcpp::node_interfaces::NodeTypeDescriptions(
node_base_,
node_logging_,
node_parameters_,
node_services_
)),
node_waitables_(new rclcpp::node_interfaces::NodeWaitables(node_base_.get())),
node_options_(options),
sub_namespace_(""),
effective_namespace_(create_effective_namespace(this->get_namespace(), sub_namespace_))
{
backport_members_.add(this);
// we have got what we wanted directly from the overrides,
// but declare the parameters anyway so they are visible.
rclcpp::detail::declare_qos_parameters(
@@ -318,7 +269,8 @@ Node::Node(
node_waitables_(other.node_waitables_),
node_options_(other.node_options_),
sub_namespace_(extend_sub_namespace(other.get_sub_namespace(), sub_namespace)),
effective_namespace_(create_effective_namespace(other.get_namespace(), sub_namespace_))
effective_namespace_(create_effective_namespace(other.get_namespace(), sub_namespace_)),
hidden_impl_(other.hidden_impl_)
{
// Validate new effective namespace.
int validation_result;
@@ -344,7 +296,6 @@ Node::Node(
Node::~Node()
{
// release sub-interfaces in an order that allows them to consult with node_base during tear-down
backport_members_.remove(this);
node_waitables_.reset();
node_time_source_.reset();
node_parameters_.reset();
@@ -667,7 +618,7 @@ Node::get_node_topics_interface()
rclcpp::node_interfaces::NodeTypeDescriptionsInterface::SharedPtr
Node::get_node_type_descriptions_interface()
{
return backport_members_.get_node_type_descriptions_interface(this);
return node_type_descriptions_;
}
rclcpp::node_interfaces::NodeServicesInterface::SharedPtr

View File

@@ -20,6 +20,7 @@
#include "rclcpp/node_interfaces/node_base.hpp"
#include "rcl/arguments.h"
#include "rcl/node_type_cache.h"
#include "rclcpp/exceptions.hpp"
#include "rcutils/logging_macros.h"
#include "rmw/validate_namespace.h"

View File

@@ -32,8 +32,7 @@ NodeServices::add_service(
{
if (group) {
if (!node_base_->callback_group_in_node(group)) {
// TODO(jacquelinekay): use custom exception
throw std::runtime_error("Cannot create service, group not in node.");
throw rclcpp::exceptions::MissingGroupNodeException("service");
}
} else {
group = node_base_->get_default_callback_group();
@@ -58,8 +57,7 @@ NodeServices::add_client(
{
if (group) {
if (!node_base_->callback_group_in_node(group)) {
// TODO(jacquelinekay): use custom exception
throw std::runtime_error("Cannot create client, group not in node.");
throw rclcpp::exceptions::MissingGroupNodeException("client");
}
} else {
group = node_base_->get_default_callback_group();

View File

@@ -34,8 +34,7 @@ NodeTimers::add_timer(
{
if (callback_group) {
if (!node_base_->callback_group_in_node(callback_group)) {
// TODO(jacquelinekay): use custom exception
throw std::runtime_error("Cannot create timer, group not in node.");
throw rclcpp::exceptions::MissingGroupNodeException("timer");
}
} else {
callback_group = node_base_->get_default_callback_group();
@@ -50,7 +49,7 @@ NodeTimers::add_timer(
std::string("failed to notify wait set on timer creation: ") + ex.what());
}
TRACEPOINT(
TRACETOOLS_TRACEPOINT(
rclcpp_timer_link_node,
static_cast<const void *>(timer->get_timer_handle().get()),
static_cast<const void *>(node_base_->get_rcl_node_handle()));

View File

@@ -58,7 +58,7 @@ NodeTopics::add_publisher(
// Assign to a group.
if (callback_group) {
if (!node_base_->callback_group_in_node(callback_group)) {
throw std::runtime_error("Cannot create publisher, callback group not in node.");
throw rclcpp::exceptions::MissingGroupNodeException("publisher");
}
} else {
callback_group = node_base_->get_default_callback_group();
@@ -97,8 +97,7 @@ NodeTopics::add_subscription(
// Assign to a group.
if (callback_group) {
if (!node_base_->callback_group_in_node(callback_group)) {
// TODO(jacquelinekay): use custom exception
throw std::runtime_error("Cannot create subscription, callback group not in node.");
throw rclcpp::exceptions::MissingGroupNodeException("subscription");
}
} else {
callback_group = node_base_->get_default_callback_group();

View File

@@ -32,8 +32,7 @@ NodeWaitables::add_waitable(
{
if (group) {
if (!node_base_->callback_group_in_node(group)) {
// TODO(jacobperron): use custom exception
throw std::runtime_error("Cannot create waitable, group not in node.");
throw rclcpp::exceptions::MissingGroupNodeException("waitable");
}
} else {
group = node_base_->get_default_callback_group();

View File

@@ -129,8 +129,7 @@ ParameterValue::ParameterValue(const rcl_interfaces::msg::ParameterValue & value
case PARAMETER_NOT_SET:
break;
default:
// TODO(wjwwood): use custom exception
throw std::runtime_error("Unknown type: " + std::to_string(value.type));
throw rclcpp::exceptions::UnknownTypeError(std::to_string(value.type));
}
}

View File

@@ -384,3 +384,22 @@ std::vector<rclcpp::NetworkFlowEndpoint> PublisherBase::get_network_flow_endpoin
return network_flow_endpoint_vector;
}
size_t PublisherBase::lowest_available_ipm_capacity() const
{
if (!intra_process_is_enabled_) {
return 0u;
}
auto ipm = weak_ipm_.lock();
if (!ipm) {
// TODO(ivanpauno): should this raise an error?
RCLCPP_WARN(
rclcpp::get_logger("rclcpp"),
"Intra process manager died for a publisher.");
return 0u;
}
return ipm->lowest_available_capacity(intra_process_publisher_id_);
}

View File

@@ -113,7 +113,7 @@ SignalHandler::get_logger()
SignalHandler &
SignalHandler::get_global_signal_handler()
{
static SignalHandler & signal_handler = *new SignalHandler();
static SignalHandler signal_handler;
return signal_handler;
}

View File

@@ -218,7 +218,7 @@ SubscriptionBase::take_type_erased(void * message_out, rclcpp::MessageInfo & mes
&message_info_out.get_rmw_message_info(),
nullptr // rmw_subscription_allocation_t is unused here
);
TRACEPOINT(rclcpp_take, static_cast<const void *>(message_out));
TRACETOOLS_TRACEPOINT(rclcpp_take, static_cast<const void *>(message_out));
if (RCL_RET_SUBSCRIPTION_TAKE_FAILED == ret) {
return false;
} else if (RCL_RET_OK != ret) {
@@ -298,20 +298,7 @@ SubscriptionBase::setup_intra_process(
bool
SubscriptionBase::can_loan_messages() const
{
bool retval = rcl_subscription_can_loan_messages(subscription_handle_.get());
if (retval) {
// TODO(clalancette): The loaned message interface is currently not safe to use with
// shared_ptr callbacks. If a user takes a copy of the shared_ptr, it can get freed from
// underneath them via rcl_return_loaned_message_from_subscription(). The correct solution is
// to return the loaned message in a custom deleter, but that needs to be carefully handled
// with locking. Warn the user about this until we fix it.
RCLCPP_WARN_ONCE(
this->node_logger_,
"Loaned messages are only safe with const ref subscription callbacks. "
"If you are using any other kind of subscriptions, "
"set the ROS_DISABLE_LOANED_MESSAGES environment variable to 1 (the default).");
}
return retval;
return rcl_subscription_can_loan_messages(subscription_handle_.get());
}
rclcpp::Waitable::SharedPtr

View File

@@ -14,6 +14,7 @@
#include <memory>
#include <string>
#include <unordered_set>
#include <utility>
#include <vector>
@@ -90,7 +91,7 @@ public:
}
}
std::lock_guard<std::mutex> guard(clock_list_lock_);
associated_clocks_.push_back(clock);
associated_clocks_.insert(clock);
// Set the clock to zero unless there's a recently received message
set_clock(last_time_msg_, ros_time_active_, clock);
}
@@ -99,10 +100,8 @@ public:
void detachClock(rclcpp::Clock::SharedPtr clock)
{
std::lock_guard<std::mutex> guard(clock_list_lock_);
auto result = std::find(associated_clocks_.begin(), associated_clocks_.end(), clock);
if (result != associated_clocks_.end()) {
associated_clocks_.erase(result);
} else {
auto removed = associated_clocks_.erase(clock);
if (removed == 0) {
RCLCPP_ERROR(logger_, "failed to remove clock");
}
}
@@ -179,8 +178,8 @@ private:
// A lock to protect iterating the associated_clocks_ field.
std::mutex clock_list_lock_;
// A vector to store references to associated clocks.
std::vector<rclcpp::Clock::SharedPtr> associated_clocks_;
// An unordered_set to store references to associated clocks.
std::unordered_set<rclcpp::Clock::SharedPtr> associated_clocks_;
// Local storage of validity of ROS time
// This is needed when new clocks are added.

View File

@@ -32,7 +32,8 @@ using rclcpp::TimerBase;
TimerBase::TimerBase(
rclcpp::Clock::SharedPtr clock,
std::chrono::nanoseconds period,
rclcpp::Context::SharedPtr context)
rclcpp::Context::SharedPtr context,
bool autostart)
: clock_(clock), timer_handle_(nullptr)
{
if (nullptr == context) {
@@ -64,9 +65,9 @@ TimerBase::TimerBase(
rcl_clock_t * clock_handle = clock_->get_clock_handle();
{
std::lock_guard<std::mutex> clock_guard(clock_->get_clock_mutex());
rcl_ret_t ret = rcl_timer_init(
timer_handle_.get(), clock_handle, rcl_context.get(), period.count(), nullptr,
rcl_get_default_allocator());
rcl_ret_t ret = rcl_timer_init2(
timer_handle_.get(), clock_handle, rcl_context.get(), period.count(),
nullptr, rcl_get_default_allocator(), autostart);
if (ret != RCL_RET_OK) {
rclcpp::exceptions::throw_from_rcl_error(ret, "Couldn't initialize rcl timer handle");
}

View File

@@ -43,11 +43,6 @@ public:
TEST_F(TestEventsExecutor, run_pub_sub)
{
// rmw_connextdds doesn't support events-executor
if (std::string(rmw_get_implementation_identifier()).find("rmw_connextdds") == 0) {
GTEST_SKIP();
}
auto node = std::make_shared<rclcpp::Node>("node");
bool msg_received = false;
@@ -80,8 +75,6 @@ TEST_F(TestEventsExecutor, run_pub_sub)
!spin_exited &&
(std::chrono::high_resolution_clock::now() - start < 1s))
{
auto time = std::chrono::high_resolution_clock::now() - start;
auto time_msec = std::chrono::duration_cast<std::chrono::milliseconds>(time);
std::this_thread::sleep_for(25ms);
}
@@ -95,11 +88,6 @@ TEST_F(TestEventsExecutor, run_pub_sub)
TEST_F(TestEventsExecutor, run_clients_servers)
{
// rmw_connextdds doesn't support events-executor
if (std::string(rmw_get_implementation_identifier()).find("rmw_connextdds") == 0) {
GTEST_SKIP();
}
auto node = std::make_shared<rclcpp::Node>("node");
bool request_received = false;
@@ -153,11 +141,6 @@ TEST_F(TestEventsExecutor, run_clients_servers)
TEST_F(TestEventsExecutor, spin_once_max_duration_timeout)
{
// rmw_connextdds doesn't support events-executor
if (std::string(rmw_get_implementation_identifier()).find("rmw_connextdds") == 0) {
GTEST_SKIP();
}
auto node = std::make_shared<rclcpp::Node>("node");
EventsExecutor executor;
@@ -190,11 +173,6 @@ TEST_F(TestEventsExecutor, spin_once_max_duration_timeout)
TEST_F(TestEventsExecutor, spin_once_max_duration_timer)
{
// rmw_connextdds doesn't support events-executor
if (std::string(rmw_get_implementation_identifier()).find("rmw_connextdds") == 0) {
GTEST_SKIP();
}
auto node = std::make_shared<rclcpp::Node>("node");
EventsExecutor executor;
@@ -226,11 +204,6 @@ TEST_F(TestEventsExecutor, spin_once_max_duration_timer)
TEST_F(TestEventsExecutor, spin_some_max_duration)
{
// rmw_connextdds doesn't support events-executor
if (std::string(rmw_get_implementation_identifier()).find("rmw_connextdds") == 0) {
GTEST_SKIP();
}
{
auto node = std::make_shared<rclcpp::Node>("node");
@@ -277,11 +250,6 @@ TEST_F(TestEventsExecutor, spin_some_max_duration)
TEST_F(TestEventsExecutor, spin_some_zero_duration)
{
// rmw_connextdds doesn't support events-executor
if (std::string(rmw_get_implementation_identifier()).find("rmw_connextdds") == 0) {
GTEST_SKIP();
}
auto node = std::make_shared<rclcpp::Node>("node");
size_t t_runs = 0;
@@ -303,11 +271,6 @@ TEST_F(TestEventsExecutor, spin_some_zero_duration)
TEST_F(TestEventsExecutor, spin_all_max_duration)
{
// rmw_connextdds doesn't support events-executor
if (std::string(rmw_get_implementation_identifier()).find("rmw_connextdds") == 0) {
GTEST_SKIP();
}
{
auto node = std::make_shared<rclcpp::Node>("node");
@@ -358,11 +321,6 @@ TEST_F(TestEventsExecutor, spin_all_max_duration)
TEST_F(TestEventsExecutor, cancel_while_timers_running)
{
// rmw_connextdds doesn't support events-executor
if (std::string(rmw_get_implementation_identifier()).find("rmw_connextdds") == 0) {
GTEST_SKIP();
}
auto node = std::make_shared<rclcpp::Node>("node");
EventsExecutor executor;
@@ -402,11 +360,6 @@ TEST_F(TestEventsExecutor, cancel_while_timers_running)
TEST_F(TestEventsExecutor, cancel_while_timers_waiting)
{
// rmw_connextdds doesn't support events-executor
if (std::string(rmw_get_implementation_identifier()).find("rmw_connextdds") == 0) {
GTEST_SKIP();
}
auto node = std::make_shared<rclcpp::Node>("node");
size_t t1_runs = 0;
@@ -435,11 +388,6 @@ TEST_F(TestEventsExecutor, destroy_entities)
// This test fails on Windows! We skip it for now
GTEST_SKIP();
// rmw_connextdds doesn't support events-executor
if (std::string(rmw_get_implementation_identifier()).find("rmw_connextdds") == 0) {
GTEST_SKIP();
}
// Create a publisher node and start publishing messages
auto node_pub = std::make_shared<rclcpp::Node>("node_pub");
auto publisher = node_pub->create_publisher<test_msgs::msg::Empty>("topic", rclcpp::QoS(10));
@@ -485,11 +433,6 @@ std::string * g_sub_log_msg;
std::promise<void> * g_log_msgs_promise;
TEST_F(TestEventsExecutor, test_default_incompatible_qos_callbacks)
{
// rmw_connextdds doesn't support events-executor
if (std::string(rmw_get_implementation_identifier()).find("rmw_connextdds") == 0) {
GTEST_SKIP();
}
auto node = std::make_shared<rclcpp::Node>("node");
rcutils_logging_output_handler_t original_output_handler = rcutils_logging_get_output_handler();

View File

@@ -22,7 +22,6 @@
#include <algorithm>
#include <atomic>
#include <chrono>
#include <future>
#include <limits>
#include <memory>
#include <string>
@@ -136,14 +135,6 @@ TYPED_TEST_SUITE(TestExecutorsStable, StandardExecutors, ExecutorTypeNames);
TYPED_TEST(TestExecutors, detachOnDestruction)
{
using ExecutorType = TypeParam;
// rmw_connextdds doesn't support events-executor
if (
std::is_same<ExecutorType, rclcpp::experimental::executors::EventsExecutor>() &&
std::string(rmw_get_implementation_identifier()).find("rmw_connextdds") == 0)
{
GTEST_SKIP();
}
{
ExecutorType executor;
executor.add_node(this->node);
@@ -160,14 +151,6 @@ TYPED_TEST(TestExecutors, detachOnDestruction)
TYPED_TEST(TestExecutorsStable, addTemporaryNode)
{
using ExecutorType = TypeParam;
// rmw_connextdds doesn't support events-executor
if (
std::is_same<ExecutorType, rclcpp::experimental::executors::EventsExecutor>() &&
std::string(rmw_get_implementation_identifier()).find("rmw_connextdds") == 0)
{
GTEST_SKIP();
}
ExecutorType executor;
{
@@ -188,14 +171,6 @@ TYPED_TEST(TestExecutorsStable, addTemporaryNode)
TYPED_TEST(TestExecutors, emptyExecutor)
{
using ExecutorType = TypeParam;
// rmw_connextdds doesn't support events-executor
if (
std::is_same<ExecutorType, rclcpp::experimental::executors::EventsExecutor>() &&
std::string(rmw_get_implementation_identifier()).find("rmw_connextdds") == 0)
{
GTEST_SKIP();
}
ExecutorType executor;
std::thread spinner([&]() {EXPECT_NO_THROW(executor.spin());});
std::this_thread::sleep_for(50ms);
@@ -207,14 +182,6 @@ TYPED_TEST(TestExecutors, emptyExecutor)
TYPED_TEST(TestExecutors, addNodeTwoExecutors)
{
using ExecutorType = TypeParam;
// rmw_connextdds doesn't support events-executor
if (
std::is_same<ExecutorType, rclcpp::experimental::executors::EventsExecutor>() &&
std::string(rmw_get_implementation_identifier()).find("rmw_connextdds") == 0)
{
GTEST_SKIP();
}
ExecutorType executor1;
ExecutorType executor2;
EXPECT_NO_THROW(executor1.add_node(this->node));
@@ -226,14 +193,6 @@ TYPED_TEST(TestExecutors, addNodeTwoExecutors)
TYPED_TEST(TestExecutors, spinWithTimer)
{
using ExecutorType = TypeParam;
// rmw_connextdds doesn't support events-executor
if (
std::is_same<ExecutorType, rclcpp::experimental::executors::EventsExecutor>() &&
std::string(rmw_get_implementation_identifier()).find("rmw_connextdds") == 0)
{
GTEST_SKIP();
}
ExecutorType executor;
bool timer_completed = false;
@@ -257,14 +216,6 @@ TYPED_TEST(TestExecutors, spinWithTimer)
TYPED_TEST(TestExecutors, spinWhileAlreadySpinning)
{
using ExecutorType = TypeParam;
// rmw_connextdds doesn't support events-executor
if (
std::is_same<ExecutorType, rclcpp::experimental::executors::EventsExecutor>() &&
std::string(rmw_get_implementation_identifier()).find("rmw_connextdds") == 0)
{
GTEST_SKIP();
}
ExecutorType executor;
executor.add_node(this->node);
@@ -292,14 +243,6 @@ TYPED_TEST(TestExecutors, spinWhileAlreadySpinning)
TYPED_TEST(TestExecutors, testSpinUntilFutureComplete)
{
using ExecutorType = TypeParam;
// rmw_connextdds doesn't support events-executor
if (
std::is_same<ExecutorType, rclcpp::experimental::executors::EventsExecutor>() &&
std::string(rmw_get_implementation_identifier()).find("rmw_connextdds") == 0)
{
GTEST_SKIP();
}
ExecutorType executor;
executor.add_node(this->node);
@@ -323,14 +266,6 @@ TYPED_TEST(TestExecutors, testSpinUntilFutureComplete)
TYPED_TEST(TestExecutors, testSpinUntilSharedFutureComplete)
{
using ExecutorType = TypeParam;
// rmw_connextdds doesn't support events-executor
if (
std::is_same<ExecutorType, rclcpp::experimental::executors::EventsExecutor>() &&
std::string(rmw_get_implementation_identifier()).find("rmw_connextdds") == 0)
{
GTEST_SKIP();
}
ExecutorType executor;
executor.add_node(this->node);
@@ -355,14 +290,6 @@ TYPED_TEST(TestExecutors, testSpinUntilSharedFutureComplete)
TYPED_TEST(TestExecutors, testSpinUntilFutureCompleteNoTimeout)
{
using ExecutorType = TypeParam;
// rmw_connextdds doesn't support events-executor
if (
std::is_same<ExecutorType, rclcpp::experimental::executors::EventsExecutor>() &&
std::string(rmw_get_implementation_identifier()).find("rmw_connextdds") == 0)
{
GTEST_SKIP();
}
ExecutorType executor;
executor.add_node(this->node);
@@ -410,14 +337,6 @@ TYPED_TEST(TestExecutors, testSpinUntilFutureCompleteNoTimeout)
TYPED_TEST(TestExecutors, testSpinUntilFutureCompleteWithTimeout)
{
using ExecutorType = TypeParam;
// rmw_connextdds doesn't support events-executor
if (
std::is_same<ExecutorType, rclcpp::experimental::executors::EventsExecutor>() &&
std::string(rmw_get_implementation_identifier()).find("rmw_connextdds") == 0)
{
GTEST_SKIP();
}
ExecutorType executor;
executor.add_node(this->node);
@@ -530,14 +449,6 @@ private:
TYPED_TEST(TestExecutors, spinAll)
{
using ExecutorType = TypeParam;
// rmw_connextdds doesn't support events-executor
if (
std::is_same<ExecutorType, rclcpp::experimental::executors::EventsExecutor>() &&
std::string(rmw_get_implementation_identifier()).find("rmw_connextdds") == 0)
{
GTEST_SKIP();
}
ExecutorType executor;
auto waitable_interfaces = this->node->get_node_waitables_interface();
auto my_waitable = std::make_shared<TestWaitable>();
@@ -580,14 +491,6 @@ TYPED_TEST(TestExecutors, spinAll)
TYPED_TEST(TestExecutors, spinSome)
{
using ExecutorType = TypeParam;
// rmw_connextdds doesn't support events-executor
if (
std::is_same<ExecutorType, rclcpp::experimental::executors::EventsExecutor>() &&
std::string(rmw_get_implementation_identifier()).find("rmw_connextdds") == 0)
{
GTEST_SKIP();
}
ExecutorType executor;
auto waitable_interfaces = this->node->get_node_waitables_interface();
auto my_waitable = std::make_shared<TestWaitable>();
@@ -630,14 +533,6 @@ TYPED_TEST(TestExecutors, spinSome)
TYPED_TEST(TestExecutors, testSpinNodeUntilFutureCompleteNodeBasePtr)
{
using ExecutorType = TypeParam;
// rmw_connextdds doesn't support events-executor
if (
std::is_same<ExecutorType, rclcpp::experimental::executors::EventsExecutor>() &&
std::string(rmw_get_implementation_identifier()).find("rmw_connextdds") == 0)
{
GTEST_SKIP();
}
ExecutorType executor;
std::promise<bool> promise;
@@ -654,14 +549,6 @@ TYPED_TEST(TestExecutors, testSpinNodeUntilFutureCompleteNodeBasePtr)
TYPED_TEST(TestExecutors, testSpinNodeUntilFutureCompleteNodePtr)
{
using ExecutorType = TypeParam;
// rmw_connextdds doesn't support events-executor
if (
std::is_same<ExecutorType, rclcpp::experimental::executors::EventsExecutor>() &&
std::string(rmw_get_implementation_identifier()).find("rmw_connextdds") == 0)
{
GTEST_SKIP();
}
ExecutorType executor;
std::promise<bool> promise;
@@ -678,14 +565,6 @@ TYPED_TEST(TestExecutors, testSpinNodeUntilFutureCompleteNodePtr)
TYPED_TEST(TestExecutors, testSpinUntilFutureCompleteInterrupted)
{
using ExecutorType = TypeParam;
// rmw_connextdds doesn't support events-executor
if (
std::is_same<ExecutorType, rclcpp::experimental::executors::EventsExecutor>() &&
std::string(rmw_get_implementation_identifier()).find("rmw_connextdds") == 0)
{
GTEST_SKIP();
}
ExecutorType executor;
executor.add_node(this->node);
@@ -940,31 +819,3 @@ TYPED_TEST(TestIntraprocessExecutors, testIntraprocessRetrigger) {
executor.spin();
EXPECT_EQ(kNumMessages, this->callback_count.load());
}
// Check spin functions with non default context
TEST(TestExecutors, testSpinWithNonDefaultContext)
{
auto non_default_context = std::make_shared<rclcpp::Context>();
non_default_context->init(0, nullptr);
{
auto node =
std::make_unique<rclcpp::Node>("node", rclcpp::NodeOptions().context(non_default_context));
EXPECT_NO_THROW(rclcpp::spin_some(node->get_node_base_interface()));
auto check_spin_until_future_complete = [&]() {
std::promise<bool> promise;
std::future<bool> future = promise.get_future();
promise.set_value(true);
auto shared_future = future.share();
auto ret = rclcpp::spin_until_future_complete(
node->get_node_base_interface(), shared_future, 1s);
EXPECT_EQ(rclcpp::FutureReturnCode::SUCCESS, ret);
};
EXPECT_NO_THROW(check_spin_until_future_complete());
}
rclcpp::shutdown(non_default_context);
}

View File

@@ -92,7 +92,7 @@ TEST_F(TestNodeService, add_service)
different_node->create_callback_group(rclcpp::CallbackGroupType::MutuallyExclusive);
RCLCPP_EXPECT_THROW_EQ(
node_services->add_service(service, callback_group_in_different_node),
std::runtime_error("Cannot create service, group not in node."));
rclcpp::exceptions::MissingGroupNodeException("service"));
}
TEST_F(TestNodeService, add_service_rcl_trigger_guard_condition_error)
@@ -119,7 +119,7 @@ TEST_F(TestNodeService, add_client)
different_node->create_callback_group(rclcpp::CallbackGroupType::MutuallyExclusive);
RCLCPP_EXPECT_THROW_EQ(
node_services->add_client(client, callback_group_in_different_node),
std::runtime_error("Cannot create client, group not in node."));
rclcpp::exceptions::MissingGroupNodeException("client"));
}
TEST_F(TestNodeService, add_client_rcl_trigger_guard_condition_error)

View File

@@ -75,7 +75,7 @@ TEST_F(TestNodeTimers, add_timer)
different_node->create_callback_group(rclcpp::CallbackGroupType::MutuallyExclusive);
RCLCPP_EXPECT_THROW_EQ(
node_timers->add_timer(timer, callback_group_in_different_node),
std::runtime_error("Cannot create timer, group not in node."));
rclcpp::exceptions::MissingGroupNodeException("timer"));
}
TEST_F(TestNodeTimers, add_timer_rcl_trigger_guard_condition_error)

View File

@@ -78,7 +78,7 @@ TEST_F(TestNodeWaitables, add_remove_waitable)
node_waitables->add_waitable(waitable, callback_group1));
RCLCPP_EXPECT_THROW_EQ(
node_waitables->add_waitable(waitable, callback_group2),
std::runtime_error("Cannot create waitable, group not in node."));
rclcpp::exceptions::MissingGroupNodeException("waitable"));
EXPECT_NO_THROW(node_waitables->remove_waitable(waitable, callback_group1));
EXPECT_NO_THROW(node_waitables->remove_waitable(waitable, callback_group2));

View File

@@ -99,13 +99,6 @@ TYPED_TEST_SUITE(TestAddCallbackGroupsToExecutorStable, StandardExecutors, Execu
TYPED_TEST(TestAddCallbackGroupsToExecutor, add_callback_groups)
{
using ExecutorType = TypeParam;
// rmw_connextdds doesn't support events-executor
if (
std::is_same<ExecutorType, rclcpp::experimental::executors::EventsExecutor>() &&
std::string(rmw_get_implementation_identifier()).find("rmw_connextdds") == 0)
{
GTEST_SKIP();
}
auto node = std::make_shared<rclcpp::Node>("my_node", "/ns");
auto timer_callback = []() {};
@@ -155,13 +148,6 @@ TYPED_TEST(TestAddCallbackGroupsToExecutor, add_callback_groups)
TYPED_TEST(TestAddCallbackGroupsToExecutor, remove_callback_groups)
{
using ExecutorType = TypeParam;
// rmw_connextdds doesn't support events-executor
if (
std::is_same<ExecutorType, rclcpp::experimental::executors::EventsExecutor>() &&
std::string(rmw_get_implementation_identifier()).find("rmw_connextdds") == 0)
{
GTEST_SKIP();
}
auto node = std::make_shared<rclcpp::Node>("my_node", "/ns");
auto timer_callback = []() {};
@@ -193,13 +179,6 @@ TYPED_TEST(TestAddCallbackGroupsToExecutor, remove_callback_groups)
TYPED_TEST(TestAddCallbackGroupsToExecutor, add_duplicate_callback_groups)
{
using ExecutorType = TypeParam;
// rmw_connextdds doesn't support events-executor
if (
std::is_same<ExecutorType, rclcpp::experimental::executors::EventsExecutor>() &&
std::string(rmw_get_implementation_identifier()).find("rmw_connextdds") == 0)
{
GTEST_SKIP();
}
ExecutorType executor;
auto node = std::make_shared<rclcpp::Node>("my_node", "/ns");
@@ -220,13 +199,6 @@ TYPED_TEST(TestAddCallbackGroupsToExecutor, add_duplicate_callback_groups)
TYPED_TEST(TestAddCallbackGroupsToExecutor, add_callback_groups_after_add_node_to_executor)
{
using ExecutorType = TypeParam;
// rmw_connextdds doesn't support events-executor
if (
std::is_same<ExecutorType, rclcpp::experimental::executors::EventsExecutor>() &&
std::string(rmw_get_implementation_identifier()).find("rmw_connextdds") == 0)
{
GTEST_SKIP();
}
ExecutorType executor;
auto node = std::make_shared<rclcpp::Node>("my_node", "/ns");
@@ -263,13 +235,6 @@ TYPED_TEST(TestAddCallbackGroupsToExecutor, add_callback_groups_after_add_node_t
TYPED_TEST(TestAddCallbackGroupsToExecutor, add_unallowable_callback_groups)
{
using ExecutorType = TypeParam;
// rmw_connextdds doesn't support events-executor
if (
std::is_same<ExecutorType, rclcpp::experimental::executors::EventsExecutor>() &&
std::string(rmw_get_implementation_identifier()).find("rmw_connextdds") == 0)
{
GTEST_SKIP();
}
ExecutorType executor;
auto node = std::make_shared<rclcpp::Node>("my_node", "/ns");
@@ -307,13 +272,6 @@ TYPED_TEST(TestAddCallbackGroupsToExecutor, add_unallowable_callback_groups)
TYPED_TEST(TestAddCallbackGroupsToExecutor, one_node_many_callback_groups_many_executors)
{
using ExecutorType = TypeParam;
// rmw_connextdds doesn't support events-executor
if (
std::is_same<ExecutorType, rclcpp::experimental::executors::EventsExecutor>() &&
std::string(rmw_get_implementation_identifier()).find("rmw_connextdds") == 0)
{
GTEST_SKIP();
}
ExecutorType timer_executor;
ExecutorType sub_executor;
@@ -355,13 +313,6 @@ TYPED_TEST(TestAddCallbackGroupsToExecutor, one_node_many_callback_groups_many_e
TYPED_TEST(TestAddCallbackGroupsToExecutorStable, subscriber_triggered_to_receive_message)
{
using ExecutorType = TypeParam;
// rmw_connextdds doesn't support events-executor
if (
std::is_same<ExecutorType, rclcpp::experimental::executors::EventsExecutor>() &&
std::string(rmw_get_implementation_identifier()).find("rmw_connextdds") == 0)
{
GTEST_SKIP();
}
auto node = std::make_shared<rclcpp::Node>("my_node", "/ns");
@@ -428,13 +379,6 @@ TYPED_TEST(TestAddCallbackGroupsToExecutorStable, subscriber_triggered_to_receiv
TYPED_TEST(TestAddCallbackGroupsToExecutorStable, callback_group_create_after_spin)
{
using ExecutorType = TypeParam;
// rmw_connextdds doesn't support events-executor
if (
std::is_same<ExecutorType, rclcpp::experimental::executors::EventsExecutor>() &&
std::string(rmw_get_implementation_identifier()).find("rmw_connextdds") == 0)
{
GTEST_SKIP();
}
auto node = std::make_shared<rclcpp::Node>("my_node", "/ns");
@@ -481,13 +425,6 @@ TYPED_TEST(TestAddCallbackGroupsToExecutorStable, callback_group_create_after_sp
TYPED_TEST(TestAddCallbackGroupsToExecutor, remove_callback_group)
{
using ExecutorType = TypeParam;
// rmw_connextdds doesn't support events-executor
if (
std::is_same<ExecutorType, rclcpp::experimental::executors::EventsExecutor>() &&
std::string(rmw_get_implementation_identifier()).find("rmw_connextdds") == 0)
{
GTEST_SKIP();
}
ExecutorType executor;
auto node = std::make_shared<rclcpp::Node>("my_node", "/ns");

View File

@@ -521,10 +521,6 @@ TEST_F(TestClient, rcl_client_response_subscription_get_actual_qos_error) {
}
TEST_F(TestClient, client_qos) {
if (std::string(rmw_get_implementation_identifier()).find("rmw_connextdds") == 0) {
GTEST_SKIP() << "Skipping. The tests is known to be flaky in the rmw_connextdds.";
}
rclcpp::ServicesQoS qos_profile;
qos_profile.liveliness(rclcpp::LivelinessPolicy::Automatic);
rclcpp::Duration duration(std::chrono::nanoseconds(1));

View File

@@ -193,3 +193,29 @@ TEST(TestCreateTimer, timer_function_pointer)
rclcpp::shutdown();
}
TEST(TestCreateTimer, timer_without_autostart)
{
rclcpp::init(0, nullptr);
auto node = std::make_shared<rclcpp::Node>("test_create_timer_node");
rclcpp::TimerBase::SharedPtr timer;
timer = rclcpp::create_timer(
node,
node->get_clock(),
rclcpp::Duration(0ms),
[]() {},
nullptr,
false);
EXPECT_TRUE(timer->is_canceled());
EXPECT_EQ(timer->time_until_trigger().count(), std::chrono::nanoseconds::max().count());
timer->reset();
EXPECT_LE(timer->time_until_trigger().count(), std::chrono::nanoseconds::max().count());
EXPECT_FALSE(timer->is_canceled());
timer->cancel();
rclcpp::shutdown();
}

View File

@@ -546,6 +546,40 @@ TEST_F(TestExecutor, spin_node_once_node) {
EXPECT_TRUE(spin_called);
}
TEST_F(TestExecutor, spin_node_all_base_interface) {
DummyExecutor dummy;
auto node = std::make_shared<rclcpp::Node>("node", "ns");
bool spin_called = false;
auto timer =
node->create_wall_timer(
std::chrono::milliseconds(1), [&]() {
spin_called = true;
});
// Wait for the wall timer to have expired.
std::this_thread::sleep_for(std::chrono::milliseconds(50));
EXPECT_FALSE(spin_called);
dummy.spin_node_all(node->get_node_base_interface(), std::chrono::milliseconds(50));
EXPECT_TRUE(spin_called);
}
TEST_F(TestExecutor, spin_node_all_node) {
DummyExecutor dummy;
auto node = std::make_shared<rclcpp::Node>("node", "ns");
bool spin_called = false;
auto timer =
node->create_wall_timer(
std::chrono::milliseconds(1), [&]() {
spin_called = true;
});
// Wait for the wall timer to have expired.
std::this_thread::sleep_for(std::chrono::milliseconds(50));
EXPECT_FALSE(spin_called);
dummy.spin_node_all(node, std::chrono::milliseconds(50));
EXPECT_TRUE(spin_called);
}
TEST_F(TestExecutor, spin_until_future_complete_future_already_complete) {
DummyExecutor dummy;
auto node = std::make_shared<rclcpp::Node>("node", "ns");

View File

@@ -238,3 +238,75 @@ TEST(TestIntraProcessBuffer, unique_buffer_consume) {
EXPECT_EQ(original_value, *popped_unique_msg);
EXPECT_EQ(original_message_pointer, popped_message_pointer);
}
/*
Check the available buffer capacity while storing and consuming data from an intra-process
buffer.
The initial available buffer capacity should equal the buffer size.
Inserting a message should decrease the available buffer capacity by 1.
Consuming a message should increase the available buffer capacity by 1.
*/
TEST(TestIntraProcessBuffer, available_capacity) {
using MessageT = char;
using Alloc = std::allocator<void>;
using Deleter = std::default_delete<MessageT>;
using SharedMessageT = std::shared_ptr<const MessageT>;
using UniqueMessageT = std::unique_ptr<MessageT, Deleter>;
using UniqueIntraProcessBufferT = rclcpp::experimental::buffers::TypedIntraProcessBuffer<
MessageT, Alloc, Deleter, UniqueMessageT>;
constexpr auto history_depth = 5u;
auto buffer_impl =
std::make_unique<rclcpp::experimental::buffers::RingBufferImplementation<UniqueMessageT>>(
history_depth);
UniqueIntraProcessBufferT intra_process_buffer(std::move(buffer_impl));
EXPECT_EQ(history_depth, intra_process_buffer.available_capacity());
auto original_unique_msg = std::make_unique<char>('a');
auto original_message_pointer = reinterpret_cast<std::uintptr_t>(original_unique_msg.get());
auto original_value = *original_unique_msg;
intra_process_buffer.add_unique(std::move(original_unique_msg));
EXPECT_EQ(history_depth - 1u, intra_process_buffer.available_capacity());
SharedMessageT popped_shared_msg;
popped_shared_msg = intra_process_buffer.consume_shared();
auto popped_message_pointer = reinterpret_cast<std::uintptr_t>(popped_shared_msg.get());
EXPECT_EQ(history_depth, intra_process_buffer.available_capacity());
EXPECT_EQ(original_value, *popped_shared_msg);
EXPECT_EQ(original_message_pointer, popped_message_pointer);
original_unique_msg = std::make_unique<char>('b');
original_message_pointer = reinterpret_cast<std::uintptr_t>(original_unique_msg.get());
original_value = *original_unique_msg;
intra_process_buffer.add_unique(std::move(original_unique_msg));
auto second_unique_msg = std::make_unique<char>('c');
auto second_message_pointer = reinterpret_cast<std::uintptr_t>(second_unique_msg.get());
auto second_value = *second_unique_msg;
intra_process_buffer.add_unique(std::move(second_unique_msg));
EXPECT_EQ(history_depth - 2u, intra_process_buffer.available_capacity());
UniqueMessageT popped_unique_msg;
popped_unique_msg = intra_process_buffer.consume_unique();
popped_message_pointer = reinterpret_cast<std::uintptr_t>(popped_unique_msg.get());
EXPECT_EQ(history_depth - 1u, intra_process_buffer.available_capacity());
EXPECT_EQ(original_value, *popped_unique_msg);
EXPECT_EQ(original_message_pointer, popped_message_pointer);
popped_unique_msg = intra_process_buffer.consume_unique();
popped_message_pointer = reinterpret_cast<std::uintptr_t>(popped_unique_msg.get());
EXPECT_EQ(history_depth, intra_process_buffer.available_capacity());
EXPECT_EQ(second_value, *popped_unique_msg);
EXPECT_EQ(second_message_pointer, popped_message_pointer);
}

View File

@@ -156,18 +156,26 @@ public:
{
message_ptr = reinterpret_cast<std::uintptr_t>(msg.get());
shared_msg = msg;
++num_msgs;
}
void add(MessageUniquePtr msg)
{
message_ptr = reinterpret_cast<std::uintptr_t>(msg.get());
unique_msg = std::move(msg);
++num_msgs;
}
void pop(std::uintptr_t & msg_ptr)
{
msg_ptr = message_ptr;
message_ptr = 0;
--num_msgs;
}
size_t size() const
{
return num_msgs;
}
// need to store the messages somewhere otherwise the memory address will be reused
@@ -175,6 +183,8 @@ public:
MessageUniquePtr unique_msg;
std::uintptr_t message_ptr;
// count add and pop
size_t num_msgs = 0u;
};
} // namespace mock
@@ -221,6 +231,10 @@ public:
return topic_name.c_str();
}
virtual
size_t
available_capacity() const = 0;
rclcpp::QoS qos_profile;
std::string topic_name;
};
@@ -280,6 +294,12 @@ public:
return take_shared_method;
}
size_t
available_capacity() const override
{
return qos_profile.depth() - buffer->size();
}
bool take_shared_method;
typename rclcpp::experimental::buffers::mock::IntraProcessBuffer<MessageT>::UniquePtr buffer;
@@ -712,3 +732,91 @@ TEST(TestIntraProcessManager, multiple_subscriptions_different_type) {
EXPECT_EQ(original_message_pointer, received_message_pointer_10);
EXPECT_NE(original_message_pointer, received_message_pointer_11);
}
/*
This tests the method "lowest_available_capacity":
- Creates 1 publisher.
- The available buffer capacity should be at least history size.
- Add 2 subscribers.
- Add everything to the intra-process manager.
- All the entities are expected to have different ids.
- Check the subscriptions count for the publisher.
- The available buffer capacity should be the history size.
- Publish one message (without receiving it).
- The available buffer capacity should decrease by 1.
- Publish another message (without receiving it).
- The available buffer capacity should decrease by 1.
- One subscriber receives one message.
- The available buffer capacity should stay the same,
as the other subscriber still has not freed its buffer.
- The other subscriber receives one message.
- The available buffer capacity should increase by 1.
- One subscription goes out of scope.
- The available buffer capacity should not change.
*/
TEST(TestIntraProcessManager, lowest_available_capacity) {
using IntraProcessManagerT = rclcpp::experimental::IntraProcessManager;
using MessageT = rcl_interfaces::msg::Log;
using PublisherT = rclcpp::mock::Publisher<MessageT>;
using SubscriptionIntraProcessT = rclcpp::experimental::mock::SubscriptionIntraProcess<MessageT>;
constexpr auto history_depth = 10u;
auto ipm = std::make_shared<IntraProcessManagerT>();
auto p1 = std::make_shared<PublisherT>(rclcpp::QoS(history_depth).best_effort());
auto s1 = std::make_shared<SubscriptionIntraProcessT>(rclcpp::QoS(history_depth).best_effort());
auto s2 = std::make_shared<SubscriptionIntraProcessT>(rclcpp::QoS(history_depth).best_effort());
auto p1_id = ipm->add_publisher(p1);
p1->set_intra_process_manager(p1_id, ipm);
auto c1 = ipm->lowest_available_capacity(p1_id);
ASSERT_LE(0u, c1);
auto s1_id = ipm->add_subscription(s1);
auto s2_id = ipm->add_subscription(s2);
bool unique_ids = s1_id != s2_id && p1_id != s1_id;
ASSERT_TRUE(unique_ids);
size_t p1_subs = ipm->get_subscription_count(p1_id);
size_t non_existing_pub_subs = ipm->get_subscription_count(42);
ASSERT_EQ(2u, p1_subs);
ASSERT_EQ(0u, non_existing_pub_subs);
c1 = ipm->lowest_available_capacity(p1_id);
auto non_existing_pub_c = ipm->lowest_available_capacity(42);
ASSERT_EQ(history_depth, c1);
ASSERT_EQ(0u, non_existing_pub_c);
auto unique_msg = std::make_unique<MessageT>();
p1->publish(std::move(unique_msg));
c1 = ipm->lowest_available_capacity(p1_id);
ASSERT_EQ(history_depth - 1u, c1);
unique_msg = std::make_unique<MessageT>();
p1->publish(std::move(unique_msg));
c1 = ipm->lowest_available_capacity(p1_id);
ASSERT_EQ(history_depth - 2u, c1);
s1->pop();
c1 = ipm->lowest_available_capacity(p1_id);
ASSERT_EQ(history_depth - 2u, c1);
s2->pop();
c1 = ipm->lowest_available_capacity(p1_id);
ASSERT_EQ(history_depth - 1u, c1);
ipm->get_subscription_intra_process(s1_id).reset();
c1 = ipm->lowest_available_capacity(p1_id);
ASSERT_EQ(history_depth - 1u, c1);
}

View File

@@ -629,6 +629,41 @@ TEST_P(TestPublisherWaitForAllAcked, check_wait_for_all_acked_with_QosPolicy) {
EXPECT_TRUE(pub->wait_for_all_acked(std::chrono::milliseconds(6000)));
}
TEST_F(TestPublisher, lowest_available_ipm_capacity) {
constexpr auto history_depth = 10u;
initialize(rclcpp::NodeOptions().use_intra_process_comms(true));
rclcpp::PublisherOptionsWithAllocator<std::allocator<void>> options_ipm_disabled;
options_ipm_disabled.use_intra_process_comm = rclcpp::IntraProcessSetting::Disable;
rclcpp::PublisherOptionsWithAllocator<std::allocator<void>> options_ipm_enabled;
options_ipm_enabled.use_intra_process_comm = rclcpp::IntraProcessSetting::Enable;
auto do_nothing = [](std::shared_ptr<const test_msgs::msg::Strings>) {};
auto pub_ipm_disabled = node->create_publisher<test_msgs::msg::Strings>(
"topic", history_depth,
options_ipm_disabled);
auto pub_ipm_enabled = node->create_publisher<test_msgs::msg::Strings>(
"topic", history_depth,
options_ipm_enabled);
auto sub = node->create_subscription<test_msgs::msg::Strings>(
"topic",
history_depth,
do_nothing);
ASSERT_EQ(1, pub_ipm_enabled->get_intra_process_subscription_count());
ASSERT_EQ(0, pub_ipm_disabled->lowest_available_ipm_capacity());
ASSERT_EQ(history_depth, pub_ipm_enabled->lowest_available_ipm_capacity());
auto msg = std::make_shared<test_msgs::msg::Strings>();
ASSERT_NO_THROW(pub_ipm_disabled->publish(*msg));
ASSERT_NO_THROW(pub_ipm_enabled->publish(*msg));
ASSERT_EQ(0, pub_ipm_disabled->lowest_available_ipm_capacity());
ASSERT_EQ(history_depth - 1u, pub_ipm_enabled->lowest_available_ipm_capacity());
}
INSTANTIATE_TEST_SUITE_P(
TestWaitForAllAckedWithParm,
TestPublisherWaitForAllAcked,

View File

@@ -152,54 +152,33 @@ 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_P(
TestPublisherFixture,
TEST_F(
TestPublisher,
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);
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 sub = node->create_subscription<rclcpp::msg::String>(topic_name, 1, callback);
auto wait_for_message_to_be_received = [&is_received, &node]() {
rclcpp::executors::SingleThreadedExecutor executor;
@@ -260,14 +239,6 @@ TEST_P(
}
}
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

@@ -314,11 +314,6 @@ TEST_F(TestQosEvent, add_to_wait_set) {
TEST_F(TestQosEvent, test_on_new_event_callback)
{
// rmw_connextdds doesn't support rmw_event_set_callback() interface
if (std::string(rmw_get_implementation_identifier()).find("rmw_connextdds") == 0) {
GTEST_SKIP();
}
auto offered_deadline = rclcpp::Duration(std::chrono::milliseconds(1));
auto requested_deadline = rclcpp::Duration(std::chrono::milliseconds(2));
@@ -360,11 +355,6 @@ TEST_F(TestQosEvent, test_on_new_event_callback)
TEST_F(TestQosEvent, test_invalid_on_new_event_callback)
{
// rmw_connextdds doesn't support rmw_event_set_callback() interface
if (std::string(rmw_get_implementation_identifier()).find("rmw_connextdds") == 0) {
GTEST_SKIP();
}
auto pub = node->create_publisher<test_msgs::msg::Empty>(topic_name, 10);
auto sub = node->create_subscription<test_msgs::msg::Empty>(topic_name, 10, message_callback);
auto dummy_cb = [](size_t count_events) {(void)count_events;};
@@ -439,11 +429,6 @@ TEST_F(TestQosEvent, test_invalid_on_new_event_callback)
TEST_F(TestQosEvent, test_pub_matched_event_by_set_event_callback)
{
// rmw_connextdds doesn't support rmw_event_set_callback() interface
if (std::string(rmw_get_implementation_identifier()).find("rmw_connextdds") == 0) {
GTEST_SKIP();
}
std::atomic_size_t matched_count = 0;
rclcpp::PublisherOptions pub_options;
@@ -451,8 +436,10 @@ TEST_F(TestQosEvent, test_pub_matched_event_by_set_event_callback)
auto pub = node->create_publisher<test_msgs::msg::Empty>(
topic_name, 10, pub_options);
auto matched_event_callback = [&matched_count](size_t count) {
std::promise<void> prom;
auto matched_event_callback = [&matched_count, &prom](size_t count) {
matched_count += count;
prom.set_value();
};
pub->set_on_new_qos_event_callback(matched_event_callback, RCL_PUBLISHER_MATCHED);
@@ -460,34 +447,32 @@ TEST_F(TestQosEvent, test_pub_matched_event_by_set_event_callback)
rclcpp::executors::SingleThreadedExecutor ex;
ex.add_node(node->get_node_base_interface());
const auto timeout = std::chrono::milliseconds(200);
const auto timeout = std::chrono::seconds(10);
{
auto sub1 = node->create_subscription<test_msgs::msg::Empty>(topic_name, 10, message_callback);
ex.spin_some(timeout);
ex.spin_until_future_complete(prom.get_future(), timeout);
prom = {};
EXPECT_EQ(matched_count, static_cast<size_t>(1));
{
auto sub2 = node->create_subscription<test_msgs::msg::Empty>(
topic_name, 10, message_callback);
ex.spin_some(timeout);
ex.spin_until_future_complete(prom.get_future(), timeout);
prom = {};
EXPECT_EQ(matched_count, static_cast<size_t>(2));
}
ex.spin_some(timeout);
ex.spin_until_future_complete(prom.get_future(), timeout);
prom = {};
EXPECT_EQ(matched_count, static_cast<size_t>(3));
}
ex.spin_some(timeout);
ex.spin_until_future_complete(prom.get_future(), timeout);
EXPECT_EQ(matched_count, static_cast<size_t>(4));
}
TEST_F(TestQosEvent, test_sub_matched_event_by_set_event_callback)
{
// rmw_connextdds doesn't support rmw_event_set_callback() interface
if (std::string(rmw_get_implementation_identifier()).find("rmw_connextdds") == 0) {
GTEST_SKIP();
}
std::atomic_size_t matched_count = 0;
rclcpp::SubscriptionOptions sub_options;
@@ -495,8 +480,10 @@ TEST_F(TestQosEvent, test_sub_matched_event_by_set_event_callback)
auto sub = node->create_subscription<test_msgs::msg::Empty>(
topic_name, 10, message_callback, sub_options);
auto matched_event_callback = [&matched_count](size_t count) {
std::promise<void> prom;
auto matched_event_callback = [&matched_count, &prom](size_t count) {
matched_count += count;
prom.set_value();
};
sub->set_on_new_qos_event_callback(matched_event_callback, RCL_SUBSCRIPTION_MATCHED);
@@ -504,39 +491,44 @@ TEST_F(TestQosEvent, test_sub_matched_event_by_set_event_callback)
rclcpp::executors::SingleThreadedExecutor ex;
ex.add_node(node->get_node_base_interface());
const auto timeout = std::chrono::milliseconds(200);
const auto timeout = std::chrono::seconds(10000);
{
auto pub1 = node->create_publisher<test_msgs::msg::Empty>(topic_name, 10);
ex.spin_some(timeout);
ex.spin_until_future_complete(prom.get_future(), timeout);
prom = {};
EXPECT_EQ(matched_count, static_cast<size_t>(1));
{
auto pub2 = node->create_publisher<test_msgs::msg::Empty>(topic_name, 10);
ex.spin_some(timeout);
ex.spin_until_future_complete(prom.get_future(), timeout);
prom = {};
EXPECT_EQ(matched_count, static_cast<size_t>(2));
}
ex.spin_some(timeout);
ex.spin_until_future_complete(prom.get_future(), timeout);
prom = {};
EXPECT_EQ(matched_count, static_cast<size_t>(3));
}
ex.spin_some(timeout);
ex.spin_until_future_complete(prom.get_future(), timeout);
EXPECT_EQ(matched_count, static_cast<size_t>(4));
}
TEST_F(TestQosEvent, test_pub_matched_event_by_option_event_callback)
{
rmw_matched_status_t matched_expected_result;
std::promise<void> prom;
rclcpp::PublisherOptions pub_options;
pub_options.event_callbacks.matched_callback =
[&matched_expected_result](rmw_matched_status_t & s) {
[&matched_expected_result, &prom](rmw_matched_status_t & s) {
EXPECT_EQ(s.total_count, matched_expected_result.total_count);
EXPECT_EQ(s.total_count_change, matched_expected_result.total_count_change);
EXPECT_EQ(s.current_count, matched_expected_result.current_count);
EXPECT_EQ(s.current_count_change, matched_expected_result.current_count_change);
prom.set_value();
};
auto pub = node->create_publisher<test_msgs::msg::Empty>(
@@ -551,11 +543,12 @@ TEST_F(TestQosEvent, test_pub_matched_event_by_option_event_callback)
matched_expected_result.current_count = 1;
matched_expected_result.current_count_change = 1;
const auto timeout = std::chrono::milliseconds(200);
const auto timeout = std::chrono::seconds(10);
{
auto sub = node->create_subscription<test_msgs::msg::Empty>(topic_name, 10, message_callback);
ex.spin_some(timeout);
ex.spin_until_future_complete(prom.get_future(), timeout);
prom = {};
// destroy a connected subscription
matched_expected_result.total_count = 1;
@@ -563,20 +556,22 @@ TEST_F(TestQosEvent, test_pub_matched_event_by_option_event_callback)
matched_expected_result.current_count = 0;
matched_expected_result.current_count_change = -1;
}
ex.spin_some(timeout);
ex.spin_until_future_complete(prom.get_future(), timeout);
}
TEST_F(TestQosEvent, test_sub_matched_event_by_option_event_callback)
{
rmw_matched_status_t matched_expected_result;
std::promise<void> prom;
rclcpp::SubscriptionOptions sub_options;
sub_options.event_callbacks.matched_callback =
[&matched_expected_result](rmw_matched_status_t & s) {
[&matched_expected_result, &prom](rmw_matched_status_t & s) {
EXPECT_EQ(s.total_count, matched_expected_result.total_count);
EXPECT_EQ(s.total_count_change, matched_expected_result.total_count_change);
EXPECT_EQ(s.current_count, matched_expected_result.current_count);
EXPECT_EQ(s.current_count_change, matched_expected_result.current_count_change);
prom.set_value();
};
auto sub = node->create_subscription<test_msgs::msg::Empty>(
topic_name, 10, message_callback, sub_options);
@@ -590,10 +585,11 @@ TEST_F(TestQosEvent, test_sub_matched_event_by_option_event_callback)
matched_expected_result.current_count = 1;
matched_expected_result.current_count_change = 1;
const auto timeout = std::chrono::milliseconds(200);
const auto timeout = std::chrono::seconds(10);
{
auto pub1 = node->create_publisher<test_msgs::msg::Empty>(topic_name, 10);
ex.spin_some(timeout);
ex.spin_until_future_complete(prom.get_future(), timeout);
prom = {};
// destroy a connected publisher
matched_expected_result.total_count = 1;
@@ -601,5 +597,5 @@ TEST_F(TestQosEvent, test_sub_matched_event_by_option_event_callback)
matched_expected_result.current_count = 0;
matched_expected_result.current_count_change = -1;
}
ex.spin_some(timeout);
ex.spin_until_future_complete(prom.get_future(), timeout);
}

View File

@@ -18,24 +18,10 @@
#include "rclcpp/rate.hpp"
class TestRate : public ::testing::Test
{
public:
void SetUp()
{
rclcpp::init(0, nullptr);
}
void TearDown()
{
rclcpp::shutdown();
}
};
/*
Basic tests for the Rate and WallRate classes.
*/
TEST_F(TestRate, rate_basics) {
TEST(TestRate, rate_basics) {
auto period = std::chrono::milliseconds(1000);
auto offset = std::chrono::milliseconds(500);
auto epsilon = std::chrono::milliseconds(100);
@@ -75,7 +61,7 @@ TEST_F(TestRate, rate_basics) {
ASSERT_TRUE(epsilon > delta);
}
TEST_F(TestRate, wall_rate_basics) {
TEST(TestRate, wall_rate_basics) {
auto period = std::chrono::milliseconds(100);
auto offset = std::chrono::milliseconds(50);
auto epsilon = std::chrono::milliseconds(1);
@@ -115,7 +101,7 @@ TEST_F(TestRate, wall_rate_basics) {
EXPECT_GT(epsilon, delta);
}
TEST_F(TestRate, from_double) {
TEST(TestRate, from_double) {
{
rclcpp::WallRate rate(1.0);
EXPECT_EQ(std::chrono::seconds(1), rate.period());

View File

@@ -70,7 +70,7 @@ TEST_F(TestRosoutSubscription, test_rosoutsubscription_getchild) {
// before calling get_child of Logger
{
RCLCPP_INFO(
rclcpp::get_logger(logger_name), this->rosout_msg_data.c_str());
rclcpp::get_logger(logger_name), "%s", this->rosout_msg_data.c_str());
auto future = received_msg_promise.get_future();
auto return_code = rclcpp::spin_until_future_complete(this->node, future, 3s);
ASSERT_EQ(rclcpp::FutureReturnCode::TIMEOUT, return_code);
@@ -83,7 +83,7 @@ TEST_F(TestRosoutSubscription, test_rosoutsubscription_getchild) {
// after calling get_child of Logger
// 1. use child_logger directly
{
RCLCPP_INFO(child_logger, this->rosout_msg_data.c_str());
RCLCPP_INFO(child_logger, "%s", this->rosout_msg_data.c_str());
auto future = received_msg_promise.get_future();
auto return_code = rclcpp::spin_until_future_complete(this->node, future, 3s);
ASSERT_EQ(rclcpp::FutureReturnCode::SUCCESS, return_code);
@@ -93,7 +93,7 @@ TEST_F(TestRosoutSubscription, test_rosoutsubscription_getchild) {
// 2. use rclcpp::get_logger
{
RCLCPP_INFO(rclcpp::get_logger(logger_name), this->rosout_msg_data.c_str());
RCLCPP_INFO(rclcpp::get_logger(logger_name), "%s", this->rosout_msg_data.c_str());
auto future = received_msg_promise.get_future();
auto return_code = rclcpp::spin_until_future_complete(this->node, future, 3s);
ASSERT_EQ(rclcpp::FutureReturnCode::SUCCESS, return_code);
@@ -104,7 +104,7 @@ TEST_F(TestRosoutSubscription, test_rosoutsubscription_getchild) {
// `child_logger` is end of life, there is no sublogger
{
RCLCPP_INFO(rclcpp::get_logger(logger_name), this->rosout_msg_data.c_str());
RCLCPP_INFO(rclcpp::get_logger(logger_name), "%s", this->rosout_msg_data.c_str());
auto future = received_msg_promise.get_future();
auto return_code = rclcpp::spin_until_future_complete(this->node, future, 3s);
ASSERT_EQ(rclcpp::FutureReturnCode::TIMEOUT, return_code);
@@ -119,7 +119,7 @@ TEST_F(TestRosoutSubscription, test_rosoutsubscription_parent_log) {
rclcpp::Logger logger = this->node->get_logger();
ASSERT_EQ(logger.get_name(), logger_name);
RCLCPP_INFO(logger, this->rosout_msg_data.c_str());
RCLCPP_INFO(logger, "%s", this->rosout_msg_data.c_str());
auto future = received_msg_promise.get_future();
auto return_code = rclcpp::spin_until_future_complete(this->node, future, 3s);
ASSERT_EQ(rclcpp::FutureReturnCode::SUCCESS, return_code);
@@ -133,14 +133,14 @@ TEST_F(TestRosoutSubscription, test_rosoutsubscription_child_log) {
this->rosout_msg_name = logger_name;
rclcpp::Logger logger = this->node->get_logger();
RCLCPP_INFO(logger, this->rosout_msg_data.c_str());
RCLCPP_INFO(logger, "%s", this->rosout_msg_data.c_str());
auto future = received_msg_promise.get_future();
auto return_code = rclcpp::spin_until_future_complete(this->node, future, 3s);
ASSERT_EQ(rclcpp::FutureReturnCode::TIMEOUT, return_code);
received_msg_promise = {};
logger = this->node->get_logger().get_child("child1");
RCLCPP_INFO(logger, this->rosout_msg_data.c_str());
RCLCPP_INFO(logger, "%s", this->rosout_msg_data.c_str());
future = received_msg_promise.get_future();
return_code = rclcpp::spin_until_future_complete(this->node, future, 3s);
ASSERT_EQ(rclcpp::FutureReturnCode::SUCCESS, return_code);
@@ -148,14 +148,14 @@ TEST_F(TestRosoutSubscription, test_rosoutsubscription_child_log) {
received_msg_promise = {};
logger = this->node->get_logger().get_child("child2");
RCLCPP_INFO(logger, this->rosout_msg_data.c_str());
RCLCPP_INFO(logger, "%s", this->rosout_msg_data.c_str());
future = received_msg_promise.get_future();
return_code = rclcpp::spin_until_future_complete(this->node, future, 3s);
ASSERT_EQ(rclcpp::FutureReturnCode::TIMEOUT, return_code);
received_msg_promise = {};
this->rosout_msg_name = "ns.test_rosout_subscription.child2";
RCLCPP_INFO(logger, this->rosout_msg_data.c_str());
RCLCPP_INFO(logger, "%s", this->rosout_msg_data.c_str());
future = received_msg_promise.get_future();
return_code = rclcpp::spin_until_future_complete(this->node, future, 3s);
ASSERT_EQ(rclcpp::FutureReturnCode::SUCCESS, return_code);
@@ -171,7 +171,7 @@ TEST_F(TestRosoutSubscription, test_rosoutsubscription_getchild_hierarchy) {
rclcpp::Logger grandchild_logger =
this->node->get_logger().get_child("child").get_child("grandchild");
ASSERT_EQ(grandchild_logger.get_name(), logger_name);
RCLCPP_INFO(grandchild_logger, this->rosout_msg_data.c_str());
RCLCPP_INFO(grandchild_logger, "%s", this->rosout_msg_data.c_str());
auto future = received_msg_promise.get_future();
auto return_code = rclcpp::spin_until_future_complete(this->node, future, 3s);
ASSERT_EQ(rclcpp::FutureReturnCode::SUCCESS, return_code);

View File

@@ -17,6 +17,8 @@
#include <chrono>
#include <functional>
#include <memory>
#include <ostream>
#include <stdexcept>
#include <string>
#include <thread>
#include <vector>
@@ -34,114 +36,28 @@ using namespace std::chrono_literals;
class TestSubscription : public ::testing::Test
{
public:
void OnMessage(test_msgs::msg::Empty::ConstSharedPtr msg)
void on_message(test_msgs::msg::Empty::ConstSharedPtr msg)
{
(void)msg;
}
protected:
static void SetUpTestCase()
{
rclcpp::init(0, nullptr);
}
protected:
static void TearDownTestCase()
{
rclcpp::shutdown();
}
void initialize(const rclcpp::NodeOptions & node_options = rclcpp::NodeOptions())
{
node = std::make_shared<rclcpp::Node>("test_subscription", "/ns", node_options);
node_ = std::make_shared<rclcpp::Node>("test_subscription", "/ns", node_options);
}
void TearDown()
{
node.reset();
}
rclcpp::Node::SharedPtr node;
};
struct TestParameters
{
TestParameters(rclcpp::QoS qos, std::string description)
: qos(qos), description(description) {}
rclcpp::QoS qos;
std::string description;
};
std::ostream & operator<<(std::ostream & out, const TestParameters & params)
{
out << params.description;
return out;
}
class TestSubscriptionInvalidIntraprocessQos
: public TestSubscription,
public ::testing::WithParamInterface<TestParameters>
{};
class TestSubscriptionSub : public ::testing::Test
{
public:
void OnMessage(test_msgs::msg::Empty::ConstSharedPtr msg)
{
(void)msg;
}
protected:
static void SetUpTestCase()
{
}
void SetUp()
{
node = std::make_shared<rclcpp::Node>("test_subscription", "/ns");
subnode = node->create_sub_node("sub_ns");
}
void TearDown()
{
node.reset();
}
rclcpp::Node::SharedPtr node;
rclcpp::Node::SharedPtr subnode;
};
class SubscriptionClassNodeInheritance : public rclcpp::Node
{
public:
SubscriptionClassNodeInheritance()
: Node("subscription_class_node_inheritance")
{
}
void OnMessage(test_msgs::msg::Empty::ConstSharedPtr msg)
{
(void)msg;
}
void CreateSubscription()
{
auto callback = std::bind(
&SubscriptionClassNodeInheritance::OnMessage, this, std::placeholders::_1);
using test_msgs::msg::Empty;
auto sub = this->create_subscription<Empty>("topic", 10, callback);
}
};
class SubscriptionClass
{
public:
void OnMessage(test_msgs::msg::Empty::ConstSharedPtr msg)
{
(void)msg;
}
void CreateSubscription()
{
auto node = std::make_shared<rclcpp::Node>("test_subscription_member_callback", "/ns");
auto callback = std::bind(&SubscriptionClass::OnMessage, this, std::placeholders::_1);
using test_msgs::msg::Empty;
auto sub = node->create_subscription<Empty>("topic", 10, callback);
}
rclcpp::Node::SharedPtr node_;
};
/*
@@ -155,7 +71,7 @@ TEST_F(TestSubscription, construction_and_destruction) {
};
{
constexpr size_t depth = 10u;
auto sub = node->create_subscription<Empty>("topic", depth, callback);
auto sub = node_->create_subscription<Empty>("topic", depth, callback);
EXPECT_NE(nullptr, sub->get_subscription_handle());
// Converting to base class was necessary for the compiler to choose the const version of
@@ -172,40 +88,7 @@ TEST_F(TestSubscription, construction_and_destruction) {
{
ASSERT_THROW(
{
auto sub = node->create_subscription<Empty>("invalid_topic?", 10, callback);
}, rclcpp::exceptions::InvalidTopicNameError);
}
}
/*
Testing subscription construction and destruction for subnodes.
*/
TEST_F(TestSubscriptionSub, construction_and_destruction) {
using test_msgs::msg::Empty;
auto callback = [](Empty::ConstSharedPtr msg) {
(void)msg;
};
{
auto sub = subnode->create_subscription<Empty>("topic", 1, callback);
EXPECT_STREQ(sub->get_topic_name(), "/ns/sub_ns/topic");
}
{
auto sub = subnode->create_subscription<Empty>("/topic", 1, callback);
EXPECT_STREQ(sub->get_topic_name(), "/topic");
}
{
auto sub = subnode->create_subscription<Empty>("~/topic", 1, callback);
std::string expected_topic_name =
std::string(node->get_namespace()) + "/" + node->get_name() + "/topic";
EXPECT_STREQ(sub->get_topic_name(), expected_topic_name.c_str());
}
{
ASSERT_THROW(
{
auto sub = node->create_subscription<Empty>("invalid_topic?", 1, callback);
auto sub = node_->create_subscription<Empty>("invalid_topic?", 10, callback);
}, rclcpp::exceptions::InvalidTopicNameError);
}
}
@@ -218,31 +101,31 @@ TEST_F(TestSubscription, various_creation_signatures) {
using test_msgs::msg::Empty;
auto cb = [](test_msgs::msg::Empty::ConstSharedPtr) {};
{
auto sub = node->create_subscription<Empty>("topic", 1, cb);
auto sub = node_->create_subscription<Empty>("topic", 1, cb);
(void)sub;
}
{
auto sub = node->create_subscription<Empty>("topic", rclcpp::QoS(1), cb);
auto sub = node_->create_subscription<Empty>("topic", rclcpp::QoS(1), cb);
(void)sub;
}
{
auto sub =
node->create_subscription<Empty>("topic", rclcpp::QoS(rclcpp::KeepLast(1)), cb);
node_->create_subscription<Empty>("topic", rclcpp::QoS(rclcpp::KeepLast(1)), cb);
(void)sub;
}
{
auto sub =
node->create_subscription<Empty>("topic", rclcpp::QoS(rclcpp::KeepAll()), cb);
node_->create_subscription<Empty>("topic", rclcpp::QoS(rclcpp::KeepAll()), cb);
(void)sub;
}
{
auto sub = node->create_subscription<Empty>(
auto sub = node_->create_subscription<Empty>(
"topic", 42, cb, rclcpp::SubscriptionOptions());
(void)sub;
}
{
auto sub = rclcpp::create_subscription<Empty>(
node, "topic", 42, cb, rclcpp::SubscriptionOptions());
node_, "topic", 42, cb, rclcpp::SubscriptionOptions());
(void)sub;
}
{
@@ -250,40 +133,78 @@ TEST_F(TestSubscription, various_creation_signatures) {
options.allocator = std::make_shared<std::allocator<void>>();
EXPECT_NE(nullptr, options.get_allocator());
auto sub = rclcpp::create_subscription<Empty>(
node, "topic", 42, cb, options);
node_, "topic", 42, cb, options);
(void)sub;
}
{
rclcpp::SubscriptionOptionsBase options_base;
rclcpp::SubscriptionOptionsWithAllocator<std::allocator<void>> options(options_base);
auto sub = rclcpp::create_subscription<Empty>(
node, "topic", 42, cb, options);
node_, "topic", 42, cb, options);
(void)sub;
}
}
class SubscriptionClass final
{
public:
void custom_create_subscription()
{
auto node = std::make_shared<rclcpp::Node>("test_subscription_member_callback", "/ns");
auto callback = std::bind(&SubscriptionClass::on_message, this, std::placeholders::_1);
auto sub = node->create_subscription<test_msgs::msg::Empty>("topic", 10, callback);
}
private:
void on_message(test_msgs::msg::Empty::ConstSharedPtr msg)
{
(void)msg;
}
};
class SubscriptionClassNodeInheritance final : public rclcpp::Node
{
public:
SubscriptionClassNodeInheritance()
: Node("subscription_class_node_inheritance")
{
}
void custom_create_subscription()
{
auto callback = std::bind(
&SubscriptionClassNodeInheritance::on_message, this, std::placeholders::_1);
auto sub = this->create_subscription<test_msgs::msg::Empty>("topic", 10, callback);
}
private:
void on_message(test_msgs::msg::Empty::ConstSharedPtr msg)
{
(void)msg;
}
};
/*
Testing subscriptions using std::bind.
*/
TEST_F(TestSubscription, callback_bind) {
initialize();
using test_msgs::msg::Empty;
{
// Member callback for plain class
SubscriptionClass subscription_object;
subscription_object.CreateSubscription();
subscription_object.custom_create_subscription();
}
{
// Member callback for class inheriting from rclcpp::Node
SubscriptionClassNodeInheritance subscription_object;
subscription_object.CreateSubscription();
subscription_object.custom_create_subscription();
}
{
// Member callback for class inheriting from testing::Test
// Regression test for https://github.com/ros2/rclcpp/issues/479 where the TEST_F GTest macro
// was interfering with rclcpp's `function_traits`.
auto callback = std::bind(&TestSubscription::OnMessage, this, std::placeholders::_1);
auto sub = node->create_subscription<Empty>("topic", 1, callback);
auto callback = std::bind(&TestSubscription::on_message, this, std::placeholders::_1);
auto sub = node_->create_subscription<test_msgs::msg::Empty>("topic", 1, callback);
}
}
@@ -292,10 +213,9 @@ TEST_F(TestSubscription, callback_bind) {
*/
TEST_F(TestSubscription, take) {
initialize();
using test_msgs::msg::Empty;
auto do_nothing = [](std::shared_ptr<const test_msgs::msg::Empty>) {FAIL();};
{
auto sub = node->create_subscription<test_msgs::msg::Empty>("~/test_take", 1, do_nothing);
auto sub = node_->create_subscription<test_msgs::msg::Empty>("~/test_take", 1, do_nothing);
test_msgs::msg::Empty msg;
rclcpp::MessageInfo msg_info;
EXPECT_FALSE(sub->take(msg, msg_info));
@@ -303,23 +223,23 @@ TEST_F(TestSubscription, take) {
{
rclcpp::SubscriptionOptions so;
so.use_intra_process_comm = rclcpp::IntraProcessSetting::Disable;
auto sub = node->create_subscription<test_msgs::msg::Empty>("~/test_take", 1, do_nothing, so);
auto sub = node_->create_subscription<test_msgs::msg::Empty>("~/test_take", 1, do_nothing, so);
rclcpp::PublisherOptions po;
po.use_intra_process_comm = rclcpp::IntraProcessSetting::Disable;
auto pub = node->create_publisher<test_msgs::msg::Empty>("~/test_take", 1, po);
auto pub = node_->create_publisher<test_msgs::msg::Empty>("~/test_take", 1, po);
{
test_msgs::msg::Empty msg;
pub->publish(msg);
}
test_msgs::msg::Empty msg;
rclcpp::MessageInfo msg_info;
bool message_recieved = false;
bool message_received = false;
auto start = std::chrono::steady_clock::now();
do {
message_recieved = sub->take(msg, msg_info);
message_received = sub->take(msg, msg_info);
std::this_thread::sleep_for(100ms);
} while (!message_recieved && std::chrono::steady_clock::now() - start < 10s);
EXPECT_TRUE(message_recieved);
} while (!message_received && std::chrono::steady_clock::now() - start < 10s);
EXPECT_TRUE(message_received);
}
// TODO(wjwwood): figure out a good way to test the intra-process exclusion behavior.
}
@@ -329,10 +249,9 @@ TEST_F(TestSubscription, take) {
*/
TEST_F(TestSubscription, take_serialized) {
initialize();
using test_msgs::msg::Empty;
auto do_nothing = [](std::shared_ptr<const rclcpp::SerializedMessage>) {FAIL();};
{
auto sub = node->create_subscription<test_msgs::msg::Empty>("~/test_take", 1, do_nothing);
auto sub = node_->create_subscription<test_msgs::msg::Empty>("~/test_take", 1, do_nothing);
std::shared_ptr<rclcpp::SerializedMessage> msg = sub->create_serialized_message();
rclcpp::MessageInfo msg_info;
EXPECT_FALSE(sub->take_serialized(*msg, msg_info));
@@ -340,23 +259,23 @@ TEST_F(TestSubscription, take_serialized) {
{
rclcpp::SubscriptionOptions so;
so.use_intra_process_comm = rclcpp::IntraProcessSetting::Disable;
auto sub = node->create_subscription<test_msgs::msg::Empty>("~/test_take", 1, do_nothing, so);
auto sub = node_->create_subscription<test_msgs::msg::Empty>("~/test_take", 1, do_nothing, so);
rclcpp::PublisherOptions po;
po.use_intra_process_comm = rclcpp::IntraProcessSetting::Disable;
auto pub = node->create_publisher<test_msgs::msg::Empty>("~/test_take", 1, po);
auto pub = node_->create_publisher<test_msgs::msg::Empty>("~/test_take", 1, po);
{
test_msgs::msg::Empty msg;
pub->publish(msg);
}
std::shared_ptr<rclcpp::SerializedMessage> msg = sub->create_serialized_message();
rclcpp::MessageInfo msg_info;
bool message_recieved = false;
bool message_received = false;
auto start = std::chrono::steady_clock::now();
do {
message_recieved = sub->take_serialized(*msg, msg_info);
message_received = sub->take_serialized(*msg, msg_info);
std::this_thread::sleep_for(100ms);
} while (!message_recieved && std::chrono::steady_clock::now() - start < 10s);
EXPECT_TRUE(message_recieved);
} while (!message_received && std::chrono::steady_clock::now() - start < 10s);
EXPECT_TRUE(message_received);
}
}
@@ -368,7 +287,7 @@ TEST_F(TestSubscription, rcl_subscription_init_error) {
// reset() is not needed for triggering exception, just to avoid an unused return value warning
EXPECT_THROW(
node->create_subscription<test_msgs::msg::Empty>("topic", 10, callback).reset(),
node_->create_subscription<test_msgs::msg::Empty>("topic", 10, callback).reset(),
rclcpp::exceptions::RCLError);
}
@@ -380,7 +299,7 @@ TEST_F(TestSubscription, rcl_subscription_fini_error) {
// Cleanup just fails, no exception expected
EXPECT_NO_THROW(
node->create_subscription<test_msgs::msg::Empty>("topic", 10, callback).reset());
node_->create_subscription<test_msgs::msg::Empty>("topic", 10, callback).reset());
}
TEST_F(TestSubscription, rcl_subscription_get_actual_qos_error) {
@@ -389,7 +308,7 @@ TEST_F(TestSubscription, rcl_subscription_get_actual_qos_error) {
auto mock = mocking_utils::patch_and_return(
"lib:rclcpp", rcl_subscription_get_actual_qos, nullptr);
auto sub = node->create_subscription<test_msgs::msg::Empty>("topic", 10, callback);
auto sub = node_->create_subscription<test_msgs::msg::Empty>("topic", 10, callback);
RCLCPP_EXPECT_THROW_EQ(
sub->get_actual_qos(), std::runtime_error("failed to get qos settings: error not set"));
}
@@ -400,7 +319,7 @@ TEST_F(TestSubscription, rcl_take_type_erased_error) {
auto mock = mocking_utils::patch_and_return(
"lib:rclcpp", rcl_take, RCL_RET_ERROR);
auto sub = node->create_subscription<test_msgs::msg::Empty>("topic", 10, callback);
auto sub = node_->create_subscription<test_msgs::msg::Empty>("topic", 10, callback);
test_msgs::msg::Empty msg;
rclcpp::MessageInfo message_info;
@@ -413,7 +332,7 @@ TEST_F(TestSubscription, rcl_take_serialized_message_error) {
auto mock = mocking_utils::patch_and_return(
"lib:rclcpp", rcl_take_serialized_message, RCL_RET_ERROR);
auto sub = node->create_subscription<test_msgs::msg::Empty>("topic", 10, callback);
auto sub = node_->create_subscription<test_msgs::msg::Empty>("topic", 10, callback);
rclcpp::SerializedMessage msg;
rclcpp::MessageInfo message_info;
@@ -426,14 +345,14 @@ TEST_F(TestSubscription, rcl_subscription_get_publisher_count_error) {
auto mock = mocking_utils::patch_and_return(
"lib:rclcpp", rcl_subscription_get_publisher_count, RCL_RET_ERROR);
auto sub = node->create_subscription<test_msgs::msg::Empty>("topic", 10, callback);
auto sub = node_->create_subscription<test_msgs::msg::Empty>("topic", 10, callback);
EXPECT_THROW(sub->get_publisher_count(), rclcpp::exceptions::RCLError);
}
TEST_F(TestSubscription, handle_loaned_message) {
initialize();
auto callback = [](std::shared_ptr<const test_msgs::msg::Empty>) {};
auto sub = node->create_subscription<test_msgs::msg::Empty>("topic", 10, callback);
auto sub = node_->create_subscription<test_msgs::msg::Empty>("topic", 10, callback);
test_msgs::msg::Empty msg;
rclcpp::MessageInfo message_info;
@@ -448,13 +367,13 @@ TEST_F(TestSubscription, on_new_message_callback) {
using test_msgs::msg::Empty;
auto do_nothing = [](std::shared_ptr<const test_msgs::msg::Empty>) {FAIL();};
auto sub = node->create_subscription<test_msgs::msg::Empty>("~/test_take", 10, do_nothing);
auto sub = node_->create_subscription<test_msgs::msg::Empty>("~/test_take", 10, do_nothing);
std::atomic<size_t> c1 {0};
auto increase_c1_cb = [&c1](size_t count_msgs) {c1 += count_msgs;};
sub->set_on_new_message_callback(increase_c1_cb);
auto pub = node->create_publisher<test_msgs::msg::Empty>("~/test_take", 3);
auto pub = node_->create_publisher<test_msgs::msg::Empty>("~/test_take", 3);
{
test_msgs::msg::Empty msg;
pub->publish(msg);
@@ -518,13 +437,13 @@ TEST_F(TestSubscription, on_new_intra_process_message_callback) {
using test_msgs::msg::Empty;
auto do_nothing = [](std::shared_ptr<const test_msgs::msg::Empty>) {FAIL();};
auto sub = node->create_subscription<test_msgs::msg::Empty>("~/test_take", 10, do_nothing);
auto sub = node_->create_subscription<test_msgs::msg::Empty>("~/test_take", 10, do_nothing);
std::atomic<size_t> c1 {0};
auto increase_c1_cb = [&c1](size_t count_msgs) {c1 += count_msgs;};
sub->set_on_new_intra_process_message_callback(increase_c1_cb);
auto pub = node->create_publisher<test_msgs::msg::Empty>("~/test_take", 1);
auto pub = node_->create_publisher<test_msgs::msg::Empty>("~/test_take", 1);
{
test_msgs::msg::Empty msg;
pub->publish(msg);
@@ -580,80 +499,13 @@ TEST_F(TestSubscription, on_new_intra_process_message_callback) {
EXPECT_THROW(sub->set_on_new_intra_process_message_callback(invalid_cb), std::invalid_argument);
}
/*
Testing subscription with intraprocess enabled and invalid QoS
*/
TEST_P(TestSubscriptionInvalidIntraprocessQos, test_subscription_throws) {
initialize(rclcpp::NodeOptions().use_intra_process_comms(true));
rclcpp::QoS qos = GetParam().qos;
using test_msgs::msg::Empty;
{
auto callback = std::bind(
&TestSubscriptionInvalidIntraprocessQos::OnMessage,
this,
std::placeholders::_1);
ASSERT_THROW(
{auto subscription = node->create_subscription<Empty>(
"topic",
qos,
callback);},
std::invalid_argument);
}
}
/*
Testing subscription with invalid use_intra_process_comm
*/
TEST_P(TestSubscriptionInvalidIntraprocessQos, test_subscription_throws_intraprocess) {
rclcpp::SubscriptionOptionsWithAllocator<std::allocator<void>> options;
options.use_intra_process_comm = static_cast<rclcpp::IntraProcessSetting>(5);
initialize();
rclcpp::QoS qos = GetParam().qos;
auto callback = std::bind(
&TestSubscriptionInvalidIntraprocessQos::OnMessage,
this,
std::placeholders::_1);
RCLCPP_EXPECT_THROW_EQ(
{auto subscription = node->create_subscription<test_msgs::msg::Empty>(
"topic",
qos,
callback,
options);},
std::runtime_error("Unrecognized IntraProcessSetting value"));
}
static std::vector<TestParameters> invalid_qos_profiles()
{
std::vector<TestParameters> parameters;
parameters.reserve(3);
parameters.push_back(
TestParameters(
rclcpp::QoS(rclcpp::KeepLast(10)).transient_local(),
"transient_local_qos"));
parameters.push_back(
TestParameters(
rclcpp::QoS(rclcpp::KeepAll()),
"keep_all_qos"));
return parameters;
}
INSTANTIATE_TEST_SUITE_P(
TestSubscriptionThrows, TestSubscriptionInvalidIntraprocessQos,
::testing::ValuesIn(invalid_qos_profiles()),
::testing::PrintToStringParamName());
TEST_F(TestSubscription, get_network_flow_endpoints_errors) {
initialize();
const rclcpp::QoS subscription_qos(1);
auto subscription_callback = [](test_msgs::msg::Empty::ConstSharedPtr msg) {
(void)msg;
};
auto subscription = node->create_subscription<test_msgs::msg::Empty>(
auto subscription = node_->create_subscription<test_msgs::msg::Empty>(
"topic", subscription_qos, subscription_callback);
{
@@ -680,3 +532,143 @@ TEST_F(TestSubscription, get_network_flow_endpoints_errors) {
EXPECT_NO_THROW(subscription->get_network_flow_endpoints());
}
}
class TestSubscriptionSub : public ::testing::Test
{
protected:
static void SetUpTestCase()
{
rclcpp::init(0, nullptr);
}
static void TearDownTestCase()
{
rclcpp::shutdown();
}
void SetUp()
{
node_ = std::make_shared<rclcpp::Node>("test_subscription", "/ns");
subnode_ = node_->create_sub_node("sub_ns");
}
rclcpp::Node::SharedPtr node_;
rclcpp::Node::SharedPtr subnode_;
};
/*
Testing subscription construction and destruction for subnodes.
*/
TEST_F(TestSubscriptionSub, construction_and_destruction) {
auto callback = [](test_msgs::msg::Empty::ConstSharedPtr msg) {
(void)msg;
};
{
auto sub = subnode_->create_subscription<test_msgs::msg::Empty>("topic", 1, callback);
EXPECT_STREQ(sub->get_topic_name(), "/ns/sub_ns/topic");
}
{
auto sub = subnode_->create_subscription<test_msgs::msg::Empty>("/topic", 1, callback);
EXPECT_STREQ(sub->get_topic_name(), "/topic");
}
{
auto sub = subnode_->create_subscription<test_msgs::msg::Empty>("~/topic", 1, callback);
std::string expected_topic_name =
std::string(node_->get_namespace()) + "/" + node_->get_name() + "/topic";
EXPECT_STREQ(sub->get_topic_name(), expected_topic_name.c_str());
}
{
ASSERT_THROW(
{
auto sub = node_->create_subscription<test_msgs::msg::Empty>("invalid_topic?", 1, callback);
}, rclcpp::exceptions::InvalidTopicNameError);
}
}
struct TestParameters final
{
TestParameters(rclcpp::QoS qos, std::string description)
: qos(qos), description(description) {}
rclcpp::QoS qos;
std::string description;
};
std::ostream & operator<<(std::ostream & out, const TestParameters & params)
{
out << params.description;
return out;
}
class TestSubscriptionInvalidIntraprocessQos
: public TestSubscription,
public ::testing::WithParamInterface<TestParameters>
{};
static std::vector<TestParameters> invalid_qos_profiles()
{
std::vector<TestParameters> parameters;
parameters.reserve(3);
parameters.push_back(
TestParameters(
rclcpp::QoS(rclcpp::KeepLast(10)).transient_local(),
"transient_local_qos"));
parameters.push_back(
TestParameters(
rclcpp::QoS(rclcpp::KeepAll()),
"keep_all_qos"));
return parameters;
}
INSTANTIATE_TEST_SUITE_P(
TestSubscriptionThrows, TestSubscriptionInvalidIntraprocessQos,
::testing::ValuesIn(invalid_qos_profiles()),
::testing::PrintToStringParamName());
/*
Testing subscription with intraprocess enabled and invalid QoS
*/
TEST_P(TestSubscriptionInvalidIntraprocessQos, test_subscription_throws) {
initialize(rclcpp::NodeOptions().use_intra_process_comms(true));
rclcpp::QoS qos = GetParam().qos;
{
auto callback = std::bind(
&TestSubscriptionInvalidIntraprocessQos::on_message,
this,
std::placeholders::_1);
ASSERT_THROW(
{auto subscription = node_->create_subscription<test_msgs::msg::Empty>(
"topic",
qos,
callback);},
std::invalid_argument);
}
}
/*
Testing subscription with invalid use_intra_process_comm
*/
TEST_P(TestSubscriptionInvalidIntraprocessQos, test_subscription_throws_intraprocess) {
rclcpp::SubscriptionOptionsWithAllocator<std::allocator<void>> options;
options.use_intra_process_comm = static_cast<rclcpp::IntraProcessSetting>(5);
initialize();
rclcpp::QoS qos = GetParam().qos;
auto callback = std::bind(
&TestSubscriptionInvalidIntraprocessQos::on_message,
this,
std::placeholders::_1);
RCLCPP_EXPECT_THROW_EQ(
{auto subscription = node_->create_subscription<test_msgs::msg::Empty>(
"topic",
qos,
callback,
options);},
std::runtime_error("Unrecognized IntraProcessSetting value"));
}

View File

@@ -310,25 +310,3 @@ TEST_F(CLASSNAME(TestContentFilterSubscription, RMW_IMPLEMENTATION), content_fil
}
}
}
TEST_F(
CLASSNAME(
TestContentFilterSubscription,
RMW_IMPLEMENTATION), create_two_content_filters_with_same_topic_name_and_destroy) {
// Create another content filter
auto options = rclcpp::SubscriptionOptions();
std::string filter_expression = "int32_value > %0";
std::vector<std::string> expression_parameters = {"4"};
options.content_filter_options.filter_expression = filter_expression;
options.content_filter_options.expression_parameters = expression_parameters;
auto callback = [](std::shared_ptr<const test_msgs::msg::BasicTypes>) {};
auto sub_2 = node->create_subscription<test_msgs::msg::BasicTypes>(
"content_filter_topic", qos, callback, options);
EXPECT_NE(nullptr, sub_2);
sub_2.reset();
}

View File

@@ -305,7 +305,7 @@ TEST_F(TestTimeSource, clock) {
trigger_clock_changes(node, ros_clock, false);
// Even now that we've recieved a message, ROS time should still not be active since the
// Even now that we've received a message, ROS time should still not be active since the
// parameter has not been explicitly set.
EXPECT_FALSE(ros_clock->ros_time_is_active());

View File

@@ -73,6 +73,20 @@ protected:
EXPECT_FALSE(timer->is_steady());
break;
}
timer_without_autostart = test_node->create_wall_timer(
100ms,
[this]() -> void
{
this->has_timer_run.store(true);
if (this->cancel_timer.load()) {
this->timer->cancel();
}
// prevent any tests running timer from blocking
this->executor->cancel();
}, nullptr, false);
EXPECT_TRUE(timer_without_autostart->is_steady());
executor->add_node(test_node);
// don't start spinning, let the test dictate when
}
@@ -93,6 +107,7 @@ protected:
std::atomic<bool> cancel_timer;
rclcpp::Node::SharedPtr test_node;
std::shared_ptr<rclcpp::TimerBase> timer;
std::shared_ptr<rclcpp::TimerBase> timer_without_autostart;
std::shared_ptr<rclcpp::executors::SingleThreadedExecutor> executor;
};
@@ -334,3 +349,18 @@ INSTANTIATE_TEST_SUITE_P(
return std::string("unknown");
}
);
/// Simple test of a timer without autostart
TEST_P(TestTimer, test_timer_without_autostart)
{
EXPECT_TRUE(timer_without_autostart->is_canceled());
EXPECT_EQ(
timer_without_autostart->time_until_trigger().count(),
std::chrono::nanoseconds::max().count());
// Reset to change start timer
timer_without_autostart->reset();
EXPECT_LE(
timer_without_autostart->time_until_trigger().count(),
std::chrono::nanoseconds::max().count());
EXPECT_FALSE(timer_without_autostart->is_canceled());
}

View File

@@ -3,31 +3,22 @@ Changelog for package rclcpp_action
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
21.0.8 (2024-11-08)
22.1.0 (2023-08-21)
-------------------
21.0.7 (2024-07-10)
-------------------
* fix: Fixed race condition in action server between is_ready and take"… (`#2531 <https://github.com/ros2/rclcpp/issues/2531>`_)
* Do not generate the exception when action service response timeout. (`#2519 <https://github.com/ros2/rclcpp/issues/2519>`_)
* Contributors: Janosch Machowinski, Tomoya Fujita, William Woodall
21.0.6 (2024-04-19)
22.0.0 (2023-07-11)
-------------------
21.0.5 (2024-02-07)
21.3.0 (2023-06-12)
-------------------
21.0.4 (2023-11-17)
21.2.0 (2023-06-07)
-------------------
21.0.3 (2023-09-08)
21.1.1 (2023-05-11)
-------------------
21.0.2 (2023-07-14)
-------------------
21.0.1 (2023-05-11)
21.1.0 (2023-04-27)
-------------------
21.0.0 (2023-04-18)

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

View File

@@ -19,7 +19,6 @@
#include <string>
#include <tuple>
#include <utility>
#include <variant>
#include "rcl_action/action_client.h"
#include "rcl_action/wait.h"
@@ -32,67 +31,6 @@
namespace rclcpp_action
{
struct ClientBaseData
{
struct FeedbackReadyData
{
FeedbackReadyData(rcl_ret_t retIn, std::shared_ptr<void> msg)
: ret(retIn), feedback_message(msg) {}
rcl_ret_t ret;
std::shared_ptr<void> feedback_message;
};
struct StatusReadyData
{
StatusReadyData(rcl_ret_t retIn, std::shared_ptr<void> msg)
: ret(retIn), status_message(msg) {}
rcl_ret_t ret;
std::shared_ptr<void> status_message;
};
struct GoalResponseData
{
GoalResponseData(rcl_ret_t retIn, rmw_request_id_t header, std::shared_ptr<void> response)
: ret(retIn), response_header(header), goal_response(response) {}
rcl_ret_t ret;
rmw_request_id_t response_header;
std::shared_ptr<void> goal_response;
};
struct CancelResponseData
{
CancelResponseData(rcl_ret_t retIn, rmw_request_id_t header, std::shared_ptr<void> response)
: ret(retIn), response_header(header), cancel_response(response) {}
rcl_ret_t ret;
rmw_request_id_t response_header;
std::shared_ptr<void> cancel_response;
};
struct ResultResponseData
{
ResultResponseData(rcl_ret_t retIn, rmw_request_id_t header, std::shared_ptr<void> response)
: ret(retIn), response_header(header), result_response(response) {}
rcl_ret_t ret;
rmw_request_id_t response_header;
std::shared_ptr<void> result_response;
};
std::variant<
FeedbackReadyData,
StatusReadyData,
GoalResponseData,
CancelResponseData,
ResultResponseData
> data;
explicit ClientBaseData(FeedbackReadyData && data_in)
: data(std::move(data_in)) {}
explicit ClientBaseData(StatusReadyData && data_in)
: data(std::move(data_in)) {}
explicit ClientBaseData(GoalResponseData && data_in)
: data(std::move(data_in)) {}
explicit ClientBaseData(CancelResponseData && data_in)
: data(std::move(data_in)) {}
explicit ClientBaseData(ResultResponseData && data_in)
: data(std::move(data_in)) {}
};
class ClientBaseImpl
{
public:
@@ -156,13 +94,11 @@ public:
size_t num_clients{0u};
size_t num_services{0u};
// Lock for action_client_
std::recursive_mutex action_client_mutex_;
// next ready event for taking, will be set by is_ready and will be processed by take_data
std::atomic<size_t> next_ready_event;
// used to indicate that next_ready_event has no ready event for processing
static constexpr size_t NO_EVENT_READY = std::numeric_limits<size_t>::max();
bool is_feedback_ready{false};
bool is_status_ready{false};
bool is_goal_response_ready{false};
bool is_cancel_response_ready{false};
bool is_result_response_ready{false};
rclcpp::Context::SharedPtr context_;
rclcpp::node_interfaces::NodeGraphInterface::WeakPtr node_graph_;
@@ -206,7 +142,6 @@ bool
ClientBase::action_server_is_ready() const
{
bool is_ready;
std::lock_guard<std::recursive_mutex> lock(pimpl_->action_client_mutex_);
rcl_ret_t ret = rcl_action_server_is_available(
this->pimpl_->node_handle.get(),
this->pimpl_->client_handle.get(),
@@ -320,7 +255,6 @@ ClientBase::get_number_of_ready_services()
void
ClientBase::add_to_wait_set(rcl_wait_set_t * wait_set)
{
std::lock_guard<std::recursive_mutex> lock(pimpl_->action_client_mutex_);
rcl_ret_t ret = rcl_action_wait_set_add_action_client(
wait_set, pimpl_->client_handle.get(), nullptr, nullptr);
if (RCL_RET_OK != ret) {
@@ -331,56 +265,23 @@ ClientBase::add_to_wait_set(rcl_wait_set_t * wait_set)
bool
ClientBase::is_ready(rcl_wait_set_t * wait_set)
{
bool is_feedback_ready{false};
bool is_status_ready{false};
bool is_goal_response_ready{false};
bool is_cancel_response_ready{false};
bool is_result_response_ready{false};
rcl_ret_t ret;
{
std::lock_guard<std::recursive_mutex> lock(pimpl_->action_client_mutex_);
ret = rcl_action_client_wait_set_get_entities_ready(
wait_set, pimpl_->client_handle.get(),
&is_feedback_ready,
&is_status_ready,
&is_goal_response_ready,
&is_cancel_response_ready,
&is_result_response_ready);
if (RCL_RET_OK != ret) {
rclcpp::exceptions::throw_from_rcl_error(
ret, "failed to check for any ready entities");
}
rcl_ret_t ret = rcl_action_client_wait_set_get_entities_ready(
wait_set, pimpl_->client_handle.get(),
&pimpl_->is_feedback_ready,
&pimpl_->is_status_ready,
&pimpl_->is_goal_response_ready,
&pimpl_->is_cancel_response_ready,
&pimpl_->is_result_response_ready);
if (RCL_RET_OK != ret) {
rclcpp::exceptions::throw_from_rcl_error(
ret, "failed to check for any ready entities");
}
pimpl_->next_ready_event = ClientBaseImpl::NO_EVENT_READY;
if (is_feedback_ready) {
pimpl_->next_ready_event = static_cast<size_t>(EntityType::FeedbackSubscription);
return true;
}
if (is_status_ready) {
pimpl_->next_ready_event = static_cast<size_t>(EntityType::StatusSubscription);
return true;
}
if (is_goal_response_ready) {
pimpl_->next_ready_event = static_cast<size_t>(EntityType::GoalClient);
return true;
}
if (is_result_response_ready) {
pimpl_->next_ready_event = static_cast<size_t>(EntityType::ResultClient);
return true;
}
if (is_cancel_response_ready) {
pimpl_->next_ready_event = static_cast<size_t>(EntityType::CancelClient);
return true;
}
return false;
return
pimpl_->is_feedback_ready ||
pimpl_->is_status_ready ||
pimpl_->is_goal_response_ready ||
pimpl_->is_cancel_response_ready ||
pimpl_->is_result_response_ready;
}
void
@@ -531,6 +432,7 @@ ClientBase::set_callback_to_entity(
}
};
// Set it temporarily to the new callback, while we replace the old one.
// This two-step setting, prevents a gap where the old std::function has
// been replaced but the middleware hasn't been told about the new one yet.
@@ -648,159 +550,140 @@ ClientBase::clear_on_ready_callback()
std::shared_ptr<void>
ClientBase::take_data()
{
// next_ready_event is an atomic, caching localy
size_t next_ready_event = pimpl_->next_ready_event.exchange(ClientBaseImpl::NO_EVENT_READY);
if (next_ready_event == ClientBaseImpl::NO_EVENT_READY) {
// there is a known bug in iron, that take_data might be called multiple
// times. Therefore instead of throwing, we just return a nullptr as a workaround.
return nullptr;
if (pimpl_->is_feedback_ready) {
std::shared_ptr<void> feedback_message = this->create_feedback_message();
rcl_ret_t ret = rcl_action_take_feedback(
pimpl_->client_handle.get(), feedback_message.get());
return std::static_pointer_cast<void>(
std::make_shared<std::tuple<rcl_ret_t, std::shared_ptr<void>>>(
ret, feedback_message));
} else if (pimpl_->is_status_ready) {
std::shared_ptr<void> status_message = this->create_status_message();
rcl_ret_t ret = rcl_action_take_status(
pimpl_->client_handle.get(), status_message.get());
return std::static_pointer_cast<void>(
std::make_shared<std::tuple<rcl_ret_t, std::shared_ptr<void>>>(
ret, status_message));
} else if (pimpl_->is_goal_response_ready) {
rmw_request_id_t response_header;
std::shared_ptr<void> goal_response = this->create_goal_response();
rcl_ret_t ret = rcl_action_take_goal_response(
pimpl_->client_handle.get(), &response_header, goal_response.get());
return std::static_pointer_cast<void>(
std::make_shared<std::tuple<rcl_ret_t, rmw_request_id_t, std::shared_ptr<void>>>(
ret, response_header, goal_response));
} else if (pimpl_->is_result_response_ready) {
rmw_request_id_t response_header;
std::shared_ptr<void> result_response = this->create_result_response();
rcl_ret_t ret = rcl_action_take_result_response(
pimpl_->client_handle.get(), &response_header, result_response.get());
return std::static_pointer_cast<void>(
std::make_shared<std::tuple<rcl_ret_t, rmw_request_id_t, std::shared_ptr<void>>>(
ret, response_header, result_response));
} else if (pimpl_->is_cancel_response_ready) {
rmw_request_id_t response_header;
std::shared_ptr<void> cancel_response = this->create_cancel_response();
rcl_ret_t ret = rcl_action_take_cancel_response(
pimpl_->client_handle.get(), &response_header, cancel_response.get());
return std::static_pointer_cast<void>(
std::make_shared<std::tuple<rcl_ret_t, rmw_request_id_t, std::shared_ptr<void>>>(
ret, response_header, cancel_response));
} else {
throw std::runtime_error("Taking data from action client but nothing is ready");
}
return take_data_by_entity_id(next_ready_event);
}
std::shared_ptr<void>
ClientBase::take_data_by_entity_id(size_t id)
{
std::shared_ptr<ClientBaseData> data_ptr;
rcl_ret_t ret;
// Mark as ready the entity from which we want to take data
switch (static_cast<EntityType>(id)) {
case EntityType::GoalClient:
{
rmw_request_id_t response_header;
std::shared_ptr<void> goal_response;
{
std::lock_guard<std::recursive_mutex> lock(pimpl_->action_client_mutex_);
goal_response = this->create_goal_response();
ret = rcl_action_take_goal_response(
pimpl_->client_handle.get(), &response_header, goal_response.get());
}
data_ptr = std::make_shared<ClientBaseData>(
ClientBaseData::GoalResponseData(
ret, response_header, goal_response));
}
pimpl_->is_goal_response_ready = true;
break;
case EntityType::ResultClient:
{
rmw_request_id_t response_header;
std::shared_ptr<void> result_response;
{
std::lock_guard<std::recursive_mutex> lock(pimpl_->action_client_mutex_);
result_response = this->create_result_response();
ret = rcl_action_take_result_response(
pimpl_->client_handle.get(), &response_header, result_response.get());
}
data_ptr =
std::make_shared<ClientBaseData>(
ClientBaseData::ResultResponseData(
ret, response_header, result_response));
}
pimpl_->is_result_response_ready = true;
break;
case EntityType::CancelClient:
{
rmw_request_id_t response_header;
std::shared_ptr<void> cancel_response;
{
std::lock_guard<std::recursive_mutex> lock(pimpl_->action_client_mutex_);
cancel_response = this->create_cancel_response();
ret = rcl_action_take_cancel_response(
pimpl_->client_handle.get(), &response_header, cancel_response.get());
}
data_ptr =
std::make_shared<ClientBaseData>(
ClientBaseData::CancelResponseData(
ret, response_header, cancel_response));
}
pimpl_->is_cancel_response_ready = true;
break;
case EntityType::FeedbackSubscription:
{
std::shared_ptr<void> feedback_message;
{
std::lock_guard<std::recursive_mutex> lock(pimpl_->action_client_mutex_);
feedback_message = this->create_feedback_message();
ret = rcl_action_take_feedback(
pimpl_->client_handle.get(), feedback_message.get());
}
data_ptr =
std::make_shared<ClientBaseData>(
ClientBaseData::FeedbackReadyData(
ret, feedback_message));
}
pimpl_->is_feedback_ready = true;
break;
case EntityType::StatusSubscription:
{
std::shared_ptr<void> status_message;
{
std::lock_guard<std::recursive_mutex> lock(pimpl_->action_client_mutex_);
status_message = this->create_status_message();
ret = rcl_action_take_status(
pimpl_->client_handle.get(), status_message.get());
}
data_ptr =
std::make_shared<ClientBaseData>(
ClientBaseData::StatusReadyData(
ret, status_message));
}
pimpl_->is_status_ready = true;
break;
}
return std::static_pointer_cast<void>(data_ptr);
return take_data();
}
void
ClientBase::execute(std::shared_ptr<void> & data_in)
ClientBase::execute(std::shared_ptr<void> & data)
{
if (!data_in) {
// workaround, if take_data was called multiple timed, it returns a nullptr
// normally we should throw here, but as an API stable bug fix, we just ignore this...
return;
if (!data) {
throw std::runtime_error("'data' is empty");
}
std::shared_ptr<ClientBaseData> data_ptr = std::static_pointer_cast<ClientBaseData>(data_in);
std::visit(
[&](auto && data) -> void {
using T = std::decay_t<decltype(data)>;
if constexpr (std::is_same_v<T, ClientBaseData::FeedbackReadyData>) {
if (RCL_RET_OK == data.ret) {
this->handle_feedback_message(data.feedback_message);
} else if (RCL_RET_ACTION_CLIENT_TAKE_FAILED != data.ret) {
rclcpp::exceptions::throw_from_rcl_error(data.ret, "error taking feedback");
}
}
if constexpr (std::is_same_v<T, ClientBaseData::StatusReadyData>) {
if (RCL_RET_OK == data.ret) {
this->handle_status_message(data.status_message);
} else if (RCL_RET_ACTION_CLIENT_TAKE_FAILED != data.ret) {
rclcpp::exceptions::throw_from_rcl_error(data.ret, "error taking status");
}
}
if constexpr (std::is_same_v<T, ClientBaseData::GoalResponseData>) {
if (RCL_RET_OK == data.ret) {
this->handle_goal_response(data.response_header, data.goal_response);
} else if (RCL_RET_ACTION_CLIENT_TAKE_FAILED != data.ret) {
rclcpp::exceptions::throw_from_rcl_error(data.ret, "error taking goal response");
}
}
if constexpr (std::is_same_v<T, ClientBaseData::ResultResponseData>) {
if (RCL_RET_OK == data.ret) {
this->handle_result_response(data.response_header, data.result_response);
} else if (RCL_RET_ACTION_CLIENT_TAKE_FAILED != data.ret) {
rclcpp::exceptions::throw_from_rcl_error(data.ret, "error taking result response");
}
}
if constexpr (std::is_same_v<T, ClientBaseData::CancelResponseData>) {
if (RCL_RET_OK == data.ret) {
this->handle_cancel_response(data.response_header, data.cancel_response);
} else if (RCL_RET_ACTION_CLIENT_TAKE_FAILED != data.ret) {
rclcpp::exceptions::throw_from_rcl_error(data.ret, "error taking cancel response");
}
}
}, data_ptr->data);
if (pimpl_->is_feedback_ready) {
auto shared_ptr = std::static_pointer_cast<std::tuple<rcl_ret_t, std::shared_ptr<void>>>(data);
auto ret = std::get<0>(*shared_ptr);
pimpl_->is_feedback_ready = false;
if (RCL_RET_OK == ret) {
auto feedback_message = std::get<1>(*shared_ptr);
this->handle_feedback_message(feedback_message);
} else if (RCL_RET_ACTION_CLIENT_TAKE_FAILED != ret) {
rclcpp::exceptions::throw_from_rcl_error(ret, "error taking feedback");
}
} else if (pimpl_->is_status_ready) {
auto shared_ptr = std::static_pointer_cast<std::tuple<rcl_ret_t, std::shared_ptr<void>>>(data);
auto ret = std::get<0>(*shared_ptr);
pimpl_->is_status_ready = false;
if (RCL_RET_OK == ret) {
auto status_message = std::get<1>(*shared_ptr);
this->handle_status_message(status_message);
} else if (RCL_RET_ACTION_CLIENT_TAKE_FAILED != ret) {
rclcpp::exceptions::throw_from_rcl_error(ret, "error taking status");
}
} else if (pimpl_->is_goal_response_ready) {
auto shared_ptr = std::static_pointer_cast<
std::tuple<rcl_ret_t, rmw_request_id_t, std::shared_ptr<void>>>(data);
auto ret = std::get<0>(*shared_ptr);
pimpl_->is_goal_response_ready = false;
if (RCL_RET_OK == ret) {
auto response_header = std::get<1>(*shared_ptr);
auto goal_response = std::get<2>(*shared_ptr);
this->handle_goal_response(response_header, goal_response);
} else if (RCL_RET_ACTION_CLIENT_TAKE_FAILED != ret) {
rclcpp::exceptions::throw_from_rcl_error(ret, "error taking goal response");
}
} else if (pimpl_->is_result_response_ready) {
auto shared_ptr = std::static_pointer_cast<
std::tuple<rcl_ret_t, rmw_request_id_t, std::shared_ptr<void>>>(data);
auto ret = std::get<0>(*shared_ptr);
pimpl_->is_result_response_ready = false;
if (RCL_RET_OK == ret) {
auto response_header = std::get<1>(*shared_ptr);
auto result_response = std::get<2>(*shared_ptr);
this->handle_result_response(response_header, result_response);
} else if (RCL_RET_ACTION_CLIENT_TAKE_FAILED != ret) {
rclcpp::exceptions::throw_from_rcl_error(ret, "error taking result response");
}
} else if (pimpl_->is_cancel_response_ready) {
auto shared_ptr = std::static_pointer_cast<
std::tuple<rcl_ret_t, rmw_request_id_t, std::shared_ptr<void>>>(data);
auto ret = std::get<0>(*shared_ptr);
pimpl_->is_cancel_response_ready = false;
if (RCL_RET_OK == ret) {
auto response_header = std::get<1>(*shared_ptr);
auto cancel_response = std::get<2>(*shared_ptr);
this->handle_cancel_response(response_header, cancel_response);
} else if (RCL_RET_ACTION_CLIENT_TAKE_FAILED != ret) {
rclcpp::exceptions::throw_from_rcl_error(ret, "error taking cancel response");
}
} else {
throw std::runtime_error("Executing action client but nothing is ready");
}
}
} // namespace rclcpp_action

View File

@@ -18,7 +18,6 @@
#include <tuple>
#include <unordered_map>
#include <utility>
#include <variant>
#include <vector>
#include "rcl_action/action_server.h"
@@ -34,50 +33,8 @@
using rclcpp_action::ServerBase;
using rclcpp_action::GoalUUID;
struct ServerBaseData;
namespace rclcpp_action
{
struct ServerBaseData
{
using GoalRequestData = std::tuple<
rcl_ret_t,
const rcl_action_goal_info_t,
rmw_request_id_t,
std::shared_ptr<void>
>;
using CancelRequestData = std::tuple<
rcl_ret_t,
std::shared_ptr<action_msgs::srv::CancelGoal::Request>,
rmw_request_id_t
>;
using ResultRequestData = std::tuple<rcl_ret_t, std::shared_ptr<void>, rmw_request_id_t>;
using GoalExpiredData = struct Empty {};
std::variant<GoalRequestData, CancelRequestData, ResultRequestData, GoalExpiredData> data;
explicit ServerBaseData(GoalRequestData && data_in)
: data(std::move(data_in)) {}
explicit ServerBaseData(CancelRequestData && data_in)
: data(std::move(data_in)) {}
explicit ServerBaseData(ResultRequestData && data_in)
: data(std::move(data_in)) {}
explicit ServerBaseData(GoalExpiredData && data_in)
: data(std::move(data_in)) {}
};
enum class ActionEventType : std::size_t
{
GoalService,
ResultService,
CancelService,
Expired,
};
class ServerBaseImpl
{
public:
@@ -103,6 +60,11 @@ public:
size_t num_services_ = 0;
size_t num_guard_conditions_ = 0;
std::atomic<bool> goal_request_ready_{false};
std::atomic<bool> cancel_request_ready_{false};
std::atomic<bool> result_request_ready_{false};
std::atomic<bool> goal_expired_{false};
// Lock for unordered_maps
std::recursive_mutex unordered_map_mutex_;
@@ -113,15 +75,8 @@ public:
// rcl goal handles are kept so api to send result doesn't try to access freed memory
std::unordered_map<GoalUUID, std::shared_ptr<rcl_action_goal_handle_t>> goal_handles_;
// next ready event for taking, will be set by is_ready and will be processed by take_data
std::atomic<size_t> next_ready_event;
// used to indicate that next_ready_event has no ready event for processing
static constexpr size_t NO_EVENT_READY = std::numeric_limits<size_t>::max();
rclcpp::Logger logger_;
};
} // namespace rclcpp_action
ServerBase::ServerBase(
@@ -239,170 +194,124 @@ ServerBase::is_ready(rcl_wait_set_t * wait_set)
&goal_expired);
}
pimpl_->goal_request_ready_ = goal_request_ready;
pimpl_->cancel_request_ready_ = cancel_request_ready;
pimpl_->result_request_ready_ = result_request_ready;
pimpl_->goal_expired_ = goal_expired;
if (RCL_RET_OK != ret) {
rclcpp::exceptions::throw_from_rcl_error(ret);
}
pimpl_->next_ready_event = ServerBaseImpl::NO_EVENT_READY;
if (goal_request_ready) {
pimpl_->next_ready_event = static_cast<uint32_t>(ActionEventType::GoalService);
return true;
}
if (cancel_request_ready) {
pimpl_->next_ready_event = static_cast<uint32_t>(ActionEventType::CancelService);
return true;
}
if (result_request_ready) {
pimpl_->next_ready_event = static_cast<uint32_t>(ActionEventType::ResultService);
return true;
}
if (goal_expired) {
pimpl_->next_ready_event = static_cast<uint32_t>(ActionEventType::Expired);
return true;
}
return false;
return pimpl_->goal_request_ready_.load() ||
pimpl_->cancel_request_ready_.load() ||
pimpl_->result_request_ready_.load() ||
pimpl_->goal_expired_.load();
}
std::shared_ptr<void>
ServerBase::take_data()
{
size_t next_ready_event = pimpl_->next_ready_event.exchange(ServerBaseImpl::NO_EVENT_READY);
if (pimpl_->goal_request_ready_.load()) {
rcl_ret_t ret;
rcl_action_goal_info_t goal_info = rcl_action_get_zero_initialized_goal_info();
rmw_request_id_t request_header;
if (next_ready_event == ServerBaseImpl::NO_EVENT_READY) {
// there is a known bug in iron, that take_data might be called multiple
// times. Therefore instead of throwing, we just return a nullptr as a workaround.
std::lock_guard<std::recursive_mutex> lock(pimpl_->action_server_reentrant_mutex_);
std::shared_ptr<void> message = create_goal_request();
ret = rcl_action_take_goal_request(
pimpl_->action_server_.get(),
&request_header,
message.get());
return std::static_pointer_cast<void>(
std::make_shared
<std::tuple<rcl_ret_t, rcl_action_goal_info_t, rmw_request_id_t, std::shared_ptr<void>>>(
ret,
goal_info,
request_header, message));
} else if (pimpl_->cancel_request_ready_.load()) {
rcl_ret_t ret;
rmw_request_id_t request_header;
// Initialize cancel request
auto request = std::make_shared<action_msgs::srv::CancelGoal::Request>();
std::lock_guard<std::recursive_mutex> lock(pimpl_->action_server_reentrant_mutex_);
ret = rcl_action_take_cancel_request(
pimpl_->action_server_.get(),
&request_header,
request.get());
return std::static_pointer_cast<void>(
std::make_shared
<std::tuple<rcl_ret_t, std::shared_ptr<action_msgs::srv::CancelGoal::Request>,
rmw_request_id_t>>(ret, request, request_header));
} else if (pimpl_->result_request_ready_.load()) {
rcl_ret_t ret;
// Get the result request message
rmw_request_id_t request_header;
std::shared_ptr<void> result_request = create_result_request();
std::lock_guard<std::recursive_mutex> lock(pimpl_->action_server_reentrant_mutex_);
ret = rcl_action_take_result_request(
pimpl_->action_server_.get(), &request_header, result_request.get());
return std::static_pointer_cast<void>(
std::make_shared<std::tuple<rcl_ret_t, std::shared_ptr<void>, rmw_request_id_t>>(
ret, result_request, request_header));
} else if (pimpl_->goal_expired_.load()) {
return nullptr;
} else {
throw std::runtime_error("Taking data from action server but nothing is ready");
}
return take_data_by_entity_id(next_ready_event);
}
std::shared_ptr<void>
ServerBase::take_data_by_entity_id(size_t id)
{
static_assert(
static_cast<size_t>(EntityType::GoalService) ==
static_cast<size_t>(ActionEventType::GoalService));
static_assert(
static_cast<size_t>(EntityType::ResultService) ==
static_cast<size_t>(ActionEventType::ResultService));
static_assert(
static_cast<size_t>(EntityType::CancelService) ==
static_cast<size_t>(ActionEventType::CancelService));
std::shared_ptr<ServerBaseData> data_ptr;
// Mark as ready the entity from which we want to take data
switch (static_cast<ActionEventType>(id)) {
case ActionEventType::GoalService:
{
rcl_ret_t ret;
rcl_action_goal_info_t goal_info = rcl_action_get_zero_initialized_goal_info();
rmw_request_id_t request_header;
std::lock_guard<std::recursive_mutex> lock(pimpl_->action_server_reentrant_mutex_);
std::shared_ptr<void> message = create_goal_request();
ret = rcl_action_take_goal_request(
pimpl_->action_server_.get(),
&request_header,
message.get());
data_ptr = std::make_shared<ServerBaseData>(
ServerBaseData::GoalRequestData(ret, goal_info, request_header, message));
}
switch (static_cast<EntityType>(id)) {
case EntityType::GoalService:
pimpl_->goal_request_ready_ = true;
break;
case ActionEventType::ResultService:
{
rcl_ret_t ret;
// Get the result request message
rmw_request_id_t request_header;
std::shared_ptr<void> result_request = create_result_request();
std::lock_guard<std::recursive_mutex> lock(pimpl_->action_server_reentrant_mutex_);
ret = rcl_action_take_result_request(
pimpl_->action_server_.get(), &request_header, result_request.get());
data_ptr =
std::make_shared<ServerBaseData>(
ServerBaseData::ResultRequestData(ret, result_request, request_header));
}
case EntityType::ResultService:
pimpl_->result_request_ready_ = true;
break;
case ActionEventType::CancelService:
{
rcl_ret_t ret;
rmw_request_id_t request_header;
// Initialize cancel request
auto request = std::make_shared<action_msgs::srv::CancelGoal::Request>();
std::lock_guard<std::recursive_mutex> lock(pimpl_->action_server_reentrant_mutex_);
ret = rcl_action_take_cancel_request(
pimpl_->action_server_.get(),
&request_header,
request.get());
data_ptr =
std::make_shared<ServerBaseData>(
ServerBaseData::CancelRequestData(ret, request, request_header));
}
break;
case ActionEventType::Expired:
{
data_ptr =
std::make_shared<ServerBaseData>(ServerBaseData::GoalExpiredData());
}
case EntityType::CancelService:
pimpl_->cancel_request_ready_ = true;
break;
}
return std::static_pointer_cast<void>(data_ptr);
return take_data();
}
void
ServerBase::execute(std::shared_ptr<void> & data_in)
ServerBase::execute(std::shared_ptr<void> & data)
{
if (!data_in) {
// workaround, if take_data was called multiple timed, it returns a nullptr
// normally we should throw here, but as an API stable bug fix, we just ignore this...
return;
if (!data && !pimpl_->goal_expired_.load()) {
throw std::runtime_error("'data' is empty");
}
std::shared_ptr<ServerBaseData> data_ptr = std::static_pointer_cast<ServerBaseData>(data_in);
std::visit(
[&](auto && data) -> void {
using T = std::decay_t<decltype(data)>;
if constexpr (std::is_same_v<T, ServerBaseData::GoalRequestData>) {
execute_goal_request_received(data_in);
}
if constexpr (std::is_same_v<T, ServerBaseData::CancelRequestData>) {
execute_cancel_request_received(data_in);
}
if constexpr (std::is_same_v<T, ServerBaseData::ResultRequestData>) {
execute_result_request_received(data_in);
}
if constexpr (std::is_same_v<T, ServerBaseData::GoalExpiredData>) {
execute_check_expired_goals();
}
},
data_ptr->data);
if (pimpl_->goal_request_ready_.load()) {
execute_goal_request_received(data);
} else if (pimpl_->cancel_request_ready_.load()) {
execute_cancel_request_received(data);
} else if (pimpl_->result_request_ready_.load()) {
execute_result_request_received(data);
} else if (pimpl_->goal_expired_.load()) {
execute_check_expired_goals();
} else {
throw std::runtime_error("Executing action server but nothing is ready");
}
}
void
ServerBase::execute_goal_request_received(std::shared_ptr<void> & data)
{
std::shared_ptr<ServerBaseData> data_ptr = std::static_pointer_cast<ServerBaseData>(data);
const ServerBaseData::GoalRequestData & gData(
std::get<ServerBaseData::GoalRequestData>(data_ptr->data));
rcl_ret_t ret = std::get<0>(gData);
rcl_action_goal_info_t goal_info = std::get<1>(gData);
rmw_request_id_t request_header = std::get<2>(gData);
const std::shared_ptr<void> message = std::get<3>(gData);
auto shared_ptr = std::static_pointer_cast
<std::tuple<rcl_ret_t, rcl_action_goal_info_t, rmw_request_id_t, std::shared_ptr<void>>>(data);
rcl_ret_t ret = std::get<0>(*shared_ptr);
if (RCL_RET_ACTION_SERVER_TAKE_FAILED == ret) {
// Ignore take failure because connext fails if it receives a sample without valid data.
// This happens when a client shuts down and connext receives a sample saying the client is
@@ -411,6 +320,14 @@ ServerBase::execute_goal_request_received(std::shared_ptr<void> & data)
} else if (RCL_RET_OK != ret) {
rclcpp::exceptions::throw_from_rcl_error(ret);
}
rcl_action_goal_info_t goal_info = std::get<1>(*shared_ptr);
rmw_request_id_t request_header = std::get<2>(*shared_ptr);
std::shared_ptr<void> message = std::get<3>(*shared_ptr);
bool expected = true;
if (!pimpl_->goal_request_ready_.compare_exchange_strong(expected, false)) {
return;
}
GoalUUID uuid = get_goal_id_from_goal_request(message.get());
convert(uuid, &goal_info);
@@ -427,16 +344,7 @@ ServerBase::execute_goal_request_received(std::shared_ptr<void> & data)
}
if (RCL_RET_OK != ret) {
if (ret == RCL_RET_TIMEOUT) {
RCLCPP_WARN(
pimpl_->logger_,
"Failed to send goal response %s (timeout): %s",
to_string(uuid).c_str(), rcl_get_error_string().str);
rcl_reset_error();
return;
} else {
rclcpp::exceptions::throw_from_rcl_error(ret);
}
rclcpp::exceptions::throw_from_rcl_error(ret);
}
const auto status = response_pair.first;
@@ -494,15 +402,10 @@ ServerBase::execute_goal_request_received(std::shared_ptr<void> & data)
void
ServerBase::execute_cancel_request_received(std::shared_ptr<void> & data)
{
std::shared_ptr<ServerBaseData> data_ptr = std::static_pointer_cast<ServerBaseData>(data);
const ServerBaseData::CancelRequestData & gData(
std::get<ServerBaseData::CancelRequestData>(data_ptr->data));
rcl_ret_t ret = std::get<0>(gData);
std::shared_ptr<action_msgs::srv::CancelGoal::Request> request = std::get<1>(gData);
rmw_request_id_t request_header = std::get<2>(gData);
auto shared_ptr = std::static_pointer_cast
<std::tuple<rcl_ret_t, std::shared_ptr<action_msgs::srv::CancelGoal::Request>,
rmw_request_id_t>>(data);
auto ret = std::get<0>(*shared_ptr);
if (RCL_RET_ACTION_SERVER_TAKE_FAILED == ret) {
// Ignore take failure because connext fails if it receives a sample without valid data.
// This happens when a client shuts down and connext receives a sample saying the client is
@@ -511,6 +414,9 @@ ServerBase::execute_cancel_request_received(std::shared_ptr<void> & data)
} else if (RCL_RET_OK != ret) {
rclcpp::exceptions::throw_from_rcl_error(ret);
}
auto request = std::get<1>(*shared_ptr);
auto request_header = std::get<2>(*shared_ptr);
pimpl_->cancel_request_ready_ = false;
// Convert c++ message to C message
rcl_action_cancel_request_t cancel_request = rcl_action_get_zero_initialized_cancel_request();
@@ -577,15 +483,6 @@ ServerBase::execute_cancel_request_received(std::shared_ptr<void> & data)
pimpl_->action_server_.get(), &request_header, response.get());
}
if (ret == RCL_RET_TIMEOUT) {
GoalUUID uuid = request->goal_info.goal_id.uuid;
RCLCPP_WARN(
pimpl_->logger_,
"Failed to send cancel response %s (timeout): %s",
to_string(uuid).c_str(), rcl_get_error_string().str);
rcl_reset_error();
return;
}
if (RCL_RET_OK != ret) {
rclcpp::exceptions::throw_from_rcl_error(ret);
}
@@ -595,14 +492,9 @@ ServerBase::execute_cancel_request_received(std::shared_ptr<void> & data)
void
ServerBase::execute_result_request_received(std::shared_ptr<void> & data)
{
std::shared_ptr<ServerBaseData> data_ptr = std::static_pointer_cast<ServerBaseData>(data);
const ServerBaseData::ResultRequestData & gData(
std::get<ServerBaseData::ResultRequestData>(data_ptr->data));
rcl_ret_t ret = std::get<0>(gData);
std::shared_ptr<void> result_request = std::get<1>(gData);
rmw_request_id_t request_header = std::get<2>(gData);
auto shared_ptr = std::static_pointer_cast
<std::tuple<rcl_ret_t, std::shared_ptr<void>, rmw_request_id_t>>(data);
auto ret = std::get<0>(*shared_ptr);
if (RCL_RET_ACTION_SERVER_TAKE_FAILED == ret) {
// Ignore take failure because connext fails if it receives a sample without valid data.
// This happens when a client shuts down and connext receives a sample saying the client is
@@ -611,7 +503,10 @@ ServerBase::execute_result_request_received(std::shared_ptr<void> & data)
} else if (RCL_RET_OK != ret) {
rclcpp::exceptions::throw_from_rcl_error(ret);
}
auto result_request = std::get<1>(*shared_ptr);
auto request_header = std::get<2>(*shared_ptr);
pimpl_->result_request_ready_ = false;
std::shared_ptr<void> result_response;
// check if the goal exists
@@ -643,14 +538,6 @@ ServerBase::execute_result_request_received(std::shared_ptr<void> & data)
std::lock_guard<std::recursive_mutex> lock(pimpl_->action_server_reentrant_mutex_);
rcl_ret_t rcl_ret = rcl_action_send_result_response(
pimpl_->action_server_.get(), &request_header, result_response.get());
if (rcl_ret == RCL_RET_TIMEOUT) {
RCLCPP_WARN(
pimpl_->logger_,
"Failed to send result response %s (timeout): %s",
to_string(uuid).c_str(), rcl_get_error_string().str);
rcl_reset_error();
return;
}
if (RCL_RET_OK != rcl_ret) {
rclcpp::exceptions::throw_from_rcl_error(rcl_ret);
}
@@ -784,13 +671,7 @@ ServerBase::publish_result(const GoalUUID & uuid, std::shared_ptr<void> result_m
for (auto & request_header : iter->second) {
rcl_ret_t ret = rcl_action_send_result_response(
pimpl_->action_server_.get(), &request_header, result_msg.get());
if (ret == RCL_RET_TIMEOUT) {
RCLCPP_WARN(
pimpl_->logger_,
"Failed to send result response %s (timeout): %s",
to_string(uuid).c_str(), rcl_get_error_string().str);
rcl_reset_error();
} else if (RCL_RET_OK != ret) {
if (RCL_RET_OK != ret) {
rclcpp::exceptions::throw_from_rcl_error(ret);
}
}

View File

@@ -2,32 +2,22 @@
Changelog for package rclcpp_components
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
21.0.8 (2024-11-08)
22.1.0 (2023-08-21)
-------------------
21.0.7 (2024-07-10)
22.0.0 (2023-07-11)
-------------------
21.0.6 (2024-04-19)
21.3.0 (2023-06-12)
-------------------
21.0.5 (2024-02-07)
-------------------
* Increase the service queue sizes in component_container (`#2381 <https://github.com/ros2/rclcpp/issues/2381>`_)
* Contributors: M. Fatih Cırıt
21.0.4 (2023-11-17)
-------------------
* Add missing header required by the rclcpp::NodeOptions type (`#2325 <https://github.com/ros2/rclcpp/issues/2325>`_)
* Contributors: Ignacio Vizzo
21.0.3 (2023-09-08)
21.2.0 (2023-06-07)
-------------------
21.0.2 (2023-07-14)
21.1.1 (2023-05-11)
-------------------
21.0.1 (2023-05-11)
21.1.0 (2023-04-27)
-------------------
21.0.0 (2023-04-18)

View File

@@ -15,7 +15,6 @@
#ifndef RCLCPP_COMPONENTS__NODE_FACTORY_HPP__
#define RCLCPP_COMPONENTS__NODE_FACTORY_HPP__
#include "rclcpp/node_options.hpp"
#include "rclcpp_components/node_instance_wrapper.hpp"
namespace rclcpp_components

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

View File

@@ -39,12 +39,10 @@ ComponentManager::ComponentManager(
{
loadNode_srv_ = create_service<LoadNode>(
"~/_container/load_node",
std::bind(&ComponentManager::on_load_node, this, _1, _2, _3),
rclcpp::ServicesQoS().keep_last(200));
std::bind(&ComponentManager::on_load_node, this, _1, _2, _3));
unloadNode_srv_ = create_service<UnloadNode>(
"~/_container/unload_node",
std::bind(&ComponentManager::on_unload_node, this, _1, _2, _3),
rclcpp::ServicesQoS().keep_last(200));
std::bind(&ComponentManager::on_unload_node, this, _1, _2, _3));
listNodes_srv_ = create_service<ListNodes>(
"~/_container/list_nodes",
std::bind(&ComponentManager::on_list_nodes, this, _1, _2, _3));

View File

@@ -3,39 +3,28 @@ Changelog for package rclcpp_lifecycle
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
21.0.8 (2024-11-08)
22.1.0 (2023-08-21)
-------------------
* add logger level service to lifecycle node. (`#2288 <https://github.com/ros2/rclcpp/issues/2288>`_)
* Contributors: Tomoya Fujita
* Stop using constref signature of benchmark DoNotOptimize. (`#2238 <https://github.com/ros2/rclcpp/issues/2238>`_)
* Contributors: Chris Lalancette
21.0.7 (2024-07-10)
22.0.0 (2023-07-11)
-------------------
* revert call shutdown in LifecycleNode destructor (Iron) (`#2559 <https://github.com/ros2/rclcpp/issues/2559>`_)
* lifecycle node dtor shutdown should be called only in primary state. (`#2543 <https://github.com/ros2/rclcpp/issues/2543>`_)
* rclcpp::shutdown should not be called before LifecycleNode dtor. (`#2539 <https://github.com/ros2/rclcpp/issues/2539>`_)
* Contributors: Tomoya Fujita
* Implement get_node_type_descriptions_interface for lifecyclenode and add smoke test for it (`#2237 <https://github.com/ros2/rclcpp/issues/2237>`_)
* Switch lifecycle to use the RCLCPP macros. (`#2233 <https://github.com/ros2/rclcpp/issues/2233>`_)
* Add new node interface TypeDescriptionsInterface to provide GetTypeDescription service (`#2224 <https://github.com/ros2/rclcpp/issues/2224>`_)
* Contributors: Chris Lalancette, Emerson Knapp
21.0.6 (2024-04-19)
-------------------
* call shutdown in LifecycleNode dtor to avoid leaving the device in un… (`#2490 <https://github.com/ros2/rclcpp/issues/2490>`_)
* Contributors: Tomoya Fujita
21.0.5 (2024-02-07)
21.3.0 (2023-06-12)
-------------------
21.0.4 (2023-11-17)
21.2.0 (2023-06-07)
-------------------
21.0.3 (2023-09-08)
-------------------
* Switch lifecycle to use the RCLCPP macros. (`#2244 <https://github.com/ros2/rclcpp/issues/2244>`_)
* Add new node interface TypeDescriptionsInterface to provide GetTypeDescription service (`#2236 <https://github.com/ros2/rclcpp/issues/2236>`_)
* Contributors: Emerson Knapp
21.0.2 (2023-07-14)
21.1.1 (2023-05-11)
-------------------
21.0.1 (2023-05-11)
21.1.0 (2023-04-27)
-------------------
21.0.0 (2023-04-18)

View File

@@ -1094,6 +1094,7 @@ private:
rclcpp::node_interfaces::NodeClockInterface::SharedPtr node_clock_;
rclcpp::node_interfaces::NodeParametersInterface::SharedPtr node_parameters_;
rclcpp::node_interfaces::NodeTimeSourceInterface::SharedPtr node_time_source_;
rclcpp::node_interfaces::NodeTypeDescriptionsInterface::SharedPtr node_type_descriptions_;
rclcpp::node_interfaces::NodeWaitablesInterface::SharedPtr node_waitables_;
const rclcpp::NodeOptions node_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_lifecycle</name>
<version>21.0.8</version>
<version>22.1.0</version>
<description>Package containing a prototype for lifecycle implementation</description>
<maintainer email="ivanpauno@ekumenlabs.com">Ivan Paunovic</maintainer>

View File

@@ -114,13 +114,15 @@ LifecycleNode::LifecycleNode(
options.clock_qos(),
options.use_clock_thread()
)),
node_waitables_(new rclcpp::node_interfaces::NodeWaitables(node_base_.get())),
node_options_(options),
impl_(new LifecycleNodeInterfaceImpl(
node_type_descriptions_(new rclcpp::node_interfaces::NodeTypeDescriptions(
node_base_,
node_logging_,
node_parameters_,
node_services_))
node_services_
)),
node_waitables_(new rclcpp::node_interfaces::NodeWaitables(node_base_.get())),
node_options_(options),
impl_(new LifecycleNodeInterfaceImpl(node_base_, node_services_, node_logging_))
{
impl_->init(enable_communication_interface);
@@ -142,10 +144,6 @@ 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()
@@ -477,18 +475,18 @@ LifecycleNode::get_node_topics_interface()
return node_topics_;
}
rclcpp::node_interfaces::NodeTypeDescriptionsInterface::SharedPtr
LifecycleNode::get_node_type_descriptions_interface()
{
return node_type_descriptions_;
}
rclcpp::node_interfaces::NodeServicesInterface::SharedPtr
LifecycleNode::get_node_services_interface()
{
return node_services_;
}
rclcpp::node_interfaces::NodeTypeDescriptionsInterface::SharedPtr
LifecycleNode::get_node_type_descriptions_interface()
{
return impl_->get_node_type_descriptions_interface();
}
rclcpp::node_interfaces::NodeParametersInterface::SharedPtr
LifecycleNode::get_node_parameters_interface()
{

View File

@@ -31,7 +31,6 @@
#include "rclcpp/node_interfaces/node_base_interface.hpp"
#include "rclcpp/node_interfaces/node_logging_interface.hpp"
#include "rclcpp/node_interfaces/node_services_interface.hpp"
#include "rclcpp/node_interfaces/node_type_descriptions.hpp"
#include "rclcpp_lifecycle/node_interfaces/lifecycle_node_interface.hpp"
@@ -52,17 +51,11 @@ namespace rclcpp_lifecycle
LifecycleNode::LifecycleNodeInterfaceImpl::LifecycleNodeInterfaceImpl(
std::shared_ptr<rclcpp::node_interfaces::NodeBaseInterface> node_base_interface,
std::shared_ptr<rclcpp::node_interfaces::NodeLoggingInterface> node_logging_interface,
std::shared_ptr<rclcpp::node_interfaces::NodeParametersInterface> node_parameters_interface,
std::shared_ptr<rclcpp::node_interfaces::NodeServicesInterface> node_services_interface)
std::shared_ptr<rclcpp::node_interfaces::NodeServicesInterface> node_services_interface,
std::shared_ptr<rclcpp::node_interfaces::NodeLoggingInterface> node_logging_interface)
: node_base_interface_(node_base_interface),
node_services_interface_(node_services_interface),
node_logging_interface_(node_logging_interface),
node_type_descriptions_(new rclcpp::node_interfaces::NodeTypeDescriptions(
node_base_interface,
node_logging_interface,
node_parameters_interface,
node_services_interface))
node_logging_interface_(node_logging_interface)
{
}
@@ -602,10 +595,4 @@ LifecycleNode::LifecycleNodeInterfaceImpl::on_deactivate() const
}
}
rclcpp::node_interfaces::NodeTypeDescriptionsInterface::SharedPtr
LifecycleNode::LifecycleNodeInterfaceImpl::get_node_type_descriptions_interface()
{
return node_type_descriptions_;
}
} // namespace rclcpp_lifecycle

View File

@@ -53,9 +53,8 @@ class LifecycleNode::LifecycleNodeInterfaceImpl final
public:
LifecycleNodeInterfaceImpl(
std::shared_ptr<rclcpp::node_interfaces::NodeBaseInterface> node_base_interface,
std::shared_ptr<rclcpp::node_interfaces::NodeLoggingInterface> node_logging_interface,
std::shared_ptr<rclcpp::node_interfaces::NodeParametersInterface> node_parameters_interface,
std::shared_ptr<rclcpp::node_interfaces::NodeServicesInterface> node_services_interface);
std::shared_ptr<rclcpp::node_interfaces::NodeServicesInterface> node_services_interface,
std::shared_ptr<rclcpp::node_interfaces::NodeLoggingInterface> node_logging_interface);
~LifecycleNodeInterfaceImpl();
@@ -105,9 +104,6 @@ public:
void
add_timer_handle(std::shared_ptr<rclcpp::TimerBase> timer);
rclcpp::node_interfaces::NodeTypeDescriptionsInterface::SharedPtr
get_node_type_descriptions_interface();
private:
RCLCPP_DISABLE_COPY(LifecycleNodeInterfaceImpl)
@@ -180,9 +176,6 @@ private:
// to controllable things
std::vector<std::weak_ptr<rclcpp_lifecycle::ManagedEntityInterface>> weak_managed_entities_;
std::vector<std::weak_ptr<rclcpp::TimerBase>> weak_timers_;
// Backported members hidden in impl
rclcpp::node_interfaces::NodeTypeDescriptionsInterface::SharedPtr node_type_descriptions_;
};
} // namespace rclcpp_lifecycle

View File

@@ -228,7 +228,8 @@ protected:
BENCHMARK_F(BenchmarkLifecycleClient, get_state)(benchmark::State & state) {
for (auto _ : state) {
(void)_;
const auto lifecycle_state = lifecycle_client->get_state();
lifecycle_msgs::msg::State lifecycle_state = lifecycle_client->get_state();
if (lifecycle_state.id != lifecycle_msgs::msg::State::PRIMARY_STATE_UNCONFIGURED) {
const std::string msg =
std::string("Expected state did not match actual: ") +
@@ -268,7 +269,7 @@ BENCHMARK_F(BenchmarkLifecycleClient, get_available_states)(benchmark::State & s
for (auto _ : state) {
(void)_;
constexpr size_t expected_states = 11u;
const auto states = lifecycle_client->get_available_states();
std::vector<lifecycle_msgs::msg::State> states = lifecycle_client->get_available_states();
if (states.size() != expected_states) {
const std::string msg =
std::string("Expected number of states did not match actual: ") +
@@ -284,7 +285,8 @@ BENCHMARK_F(BenchmarkLifecycleClient, get_available_transitions)(benchmark::Stat
for (auto _ : state) {
(void)_;
constexpr size_t expected_transitions = 2u;
const auto transitions = lifecycle_client->get_available_transitions();
std::vector<lifecycle_msgs::msg::TransitionDescription> transitions =
lifecycle_client->get_available_transitions();
if (transitions.size() != expected_transitions) {
const std::string msg =
std::string("Expected number of transitions did not match actual: ") +
@@ -300,7 +302,8 @@ BENCHMARK_F(BenchmarkLifecycleClient, get_transition_graph)(benchmark::State & s
for (auto _ : state) {
(void)_;
constexpr size_t expected_transitions = 25u;
const auto transitions = lifecycle_client->get_transition_graph();
std::vector<lifecycle_msgs::msg::TransitionDescription> transitions =
lifecycle_client->get_transition_graph();
if (transitions.size() != expected_transitions) {
const std::string msg =
std::string("Expected number of transitions did not match actual: ") +

View File

@@ -97,13 +97,15 @@ protected:
BENCHMARK_F(BenchmarkLifecycleNode, get_current_state)(benchmark::State & state) {
for (auto _ : state) {
(void)_;
const auto & lifecycle_state = node->get_current_state();
const rclcpp_lifecycle::State & lifecycle_state = node->get_current_state();
if (lifecycle_state.id() != lifecycle_msgs::msg::State::PRIMARY_STATE_UNCONFIGURED) {
const std::string message =
std::string("Node's current state is: ") + std::to_string(lifecycle_state.id());
state.SkipWithError(message.c_str());
}
benchmark::DoNotOptimize(lifecycle_state);
// Google benchmark 1.8.2 warns that the constref DoNotOptimize signature may be optimized away
// by the compiler. Cast const away to ensure we don't get that problem (and warning).
benchmark::DoNotOptimize(const_cast<rclcpp_lifecycle::State &>(lifecycle_state));
benchmark::ClobberMemory();
}
}
@@ -112,7 +114,7 @@ BENCHMARK_F(BenchmarkLifecycleNode, get_available_states)(benchmark::State & sta
for (auto _ : state) {
(void)_;
constexpr size_t expected_states = 11u;
const auto lifecycle_states = node->get_available_states();
std::vector<rclcpp_lifecycle::State> lifecycle_states = node->get_available_states();
if (lifecycle_states.size() != expected_states) {
const std::string msg = std::to_string(lifecycle_states.size());
state.SkipWithError(msg.c_str());
@@ -126,7 +128,7 @@ BENCHMARK_F(BenchmarkLifecycleNode, get_available_transitions)(benchmark::State
for (auto _ : state) {
(void)_;
constexpr size_t expected_transitions = 2u;
const auto & transitions = node->get_available_transitions();
std::vector<rclcpp_lifecycle::Transition> transitions = node->get_available_transitions();
if (transitions.size() != expected_transitions) {
const std::string msg = std::to_string(transitions.size());
state.SkipWithError(msg.c_str());
@@ -140,7 +142,7 @@ BENCHMARK_F(BenchmarkLifecycleNode, get_transition_graph)(benchmark::State & sta
for (auto _ : state) {
(void)_;
constexpr size_t expected_transitions = 25u;
const auto & transitions = node->get_transition_graph();
std::vector<rclcpp_lifecycle::Transition> transitions = node->get_transition_graph();
if (transitions.size() != expected_transitions) {
const std::string msg =
std::string("Expected number of transitions did not match actual: ") +
@@ -162,18 +164,20 @@ BENCHMARK_F(BenchmarkLifecycleNode, transition_valid_state)(benchmark::State & s
reset_heap_counters();
for (auto _ : state) {
(void)_;
const auto & active =
const rclcpp_lifecycle::State & active =
node->trigger_transition(lifecycle_msgs::msg::Transition::TRANSITION_ACTIVATE);
if (active.id() != lifecycle_msgs::msg::State::PRIMARY_STATE_ACTIVE) {
state.SkipWithError("Transition to active state failed");
}
const auto & inactive =
const rclcpp_lifecycle::State & inactive =
node->trigger_transition(lifecycle_msgs::msg::Transition::TRANSITION_DEACTIVATE);
if (inactive.id() != lifecycle_msgs::msg::State::PRIMARY_STATE_INACTIVE) {
state.SkipWithError("Transition to inactive state failed");
}
benchmark::DoNotOptimize(active);
benchmark::DoNotOptimize(inactive);
// Google benchmark 1.8.2 warns that the constref DoNotOptimize signature may be optimized away
// by the compiler. Cast const away to ensure we don't get that problem (and warning).
benchmark::DoNotOptimize(const_cast<rclcpp_lifecycle::State &>(active));
benchmark::DoNotOptimize(const_cast<rclcpp_lifecycle::State &>(inactive));
benchmark::ClobberMemory();
}
}

View File

@@ -25,8 +25,6 @@
#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"
@@ -36,8 +34,6 @@
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);
@@ -253,35 +249,6 @@ 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");

View File

@@ -55,6 +55,12 @@ public:
explicit EmptyLifecycleNode(const std::string & node_name, const TimerType & timer_type)
: rclcpp_lifecycle::LifecycleNode(node_name)
{
rclcpp::PublisherOptionsWithAllocator<std::allocator<void>> options;
publisher_ =
std::make_shared<rclcpp_lifecycle::LifecyclePublisher<test_msgs::msg::Empty>>(
get_node_base_interface().get(), std::string("topic"), rclcpp::QoS(10), options);
add_managed_entity(publisher_);
// For coverage this is being added here
switch (timer_type) {
case TimerType::WALL_TIMER:
@@ -71,6 +77,14 @@ public:
}
}
}
std::shared_ptr<rclcpp_lifecycle::LifecyclePublisher<test_msgs::msg::Empty>> publisher()
{
return publisher_;
}
private:
std::shared_ptr<rclcpp_lifecycle::LifecyclePublisher<test_msgs::msg::Empty>> publisher_;
};
class TestLifecyclePublisher : public ::testing::TestWithParam<TimerType>
@@ -79,103 +93,95 @@ public:
void SetUp()
{
rclcpp::init(0, nullptr);
node_ = std::make_shared<EmptyLifecycleNode>("node", GetParam());
}
void TearDown()
{
rclcpp::shutdown();
}
protected:
std::shared_ptr<EmptyLifecycleNode> node_;
};
TEST_P(TestLifecyclePublisher, publish_managed_by_node) {
auto node = std::make_shared<EmptyLifecycleNode>("node", GetParam());
rclcpp::PublisherOptionsWithAllocator<std::allocator<void>> options;
std::shared_ptr<rclcpp_lifecycle::LifecyclePublisher<test_msgs::msg::Empty>> publisher =
node->create_publisher<test_msgs::msg::Empty>(std::string("topic"), rclcpp::QoS(10), options);
// transition via LifecycleNode
auto success = rclcpp_lifecycle::node_interfaces::LifecycleNodeInterface::CallbackReturn::SUCCESS;
auto reset_key = rclcpp_lifecycle::node_interfaces::LifecycleNodeInterface::CallbackReturn::ERROR;
auto ret = reset_key;
EXPECT_EQ(State::PRIMARY_STATE_UNCONFIGURED, node->get_current_state().id());
node->trigger_transition(
EXPECT_EQ(State::PRIMARY_STATE_UNCONFIGURED, node_->get_current_state().id());
node_->trigger_transition(
rclcpp_lifecycle::Transition(Transition::TRANSITION_CONFIGURE), ret);
ASSERT_EQ(success, ret);
ret = reset_key;
node->trigger_transition(
node_->trigger_transition(
rclcpp_lifecycle::Transition(Transition::TRANSITION_ACTIVATE), ret);
ASSERT_EQ(success, ret);
ret = reset_key;
EXPECT_TRUE(publisher->is_activated());
EXPECT_TRUE(node_->publisher()->is_activated());
{
auto msg_ptr = std::make_unique<test_msgs::msg::Empty>();
EXPECT_NO_THROW(publisher->publish(*msg_ptr));
EXPECT_NO_THROW(node_->publisher()->publish(*msg_ptr));
}
{
auto msg_ptr = std::make_unique<test_msgs::msg::Empty>();
EXPECT_NO_THROW(publisher->publish(std::move(msg_ptr)));
EXPECT_NO_THROW(node_->publisher()->publish(std::move(msg_ptr)));
}
{
auto loaned_msg = publisher->borrow_loaned_message();
EXPECT_NO_THROW(publisher->publish(std::move(loaned_msg)));
auto loaned_msg = node_->publisher()->borrow_loaned_message();
EXPECT_NO_THROW(node_->publisher()->publish(std::move(loaned_msg)));
}
node->trigger_transition(
node_->trigger_transition(
rclcpp_lifecycle::Transition(Transition::TRANSITION_DEACTIVATE), ret);
ASSERT_EQ(success, ret);
ret = reset_key;
(void)ret; // Just to make clang happy
EXPECT_FALSE(publisher->is_activated());
EXPECT_FALSE(node_->publisher()->is_activated());
{
auto msg_ptr = std::make_unique<test_msgs::msg::Empty>();
EXPECT_NO_THROW(publisher->publish(*msg_ptr));
EXPECT_NO_THROW(node_->publisher()->publish(*msg_ptr));
}
{
auto msg_ptr = std::make_unique<test_msgs::msg::Empty>();
EXPECT_NO_THROW(publisher->publish(std::move(msg_ptr)));
EXPECT_NO_THROW(node_->publisher()->publish(std::move(msg_ptr)));
}
{
auto loaned_msg = publisher->borrow_loaned_message();
EXPECT_NO_THROW(publisher->publish(std::move(loaned_msg)));
auto loaned_msg = node_->publisher()->borrow_loaned_message();
EXPECT_NO_THROW(node_->publisher()->publish(std::move(loaned_msg)));
}
}
TEST_P(TestLifecyclePublisher, publish) {
auto node = std::make_shared<EmptyLifecycleNode>("node", GetParam());
rclcpp::PublisherOptionsWithAllocator<std::allocator<void>> options;
std::shared_ptr<rclcpp_lifecycle::LifecyclePublisher<test_msgs::msg::Empty>> publisher =
node->create_publisher<test_msgs::msg::Empty>(std::string("topic"), rclcpp::QoS(10), options);
// transition via LifecyclePublisher
publisher->on_deactivate();
EXPECT_FALSE(publisher->is_activated());
node_->publisher()->on_deactivate();
EXPECT_FALSE(node_->publisher()->is_activated());
{
auto msg_ptr = std::make_unique<test_msgs::msg::Empty>();
EXPECT_NO_THROW(publisher->publish(*msg_ptr));
EXPECT_NO_THROW(node_->publisher()->publish(*msg_ptr));
}
{
auto msg_ptr = std::make_unique<test_msgs::msg::Empty>();
EXPECT_NO_THROW(publisher->publish(std::move(msg_ptr)));
EXPECT_NO_THROW(node_->publisher()->publish(std::move(msg_ptr)));
}
{
auto loaned_msg = publisher->borrow_loaned_message();
EXPECT_NO_THROW(publisher->publish(std::move(loaned_msg)));
auto loaned_msg = node_->publisher()->borrow_loaned_message();
EXPECT_NO_THROW(node_->publisher()->publish(std::move(loaned_msg)));
}
publisher->on_activate();
EXPECT_TRUE(publisher->is_activated());
node_->publisher()->on_activate();
EXPECT_TRUE(node_->publisher()->is_activated());
{
auto msg_ptr = std::make_unique<test_msgs::msg::Empty>();
EXPECT_NO_THROW(publisher->publish(*msg_ptr));
EXPECT_NO_THROW(node_->publisher()->publish(*msg_ptr));
}
{
auto msg_ptr = std::make_unique<test_msgs::msg::Empty>();
EXPECT_NO_THROW(publisher->publish(std::move(msg_ptr)));
EXPECT_NO_THROW(node_->publisher()->publish(std::move(msg_ptr)));
}
{
auto loaned_msg = publisher->borrow_loaned_message();
EXPECT_NO_THROW(publisher->publish(std::move(loaned_msg)));
auto loaned_msg = node_->publisher()->borrow_loaned_message();
EXPECT_NO_THROW(node_->publisher()->publish(std::move(loaned_msg)));
}
}